diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..9271a9f27 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,101 @@ + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion + +[*.cs] +csharp_indent_labels = one_less_than_current +csharp_space_around_binary_operators = before_and_after +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +file_header_template = Copyright © 2024-Present The Synapse Authors\n\nLicensed under the Apache License, Version 2.0 (the "License"),\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an "AS IS" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License. \ No newline at end of file diff --git a/.github/workflows/build-dotnet.yml b/.github/workflows/build-dotnet.yml index 58e0de7ad..b24e0a3e1 100644 --- a/.github/workflows/build-dotnet.yml +++ b/.github/workflows/build-dotnet.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - dotnet-version: ['6.0.x' ] + dotnet-version: ['8.0.x' ] steps: - name: Checkout diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8018263e7..114288343 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -354,4 +354,4 @@ jobs: with: files: "synctl*" env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 64de12b6c..13035ac99 100644 --- a/.gitignore +++ b/.gitignore @@ -231,3 +231,5 @@ _Pvt_Extensions # FAKE - F# Make .fake/ /deployment/docker-compose/secrets/basic.json +/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/node_modules +/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/package-lock.json diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..97e590eb0 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,5 @@ +# Code of Conduct + +This community adheres to the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). + +To report any violations of the Code of Conduct, please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io). \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06e8b4d9f..086305bc3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,91 +1,29 @@ -# Contributing to Synapse +# Contributing -This page contains information about reporting issues, how to suggest changes as -well as the guidelines we follow for how our documents are formatted. +Thank you for considering contributing to the Synapse project! Your contributions help improve the project and are greatly appreciated. Please follow the guidelines below to ensure a smooth and effective contribution process. -## Table of Contents +## How to Contribute -- [Reporting an Issue](#reporting-an-issue) -- [Suggesting a Change](#suggesting-a-change) -- [Spec Formatting Conventions](#spec-formatting-conventions) +### Reporting Issues -## Reporting an Issue +If you encounter any bugs or have suggestions for improvements, please report them using GitHub Issues: -To report an issue, or to suggest an idea for a change that you haven't had time -to write-up yet, open an [issue](https://github.com/serverlessworkflow/synapse/issues). It -is best to check our existing -[issues](https://github.com/serverlessworkflow/synapse/issues) first to see if a similar -one has already been opened and discussed. +1. Go to the [Issues page](https://github.com/serverlessworkflow/synapse/issues) of the repository. +2. Check if the issue has already been reported. If not, click on "New issue". +3. Provide a clear and concise description of the problem or suggestion. Include any relevant details or screenshots. -## Suggesting a Change +### Suggesting Enhancements -To suggest a change to this repository, submit a -[pull request](https://github.com/serverlessworkflow/synapse/pulls) (PR) with the complete -set of changes you'd like to see. See the -[Spec Formatting Conventions](#spec-formatting-conventions) section for the -guidelines we follow for how documents are formatted. +If you have ideas for new features or enhancements: -Each PR must be signed per the following section. +1. Open a new issue on the [Issues page](https://github.com/serverlessworkflow/synapse/issues). +2. Clearly describe the enhancement or feature request and explain why it would be valuable. -### Assigning and Owning work +### Submitting Pull Requests -If you want to own and work on an issue, add a comment or “#dibs” it asking -about ownership. A maintainer will then add the Assigned label and modify the -first comment in the issue to include `Assigned to: @person` +To contribute code changes, please follow these steps: -## Spec Formatting Conventions - -Documents in this repository will adhere to the following rules: - -- Lines are wrapped at 80 columns (when possible) -- Specifications will use [RFC2119](https://tools.ietf.org/html/rfc2119) - keywords to indicate normative requirements - -## Checks - -### Markdown style - -Markdown files should be properly formatted before a pull request is sent out. -In this repository we follow the -[markdownlint rules](https://github.com/DavidAnson/markdownlint#rules--aliases) -with some customizations. See [markdownlint](.markdownlint.yaml) or -[settings](.vscode/settings.json) for details. - -We highly encourage to use line breaks in markdown files at `80` characters -wide. There are tools that can do it for you effectively. Please submit proposal -to include your editor settings required to enable this behavior so the out of -the box settings for this repository will be consistent. - -If you are using Visual Studio Code, -you can also use the `fixAll` command of the -[vscode markdownlint extension](https://github.com/DavidAnson/vscode-markdownlint). - -To otherwise check for style violations, use - -```bash -# Ruby and gem are required for mdl -gem install mdl -mdl -c .mdlrc . -``` - -To fix style violations, follow the -[instruction](https://github.com/DavidAnson/markdownlint#optionsresultversion) -with the Node version of markdownlint. - -### Typos - -In addition, please make sure to clean up typos before you submit the change. - -To check for typos, you may use - -```bash -# Golang is needed for the misspell tool. -make install-misspell -make misspell -``` - -To quickly fix typos, use - -```bash -make misspell-correction -``` +1. **Fork the repository**: Create a personal fork of the repository on GitHub. +2. **Clone your fork**: Clone the forked repository to your local machine. + ```bash + git clone https://github.com//synapse.git diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 525ef5a90..fa81158d0 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,138 +1,94 @@ -# Synapse Project Governance +# Governance -As a CNCF member project, we abide by the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). +As a CNCF member project, Synapse adheres to the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). -For specific guidance on practical contribution steps for any Synapse sub-project please -see our [contributing guide](CONTRIBUTING.md). +For specific guidance on contributing to Synapse, please refer to our [contributing guide](contributing.md). -You can contact the project maintainers at any time by sending an email to the -[Synapse WMS Maintainers](mailto:cncf-serverlessws-maintainers@lists.cncf.io) - mailing list. +For any questions or concerns, contact the project maintainers via the [Serverless Workflow Specification Maintainers](mailto:cncf-serverlessws-maintainers@lists.cncf.io) mailing list. ## Maintainership -Main responsibilities of maintainers include: +### Responsibilities -1) They share responsibility in the project's success. -2) They have made a long-term, recurring time investment to improve the project. -3) They spend that time doing whatever needs to be done, not necessarily what -is the most interesting or fun. +Maintainers are responsible for: + +1. Ensuring the project's overall success. +2. Committing significant time and effort to enhance the project. +3. Handling necessary tasks, even those that may not be the most engaging. ## Reviewers -A reviewer is a core role within the project. -They share in reviewing issues and pull requests. Their pull request approvals -are needed to merge a large code change into the project. - -## Adding maintainers - -Maintainers are first and foremost contributors that have shown they are -committed to the long term success of a project. Contributors wanting to become -maintainers are expected to be deeply involved in contributing code, pull -request review, and triage of issues in the project for more than three months. - -Just contributing does not make you a maintainer, it is about building trust -with the current maintainers of the project and being a person that they can -depend on and trust to make decisions in the best interest of the project. - -Periodically, the existing maintainers curate a list of contributors that have -shown regular activity on the project over the prior months. From this list, -maintainer candidates are selected and proposed on the project mailing list. - -After a candidate has been announced on the project mailing list, the -existing maintainers are given fourteen business days to discuss the candidate, -raise objections and cast their vote. Votes may take place on the mailing list -or via pull request comment. Candidates must be approved by at least 66% of the -current maintainers by adding their vote on the mailing list. The reviewer role -has the same process but only requires 33% of current maintainers. Only -maintainers of the repository that the candidate is proposed for are allowed to -vote. - -If a candidate is approved, a maintainer will contact the candidate to invite -the candidate to open a pull request that adds the contributor to the -[MAINTAINERS](MAINTAINERS.md) file. The voting process may take place inside a pull request if a -maintainer has already discussed the candidacy with the candidate and a -maintainer is willing to be a sponsor by opening the pull request. The candidate -becomes a maintainer once the pull request is merged. +Reviewers play a crucial role by: -## Subprojects +- Reviewing and approving issues and pull requests. +- Approving pull requests is necessary for code changes to be merged into the project. + +## Emeritus Maintainers -Synapse subprojects all culminate in officially supported and maintained releases -of the specification. -All subprojects must adhere to [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md) -as well as this governance document. +Emeritus maintainers are former maintainers who have significantly contributed to the project but are no longer able to be actively involved. They continue to: -### Adding core subprojects +1. Provide guidance and mentorship to current maintainers and contributors. +2. Offer historical context and insights based on their past experiences. +3. Participate in discussions and reviews on an advisory basis. -New subprojects can request to be added to the Synapse GitHub -organization by submitting a GitHub issue in the specification repository. +## Adding Maintainers -The existing maintainers are given fourteen business days to discuss the new -project, raise objections and cast their vote. Projects must be approved by at -least 66% of the current maintainers. +To add a new maintainer: -If a project is approved, a maintainer will add the project to the Synapse -GitHub organization, and make an announcement on a public Slack channel. +1. Candidates must demonstrate strong, ongoing commitment to the project by actively contributing, reviewing pull requests, and managing issues for at least three months. +2. Current maintainers review and propose new maintainers through a pull request. +3. A candidate requires at least 66% approval from existing maintainers to be added. Only one maintainer per organization is allowed. -## Stepping down policy +For the reviewer role, candidates need 33% approval from current maintainers. -Life priorities, interests, and passions can change. If you're a maintainer but -feel you must remove yourself from the list, inform other maintainers that you -intend to step down, and if possible, help find someone to pick up your work. -At the very least, ensure your work can be continued where you left off. +## Adding Emeritus Maintainers -After you've informed other maintainers, create a pull request to remove -yourself from the MAINTAINERS file. +To transition a maintainer to emeritus status: -## Removal of inactive maintainers +1. Follow the same voting and approval process as for adding new maintainers. +2. A pull request is created for the transition, requiring a 66% approval vote from current maintainers. +3. Once approved, the emeritus maintainer is added to the EMERITUS file and announced to the community. + +## Subprojects -Similar to the procedure for adding new maintainers, existing maintainers can -be removed from the list if they do not show significant activity on the -project. Periodically, the maintainers review the list of maintainers and their -activity over the last three months. +### Adding Core Subprojects -If a maintainer has shown insufficient activity over this period, a neutral -person will contact the maintainer to ask if they want to continue being -a maintainer. If the maintainer decides to step down as a maintainer, they -open a pull request to be removed from the MAINTAINERS file. +1. To add a new subproject, submit a GitHub issue in the specification repository. +2. Existing maintainers have fourteen business days to discuss and vote on the proposal. +3. A subproject requires at least 66% approval from current maintainers. -If the maintainer wants to remain a maintainer, but is unable to perform the -required duties they can be removed with a vote of at least 66% of -the current maintainers. An e-mail is sent to the -mailing list, inviting maintainers of the project to vote. The voting period is -fourteen business days. Issues related to a maintainers performance should be -discussed with them among the other maintainers so that they are not surprised -by a pull request removing them. +### Stepping Down -## How are decisions made? +Maintainers intending to step down should: -Synapse is an open-source project with an open design philosophy. This means -that the repository is the source of truth for EVERY aspect of the project, -including its philosophy, design, road map, and APIs. *If it's part of the -project, it's in the repository. If it's in the repository, it's part of the project.* +1. Inform other maintainers and, if possible, help find a successor. +2. Open a pull request to remove their name from the MAINTAINERS file. -As a result, all decisions can be expressed as changes to the repository. An -implementation change is a change to the source code. An API change is a change -to the API specification, and so on. +## Removal of Inactive Maintainers -All decisions affecting Synapse, big and small, follow the same 3 steps: +Inactive maintainers are reviewed periodically. If a maintainer has not been active for three months: -* Step 1: Open a pull request. Anyone can do this. +1. A neutral person will contact them to confirm their desire to continue. +2. If they wish to step down, a pull request is opened to remove them from the MAINTAINERS file. +3. If they wish to remain but cannot perform their duties, they can be removed with a 66% vote from the current maintainers. -* Step 2: Discuss the pull request. Anyone can do this. +## Decision-Making Process -* Step 3: Merge or refuse the pull request. Who does this depends on the nature -of the pull request and which areas of the project it affects. +Decisions are made through: -## I'm a maintainer. Should I make pull requests too? +1. Opening a pull request, which anyone can do. +2. Discussing the pull request, which anyone can contribute to. +3. Merging or refusing the pull request, which depends on the nature of the change. -Yes. Nobody should ever push to master directly. All changes should be -made through a pull request. +Consensus among maintainers is required for significant decisions. Proposals should be discussed before opening a pull request. + +## Maintainer Pull Requests + +Maintainers must also make changes via pull requests. Direct pushes to master are not allowed. For details, see the [Contributing](contributing.md) guide. ## Conflict Resolution -If you have a technical dispute that you feel has reached an impasse with a -subset of the community, any contributor may open an issue, specifically -calling for a resolution vote of the current core maintainers to resolve the dispute. -The same voting quorums required (2/3) for adding and removing maintainers -will apply to conflict resolution. \ No newline at end of file +To merge changes, at least one maintainer must approve the pull request. If maintainers do not voice their opinions within two days, their approval is assumed via [lazy consensus](http://communitymgt.wikia.com/wiki/Lazy_consensus). + +For disputes, efforts should be made to resolve issues amicably. If unresolved, a third-party maintainer can mediate. The core maintainers have the final say, making decisions by consensus or majority vote if necessary, ideally within two weeks. + diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 19f3d4801..8a0207797 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,4 +1,4 @@ -# Synapse Maintainers +# Maintainers * [Charles d'Avernas](https://github.com/cdavernas) * [Jean-Baptiste Bianchi](https://github.com/jbbianchi) \ No newline at end of file diff --git a/MAITAINER-GUIDELINES.md b/MAITAINER-GUIDELINES.md deleted file mode 100644 index 742e371d3..000000000 --- a/MAITAINER-GUIDELINES.md +++ /dev/null @@ -1,29 +0,0 @@ -# Maintainer's Guide - -## Tips - -Here are a few tips for repository maintainers. - -* Stay on top of your pull requests. PRs that languish for too long can become difficult to merge. -* Work from your own fork. As you are making contributions to the project, you should be working from your own fork just as outside contributors do. This keeps the branches in github to a minimum and reduces unnecessary CI runs. -* Try to proactively label issues with backport labels if it's obvious that a change should be backported to previous releases. -* When landing pull requests, if there is more than one commit, try to squash into a single commit. Usually this can just be done with the GitHub UI when merging the PR. Use "Squash and merge". -* Triage issues once in a while in order to keep the repository alive. During the triage: - * If some issues are stale for too long because they are no longer valid/relevant or because the discussion reached no significant action items to perform, close them and invite the users to reopen if they need it. - * If some PRs are no longer valid but still needed, ask the user to rebase them - * If some issues and PRs are still relevant, use labels to help organize tasks - * If you find an issue that you want to create a fix for and submit a pull request, be sure to assign it to yourself so that others maintainers don't start working on it at the same time. - -## Branch Management - -The `main` branch is the bleeding edge. New major versions of the project -are cut from this branch and tagged. If you intend to submit a pull request -you should use `main HEAD` as your starting point. - -Each major release will result in a new branch and tag. For example, the -release of version 1.0.0 of the project results in a `v1.0.0` tag on the -release commit, and a new branch `release-1.y.z` for subsequent minor and patch -level releases of that major version if necessary. However, development will continue -apace on `main` for the next major version - e.g. 2.0.0. Version branches -are only created for each major version. Minor and patch level releases -are simply tagged. \ No newline at end of file diff --git a/OWNERS.md b/OWNERS.md deleted file mode 100644 index 2394ccae2..000000000 --- a/OWNERS.md +++ /dev/null @@ -1,18 +0,0 @@ -See the [governance document](GOVERNANCE.md) for the definition of the roles and responsibilities - -## Maintainers: - -- [Charles d'Avernas](https://github.com/cdavernas) -- [Jean-Baptiste Bianchi](https://github.com/jbbianchi) - -## Reviewers: - -- [Charles d'Avernas](https://github.com/cdavernas) -- [Jean-Baptiste Bianchi](https://github.com/jbbianchi) -- [Tihomir Surdilovic](https://github.com/tsurdilo) - -## Approvers: - -- [Charles d'Avernas](https://github.com/cdavernas) -- [Jean-Baptiste Bianchi](https://github.com/jbbianchi) -- [Tihomir Surdilovic](https://github.com/tsurdilo) diff --git a/README.md b/README.md index 383fc858d..729524a61 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Synapse Logo + Synapse Logo

--- @@ -13,221 +13,130 @@ ## About -Synapse is a vendor-neutral, free, open-source, and community-driven Workflow Management System (WFMS) implementing the [Serverless Workflow specification](https://github.com/serverlessworkflow/specification). +Synapse is a vendor-neutral, open-source, and community-driven Workflow Management System (WFMS) designed to implement the [Serverless Workflow specification](https://github.com/serverlessworkflow/specification). -## Requirements +It enables developers and organizations to define and execute workflows effortlessly using a high-level, intuitive Domain Specific Language (DSL). -- [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) or later +With Synapse, you can create powerful workflows that are cloud vendor-agnostic, easily scalable, and highly customizable. -*Optionally, and depending on which flavor of the Synapse Server you chose to run, you might require:* +### Features -- [Docker](https://www.docker.com/) -- [Kubernetes](https://kubernetes.io/) +- **Easy to Use**: The Serverless Workflow DSL is designed for universal understanding, enabling users to quickly grasp workflow concepts and create complex workflows effortlessly. +- **Event Driven**: Seamlessly integrate events into workflows with support for various formats, including CloudEvents, allowing for event-driven workflow architectures. +- **Service Oriented**: Integrate seamlessly with service-oriented architectures, allowing workflows to interact with various services over standard application protocols like HTTP, gRPC, OpenAPI, AsyncAPI, and more. +- **FaaS Centric**: Invoke functions hosted on various platforms within workflows, promoting a function-as-a-service (FaaS) paradigm and enabling microservices architectures. +- **Timely**: Define timeouts for workflows and tasks to manage execution duration effectively. +- **Fault Tolerant**: Easily define error handling strategies to manage and recover from errors that may occur during workflow execution, ensuring robustness and reliability. +- **Schedulable**: Schedule workflows using CRON expressions or trigger them based on events, providing control over workflow execution timing. +- **Interoperable**: Integrates seamlessly with different services and resources. +- **Robust**: Offers features such as conditional branching, event handling, and looping constructs. +- **Scalable**: Promotes code reusability, maintainability, and scalability across different environments. +- **Cross-Platform**: Runs on various operating systems, providing flexibility and ease of integration. -## Quick start +### Microservices -**1. Download and extract synctl:** +Synapse is composed of several specialized applications, allowing for atomic scalability, resilience, and ease of maintenance: -*On Windows*: +- **API Server**: Serves an HTTP API to manage Synapse resources, and optionally serves the **Dashboard**, which is Synapse's Graphical User Interface. +- **Operator**: Controls workflows and workflow instances, and starts workflow runners. +- **Runner**: Executes a single instance of a workflow. +- **Correlator**: Performs Complex Event Processing (CEP) and correlates ingested events. +- **CLI**: Allows interaction with the Synapse API via the command line interface. -``` -wget https://github.com/serverlessworkflow/synapse/releases/latest/download/synctl-win-x64.zip -tar -xf synctl-win-x64.zip -``` +## Getting Started -*On Linux*: +### Prerequisites -``` -wget https://github.com/serverlessworkflow/synapse/releases/latest/download/synctl-linux-x64.tar.gz -tar -xf synctl-linux-x64.tar.gz -``` - -*On Mac OSX*: - -``` -wget https://github.com/serverlessworkflow/synapse/releases/latest/download/synctl-osx-x64.tar.gz -tar -xf synctl-osx-x64.tar.gz -``` +[Docker]() or [Kubernetes](), depending on the container platform you wish to use. -**2. Install Synapse:** +### Installation -*Natively*: +The simplest way to get started is by using the provided Docker Compose setup. -``` -synctl system install native -``` +1. Clone the Synapse repository: -*On Docker*: + ```bash + git clone https://github.com/serverlessworkflow/synapse.git + ``` -``` -synctl system install docker -``` +2. Navigate to the Docker Compose directory: -*On Kubernetes*: + ```bash + cd synapse/deployments/docker-compose + ``` -``` -synctl system install kubernetes -``` +3. Build the Docker images: -**3. Have fun!** - -## Server - -### Installing - -#### Native - -The Synapse Server can run natively on Windows, Mac and Linux, without any dependencies aside from .NET. Even though it is the easier way to get started, it should only be used for tests purposes. For production, you should prefer the Docker or Kubernetes based setups. - -To get started, just download the appropriate [release](https://github.com/serverlessworkflow/synapse/releases/latest) for your system, then start it using the following command: - -```shell -dotnet run ./Synapse.Server.dll -``` - -For more information on how to configure a Native Synapse Server, please read the [docs](https://github.com/serverlessworkflow/synapse/wiki). - -#### Docker - -Docker is the recommended way to run the Synapse Server for those who do not want to host it on a Kubernetes cluster. - -To run the server on Docker, simply execute the following command in your system's shell: - -```shell -docker run --name synapse -v /var/run/docker.sock:/var/run/docker.sock -p 42286:42286 -p 41387:41387 ghcr.io/serverlessworkflow/synapse:latest -``` + ```bash + docker-compose build + ``` -*Notes: you need to mount the `docker.sock` and/or run the container with the `--network host` option for Synapse to be able to spawn its own containers* +4. Start the services using Docker Compose: -For more information on how to configure Synapse for Docker, please read the [docs](). + ```bash + docker-compose up + ``` -#### Docker-Compose +This will pull the necessary Docker images and start the Synapse services as defined in the `docker-compose.yml` file. You can then access the Synapse API and dashboard as configured. -Docker-Compose helps you to get started easier and faster than with Docker, as it allows to declare and configure multiple container at once, which will likely be needed if using persistence, for instance. +### Run using `synctl` Command-line Interface -To run the server on Docker-Compose, simply execute the following command in your system's shell: +First, set up the Synapse API server to use with `synctl`: -```shell -docker-compose -f deployment/docker-compose/docker-compose.yml up -d +```bash +synctl config set-api default -server=http://localhost:8080 ``` -Alternatively, you can use the file using EventStore and MongoDB powered persistence: +Then, create a new file with the definition of the workflow to create: -```shell -docker-compose -f deployment/docker-compose/eventstore+mongo.yml up -d +```yaml +# greeter.yaml +document: + dsl: '1.0.0' + name: greeter + namespace: default + version: '0.1.0' +do: + greet: + set: + greetings: '${ "Hello \(.user.firstName) \(.user.lastName)!" }' ``` -#### Kubernetes +Next, run the following command to create the workflow on the API: -Kubernetes is the preferred way to run the Synapse Server, as it offers a wide range of tools to configure, run and manage multiple containers at once, which will likely be needed if using persistence, for instance. - -To run the server on Kubernetes, simply execute the following command in your system's shell: - -```shell -kubectl apply -f deployment/kubernetes/stand-alone.yaml +```bash +synctl workflow create -f greeter.yaml ``` -Alternatively, you can use the file using EventStore and MongoDB powered persistence: +Finally, run the following command to run the workflow with the specified JSON input: -```shell -kubectl apply -f deployment/kubernetes/eventstore+mongo.yaml +```bash +synctl workflow run greeter --namespace default --version 0.1.0 --input '{\"user\":{\"firstName\":\"John\",\"lastName\":\"Doe\"}}' ``` -## User Interfaces - -Synapse provides 2 different UIs for interacting with the server: - -### GUI -

- Dashboard demo -

-The `Dashboard` is a Blazor Web Assembly (WASM) Graphical User Interface (GUI) that comes bundled with the Synapse Server. - -To get started, simply open a web browser and navigate to the Synapse Server's base url. +The command above will provide the fully qualified name of the created workflow instance. You can utilize this name to inspect its output once the execution is finished, as demonstrated below: -For more information on how to use the `Dashboard`, please read the [docs](https://github.com/serverlessworkflow/synapse/wiki). - -### CLI - -`synctl` is a Command Line Interface (CLI) used to interact with the Synapse Server. - -To get started, just download the appropriate [release](https://github.com/serverlessworkflow/synapse/releases/latest) for your system, then type the following command: - -```shell -synctl --help +```bash +synctl workflow-instance get-output greeter-uk58h3dssqp620a --namespace default --output yaml ``` -For more information on how to use `synctl`, please read the [docs](https://github.com/serverlessworkflow/synapse/wiki). - -## Application Programing Interfaces - -The Synapse Server is shipped with 3 different APIs, each addressing a different use-case. All the implementations of those APIs are supplied with their respective client library. - -### Management API - -The Synapse Management API is used to manage workflows and their instances. - -Implementations: - -- [x] HTTP REST -- [x] [GRPC](https://github.com/grpc/grpc-dotnet) -- [ ] WebSockets ([SignalR](https://github.com/dotnet/aspnetcore/tree/main/src/SignalR)) - -### Monitoring API - -The Synapse Monitoring API is used for real-time observability of workflows and their instances. It is used by the Dashboard to enable real-time updates. - -Implementations: - -- [ ] [GRPC](https://github.com/grpc/grpc-dotnet) -- [x] WebSockets ([SignalR](https://github.com/dotnet/aspnetcore/tree/main/src/SignalR)) - -### Runtime API - -The Synapse Runtime API is used by workers to run workflows and maintain their state. It preferably should not be used by anything else than runtime executors. - -Implementations: - -- [x] [GRPC](https://github.com/grpc/grpc-dotnet) -- [ ] WebSockets ([SignalR](https://github.com/dotnet/aspnetcore/tree/main/src/SignalR)) +For more information about `synctl`, please refer to the [documentation](#synctl). ## Community -We have a growing community working together to build a community-driven and vendor-neutral -workflow ecosystem. Community contributions are welcome and much needed to foster project growth. +The Synapse project has a vibrant and growing community dedicated to building a community-driven and vendor-neutral workflow runtime ecosystem. Contributions from the community are encouraged and essential to the continued growth and success of the project. -See [here](community/contributors.md) for the list of community members that have contributed to the specification. +A list of community members who have contributed to Synapse can be found [here](./community/README.md). -To learn how to contribute to the specification reference the ['how to contribute'](CONTRIBUTING.md) doc. +To learn how to contribute to Synapse, please refer to the [contribution guidelines](CONTRIBUTING.md). -If you have any copyright questions when contributing to a CNCF project like this one, -reference the [Ownership of Copyrights in CNCF Project Contributions](https://github.com/cncf/foundation/blob/master/copyright-notices.md) doc. +For any copyright-related questions when contributing to a CNCF project like Synapse, please refer to the [Ownership of Copyrights in CNCF Project Contributions](https://github.com/cncf/foundation/blob/master/copyright-notices.md) document. ### Code of Conduct -As contributors and maintainers of this project, and in the interest of fostering -an open and welcoming community, we pledge to respect all people who contribute -through reporting issues, posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. - -We are committed to making participation in this project a harassment-free experience for -everyone, regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, body size, race, ethnicity, age, -religion, or nationality. - -See our full project Code of Conduct information [here](CODE-OF-CONDUCT.md). +As contributors and maintainers of Synapse, and in the interest of fostering an open and welcoming community, we commit to respecting all individuals who contribute through activities such as reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other forms of participation. -## Repository Structure +The project is committed to making participation in Synapse a harassment-free experience for everyone, regardless of experience level, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. -Here is the outline of the repository to help navigate the specification -documents: +For more detailed information, please see the full project Code of Conduct [here](code-of-conduct.md). -| File/Directory | Description | -| --- | --- | -| [LICENSE](LICENSE) | Specification License doc | -| [OWNERS](OWNERS.md) | Defines the current maintainers and approvers | -| [MAINTAINERS](MAINTAINERS.md) | Project Maintainers Info | -| [GOVERNANCE](GOVERNANCE.md) | Project Governance Info | -| [CONTRIBUTING](CONTRIBUTING.md) | Documentation on how to contribute to the project | -| [CODE-OF-CONDUCT](code-of-conduct.md) | Defines the project's Code of Conduct | -| [ROADMAP](https://github.com/serverlessworkflow/synapse/milestones) | Project Roadmap | diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..5cd45ae7e --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ +# Security Policy + +## Reporting a Vulnerability + +The Synapse team and community take security vulnerabilities very seriously. Responsible disclosure of security issues is greatly appreciated, and every effort will be made to acknowledge and address your findings. + +To report a security issue: + +- **Use the GitHub Security Advisory**: Please use the ["Report a Vulnerability"](https://github.com/serverlessworkflow/synapse/security/advisories/new) tab on GitHub to submit your report. + +The Synapse team will acknowledge your report and provide details on the next steps. After the initial response, the security team will keep you informed of the progress towards a fix and any subsequent announcements. Additional information or guidance may be requested as necessary. + +## Security Best Practices + +To ensure the security and stability of Synapse as the runtime environment for the Serverless Workflow DSL, consider the following best practices: + +- **Runtime Environment Hardening**: Secure the underlying infrastructure where Synapse is deployed. This includes using up-to-date operating systems, applying security patches regularly, and configuring firewalls and security groups to limit access to only necessary ports and services. + +- **Secure Configuration Management**: Ensure that Synapse configuration files, especially those containing sensitive information like database credentials or API keys, are stored securely. Use environment variables or secret management tools to avoid hardcoding sensitive data. + +- **Container Security**: If running Synapse within containers, use hardened and minimal base images. Regularly scan container images for vulnerabilities, and avoid running containers with elevated privileges. Leverage container orchestration tools to enforce security policies, such as network segmentation and resource limits. + +- **Workflow Isolation**: Assign workflows to specific operators based on their namespace or organizational unit. This ensures that each sector or department has dedicated resources for running workflows in isolated environments, reducing the risk of unauthorized access and maintaining operational integrity. + +- **Logging and Monitoring**: Implement robust logging and monitoring for Synapse runtime operations. Ensure that logs capture relevant security events, such as unauthorized access attempts or unusual activity. Use monitoring tools to detect and alert on potential security incidents in real-time. + +- **Secure Communication Channels**: Ensure that all communication between Synapse and external services (e.g., APIs, databases) is encrypted using TLS. Validate certificates and use mutual TLS where possible to further secure communications. + +- **Access Control and Authentication**: Implement strict access control mechanisms within Synapse. Use Role-Based Access Control (RBAC) to define and enforce permissions for different users and services interacting with the runtime. Integrate with identity providers (e.g., OAuth, LDAP) for centralized authentication and authorization. + +- **Regular Security Audits**: Conduct regular security audits of the Synapse runtime environment, including code reviews, penetration testing, and vulnerability assessments. Address any findings promptly to maintain a secure operational environment. + +- **Incident Response Planning**: Develop and maintain an incident response plan specifically for Synapse. This should include procedures for identifying, containing, and remediating security incidents. Ensure that the plan is tested regularly and that the team is prepared to act swiftly in the event of a security breach. + +- **Backup and Recovery**: Implement a robust backup and recovery strategy for Synapse. Ensure that backups are encrypted, stored securely, and tested regularly to ensure data can be restored in case of a security incident or data corruption. + +By adhering to these best practices, the security of the Synapse runtime can be significantly enhanced, reducing the risk of vulnerabilities and ensuring the integrity and reliability of workflows executed within it. + +--- + +Thank you for contributing to the security and integrity of Synapse! \ No newline at end of file diff --git a/Synapse.sln b/Synapse.sln index 4196950c7..b1a985739 100644 --- a/Synapse.sln +++ b/Synapse.sln @@ -1,389 +1,262 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.0.32112.339 +VisualStudioVersion = 17.9.34728.123 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D789B1B2-073B-4609-948F-25C70148CDFB}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{ED2CBFD5-2895-44F7-9F56-CEA41A7AF4F2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{9E296C8A-4D78-4592-B046-11A3A953FD25}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Docker", "src\runtime\Synapse.Runtime.Docker\Synapse.Runtime.Docker.csproj", "{032F057F-AF44-4E6F-9112-FC3436D24D96}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dashboard", "dashboard", "{7DF998B8-0FB1-470E-8ED0-EA1CC7B16901}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "runtime", "runtime", "{FA3BA61B-BBD3-4EF3-8087-09C3D7411540}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cli", "cli", "{D3B3B95D-B598-4B13-B754-4A7E530405A6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Domain", "src\core\Synapse.Domain\Synapse.Domain.csproj", "{B436986B-05AF-4F29-840E-54B88020C434}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "operator", "operator", "{32EAD165-3D99-42CD-B3AF-05136DCC7F35}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Infrastructure", "src\core\Synapse.Infrastructure\Synapse.Infrastructure.csproj", "{6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "runner", "runner", "{1DA47E5F-B23A-4D3C-96AA-4BD2662AB946}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Application", "src\core\Synapse.Application\Synapse.Application.csproj", "{55AC4DEF-842B-42D9-8AB6-A601E3240584}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Core", "src\core\Synapse.Core\Synapse.Core.csproj", "{F2804D69-04C9-463D-B5E5-D3185163EBC0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Integration", "src\core\Synapse.Integration\Synapse.Integration.csproj", "{2672E2E5-32D1-460E-A37B-3E268D70A4E5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Core.Infrastructure.Containers.Docker", "src\core\Synapse.Core.Infrastructure.Containers.Docker\Synapse.Core.Infrastructure.Containers.Docker.csproj", "{40EB503E-6E97-4375-A172-6EF55F9E0FF3}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dashboard", "dashboard", "{19826311-054C-4E93-8B79-080145851939}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "api", "api", "{63715FC0-736D-4972-A865-41126155DF45}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Dashboard", "src\dashboard\Synapse.Dashboard\Synapse.Dashboard.csproj", "{854B5E8B-3105-4CD3-B7DD-279278CAF138}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Dashboard", "src\dashboard\Synapse.Dashboard\Synapse.Dashboard.csproj", "{A9BB7219-B24B-4E40-B10A-69E618BDA272}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apis", "apis", "{0D5F8417-6AD7-4F3E-9D46-38CB1AC9D9FE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Api.Client.Http", "src\api\Synapse.Api.Client.Http\Synapse.Api.Client.Http.csproj", "{327FF68E-E729-4616-B1BE-B262A95890A2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "runtime", "runtime", "{E19B66DC-F575-4A18-B8AA-E9D3519EB885}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Api.Http", "src\api\Synapse.Api.Http\Synapse.Api.Http.csproj", "{387B4948-7754-4F0B-B806-94F68A9824AC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "management", "management", "{6DC4553F-E522-4BC3-83C2-EAFD6E2B6852}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Api.Application", "src\api\Synapse.Api.Application\Synapse.Api.Application.csproj", "{F8D7F756-16A1-420B-AED4-8EF9A65B640E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Management.Core", "src\apis\management\Synapse.Apis.Management.Core\Synapse.Apis.Management.Core.csproj", "{37E915CF-9811-4F5F-859F-52F65C5B9487}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "http", "http", "{1FC34AB7-B31C-47D2-B16C-C677F0794F63}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "grpc", "grpc", "{E3AA9F8A-39ED-445F-9B19-E04DCBEB943A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Management.Grpc", "src\apis\management\Synapse.Apis.Management.Grpc\Synapse.Apis.Management.Grpc.csproj", "{8B3AE585-D217-4271-8FE8-2DFBE554CD2B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Management.Grpc.Client", "src\apis\management\Synapse.Apis.Management.Grpc.Client\Synapse.Apis.Management.Grpc.Client.csproj", "{2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Management.Http.Client", "src\apis\management\Synapse.Apis.Management.Http.Client\Synapse.Apis.Management.Http.Client.csproj", "{2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Management.Http", "src\apis\management\Synapse.Apis.Management.Http\Synapse.Apis.Management.Http.csproj", "{7BD25BAF-E55A-4753-AB23-5D4518424970}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Runtime.Core", "src\apis\runtime\Synapse.Apis.Runtime.Core\Synapse.Apis.Runtime.Core.csproj", "{B2FE4BF5-21C8-4055-AE10-68D248CA60CF}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "grpc", "grpc", "{C44235AD-1105-493A-9802-1D3CFB293A04}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Runtime.Grpc", "src\apis\runtime\Synapse.Apis.Runtime.Grpc\Synapse.Apis.Runtime.Grpc.csproj", "{2FD03D00-6B85-4114-B05E-269668948727}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Runtime.Grpc.Client", "src\apis\runtime\Synapse.Apis.Runtime.Grpc.Client\Synapse.Apis.Runtime.Grpc.Client.csproj", "{ADDA4D4B-E342-402C-9A2F-BF7606E2414B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{638773F0-B231-4AF0-8E16-E877407E7250}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C31B1410-C7AF-4D11-8DA5-6877756A9907}" + ProjectSection(SolutionItems) = preProject + CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md + CONTRIBUTING.md = CONTRIBUTING.md + GOVERNANCE.md = GOVERNANCE.md + LICENSE = LICENSE + MAINTAINERS.md = MAINTAINERS.md + README.md = README.md + SECURITY.md = SECURITY.md + EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{CCCFA55B-D351-4583-9FB5-D51780A2C033}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F041A1CB-45FA-4432-BAD2-DE2EE174C6FC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "monitoring", "monitoring", "{0FF3E199-BA7A-4EA4-92B4-8910FF58A103}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Api.Server", "src\api\Synapse.Api.Server\Synapse.Api.Server.csproj", "{8DFC5C6C-830A-481F-8E88-632CF5065793}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{A8B7D153-AB02-40EF-9C7F-529BE98357E1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Core.Infrastructure", "src\core\Synapse.Core.Infrastructure\Synapse.Core.Infrastructure.csproj", "{03F9EC99-55D5-4880-9972-592A366418FD}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Monitoring.Core", "src\apis\monitoring\Synapse.Apis.Monitoring.Core\Synapse.Apis.Monitoring.Core.csproj", "{018CFFE3-A56E-496A-B288-E37B7F78EAF1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.IntegrationTests", "tests\Synapse.IntegrationTests\Synapse.IntegrationTests.csproj", "{6F7ED286-A02C-4E78-9FE3-2689BD94B97C}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "websocket", "websocket", "{3706E8C4-D534-4709-AD43-2F60E4F02609}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.UnitTests", "tests\Synapse.UnitTests\Synapse.UnitTests.csproj", "{CB1DF439-BFBA-408D-BC09-3FF94E6C4F0F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Monitoring.WebSocket", "src\apis\monitoring\Synapse.Apis.Monitoring.WebSocket\Synapse.Apis.Monitoring.WebSocket.csproj", "{4184709F-DB1A-4A27-ABC2-AE9696E40195}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Api.Client.Core", "src\api\Synapse.Api.Client.Core\Synapse.Api.Client.Core.csproj", "{3E650D2D-B018-4056-8922-FC7DF68233B1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ipc", "ipc", "{50840147-E3CD-42AB-955C-1070F21E81DE}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "runtime", "runtime", "{175CE1C5-FE17-4C8B-8823-E812BAD4E527}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Management.Ipc.Client", "src\apis\management\Synapse.Apis.Management.Ipc.Client\Synapse.Apis.Management.Ipc.Client.csproj", "{66F0739D-790F-4349-BB52-C86086C4D5EB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Abstractions", "src\runtime\Synapse.Runtime.Abstractions\Synapse.Runtime.Abstractions.csproj", "{A0E5E7F2-8C9C-4F36-B3FD-C09074893023}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ipc", "ipc", "{02C8E23B-EDCE-4373-96CA-F633AEAD17F6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Native", "src\runtime\Synapse.Runtime.Native\Synapse.Runtime.Native.csproj", "{DC24E506-602F-4FD9-B8C0-CEA6B2AD8888}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Apis.Runtime.Ipc.Client", "src\apis\runtime\Synapse.Apis.Runtime.Ipc.Client\Synapse.Apis.Runtime.Ipc.Client.csproj", "{CA50AABD-3B15-460B-8B50-E30815DE0A1D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Containerized", "src\runtime\Synapse.Runtime.Containerized\Synapse.Runtime.Containerized.csproj", "{F327B8F1-9A13-4924-AE1B-E69788AC73E7}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apps", "apps", "{43E4163F-489D-4F58-BDB9-12813207FC49}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runner", "src\runner\Synapse.Runner\Synapse.Runner.csproj", "{E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Server", "src\apps\Synapse.Server\Synapse.Server.csproj", "{957782C5-7A45-4D16-9539-607C0F397471}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Dashboard.StateManagement", "src\dashboard\Synapse.Dashboard.StateManagement\Synapse.Dashboard.StateManagement.csproj", "{91EF9F64-4997-407C-B353-C26B1421D0FB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Worker", "src\apps\Synapse.Worker\Synapse.Worker.csproj", "{DDDFF6AC-8237-4A59-A834-CF959419E316}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Operator", "src\operator\Synapse.Operator\Synapse.Operator.csproj", "{A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Cli", "src\apps\Synapse.Cli\Synapse.Cli.csproj", "{D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Cli", "src\cli\Synapse.Cli\Synapse.Cli.csproj", "{C86F6C8B-5946-433D-9E09-2C0269CE6372}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Native", "src\runtime\Synapse.Runtime.Native\Synapse.Runtime.Native.csproj", "{758730F9-7E71-410F-9B99-C3B6D9F0B4A2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "correlator", "correlator", "{F6CA60F0-3A4A-4B27-9805-6FA96F64D94D}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{D16F33A0-AFCB-474E-ADA1-07D3A1FE7B90}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Correlator", "src\correlator\Synapse.Correlator\Synapse.Correlator.csproj", "{1EA900CD-6CDE-4358-8412-060BF2AC3B21}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "persistence", "persistence", "{122C059F-62B2-4A98-BB40-351CE3D0ED98}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deployments", "deployments", "{562C91A3-6E91-4489-9D9D-064E7436D900}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Plugins.Persistence.EventStore", "src\plugins\persistence\Synapse.Plugins.Persistence.EventStore\Synapse.Plugins.Persistence.EventStore.csproj", "{5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}" +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "deployments\docker-compose\docker-compose.dcproj", "{A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Plugins.Persistence.MongoDB", "src\plugins\persistence\Synapse.Plugins.Persistence.MongoDB\Synapse.Plugins.Persistence.MongoDB.csproj", "{F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "community", "community", "{EFD78767-3171-46D2-880C-4E8F3C97529A}" + ProjectSection(SolutionItems) = preProject + community\README.md = community\README.md + EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deployment", "deployment", "{9DCC70EA-2F8F-4895-B9AA-110A98D50C47}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{750922F9-5C47-42FE-945F-576818E6DEC9}" EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "deployment\docker-compose\docker-compose.dcproj", "{A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "images", "images", "{81124062-334B-4AE3-A538-92B044207A94}" + ProjectSection(SolutionItems) = preProject + assets\images\logo.eps = assets\images\logo.eps + assets\images\logo.svg = assets\images\logo.svg + assets\images\logomark.eps = assets\images\logomark.eps + assets\images\logomark.svg = assets\images\logomark.svg + assets\images\logomark_background.eps = assets\images\logomark_background.eps + assets\images\logomark_background.svg = assets\images\logomark_background.svg + assets\images\logomark_black.eps = assets\images\logomark_black.eps + assets\images\logomark_black.svg = assets\images\logomark_black.svg + assets\images\logomark_white.eps = assets\images\logomark_white.eps + assets\images\logomark_white.svg = assets\images\logomark_white.svg + assets\images\logo_background.eps = assets\images\logo_background.eps + assets\images\logo_background.svg = assets\images\logo_background.svg + assets\images\logo_black.eps = assets\images\logo_black.eps + assets\images\logo_black.svg = assets\images\logo_black.svg + assets\images\logo_white.eps = assets\images\logo_white.eps + assets\images\logo_white.svg = assets\images\logo_white.svg + assets\images\transparent_logo.png = assets\images\transparent_logo.png + assets\images\transparent_logomark.ico = assets\images\transparent_logomark.ico + assets\images\transparent_logomark.png = assets\images\transparent_logomark.png + assets\images\transparent_logomark_black.png = assets\images\transparent_logomark_black.png + assets\images\transparent_logomark_white.png = assets\images\transparent_logomark_white.png + assets\images\transparent_logo_black.png = assets\images\transparent_logo_black.png + assets\images\transparent_logo_white.png = assets\images\transparent_logo_white.png + EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Synapse.Runtime.Kubernetes", "src\runtime\Synapse.Runtime.Kubernetes\Synapse.Runtime.Kubernetes.csproj", "{B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fonts", "fonts", "{2A6EE5DF-BD7E-4CC6-BB9B-7BE5FC128302}" + ProjectSection(SolutionItems) = preProject + assets\fonts\Aquatico-Regular.otf = assets\fonts\Aquatico-Regular.otf + assets\fonts\Aquatico-Regular.ttf = assets\fonts\Aquatico-Regular.ttf + assets\fonts\Aquatico-Regular.woff = assets\fonts\Aquatico-Regular.woff + assets\fonts\Aquatico-Regular.woff2 = assets\fonts\Aquatico-Regular.woff2 + EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "kubernetes", "kubernetes", "{8BA50965-1742-4C74-8768-18B8E1F6707F}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "helm", "helm", "{B6A96DC3-E57A-4022-9279-1DB57744C893}" ProjectSection(SolutionItems) = preProject - deployment\kubernetes\eventstore+mongo.yaml = deployment\kubernetes\eventstore+mongo.yaml - deployment\kubernetes\stand-alone.yaml = deployment\kubernetes\stand-alone.yaml + deployments\helm\Chart.yaml = deployments\helm\Chart.yaml + deployments\helm\values.yaml = deployments\helm\values.yaml EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{40114B2A-998C-471F-93F0-9834DE4F6BD2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{3F5CCCE8-46F8-4873-8EF9-38E14EAE7E01}" ProjectSection(SolutionItems) = preProject - CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md - CONTRIBUTING.md = CONTRIBUTING.md - GOVERNANCE.md = GOVERNANCE.md - LICENSE = LICENSE - MAINTAINERS.md = MAINTAINERS.md - MAITAINER-GUIDELINES.md = MAITAINER-GUIDELINES.md - OWNERS.md = OWNERS.md - README.md = README.md + deployments\helm\templates\configmap.yaml = deployments\helm\templates\configmap.yaml + deployments\helm\templates\deployment.yaml = deployments\helm\templates\deployment.yaml + deployments\helm\templates\ingress.yaml = deployments\helm\templates\ingress.yaml + deployments\helm\templates\services.yaml = deployments\helm\templates\services.yaml EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Debug|Any CPU.Build.0 = Debug|Any CPU - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Debug|x86.ActiveCfg = Debug|Any CPU - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Debug|x86.Build.0 = Debug|Any CPU - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Release|Any CPU.ActiveCfg = Release|Any CPU - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Release|Any CPU.Build.0 = Release|Any CPU - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Release|x86.ActiveCfg = Release|Any CPU - {032F057F-AF44-4E6F-9112-FC3436D24D96}.Release|x86.Build.0 = Release|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Debug|x86.ActiveCfg = Debug|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Debug|x86.Build.0 = Debug|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Release|Any CPU.Build.0 = Release|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Release|x86.ActiveCfg = Release|Any CPU - {B436986B-05AF-4F29-840E-54B88020C434}.Release|x86.Build.0 = Release|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Debug|x86.ActiveCfg = Debug|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Debug|x86.Build.0 = Debug|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Release|Any CPU.Build.0 = Release|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Release|x86.ActiveCfg = Release|Any CPU - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E}.Release|x86.Build.0 = Release|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Debug|x86.ActiveCfg = Debug|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Debug|x86.Build.0 = Debug|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Release|Any CPU.Build.0 = Release|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Release|x86.ActiveCfg = Release|Any CPU - {55AC4DEF-842B-42D9-8AB6-A601E3240584}.Release|x86.Build.0 = Release|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Debug|x86.ActiveCfg = Debug|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Debug|x86.Build.0 = Debug|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Release|Any CPU.Build.0 = Release|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Release|x86.ActiveCfg = Release|Any CPU - {2672E2E5-32D1-460E-A37B-3E268D70A4E5}.Release|x86.Build.0 = Release|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Debug|Any CPU.Build.0 = Debug|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Debug|x86.ActiveCfg = Debug|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Debug|x86.Build.0 = Debug|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Release|Any CPU.ActiveCfg = Release|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Release|Any CPU.Build.0 = Release|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Release|x86.ActiveCfg = Release|Any CPU - {854B5E8B-3105-4CD3-B7DD-279278CAF138}.Release|x86.Build.0 = Release|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Debug|x86.ActiveCfg = Debug|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Debug|x86.Build.0 = Debug|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Release|Any CPU.Build.0 = Release|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Release|x86.ActiveCfg = Release|Any CPU - {37E915CF-9811-4F5F-859F-52F65C5B9487}.Release|x86.Build.0 = Release|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Debug|x86.ActiveCfg = Debug|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Debug|x86.Build.0 = Debug|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Release|Any CPU.Build.0 = Release|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Release|x86.ActiveCfg = Release|Any CPU - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B}.Release|x86.Build.0 = Release|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Debug|x86.ActiveCfg = Debug|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Debug|x86.Build.0 = Debug|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Release|Any CPU.Build.0 = Release|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Release|x86.ActiveCfg = Release|Any CPU - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF}.Release|x86.Build.0 = Release|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Debug|x86.ActiveCfg = Debug|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Debug|x86.Build.0 = Debug|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Release|Any CPU.Build.0 = Release|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Release|x86.ActiveCfg = Release|Any CPU - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414}.Release|x86.Build.0 = Release|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Debug|x86.ActiveCfg = Debug|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Debug|x86.Build.0 = Debug|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Release|Any CPU.Build.0 = Release|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Release|x86.ActiveCfg = Release|Any CPU - {7BD25BAF-E55A-4753-AB23-5D4518424970}.Release|x86.Build.0 = Release|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Debug|x86.ActiveCfg = Debug|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Debug|x86.Build.0 = Debug|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Release|Any CPU.Build.0 = Release|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Release|x86.ActiveCfg = Release|Any CPU - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF}.Release|x86.Build.0 = Release|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Debug|x86.ActiveCfg = Debug|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Debug|x86.Build.0 = Debug|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Release|Any CPU.Build.0 = Release|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Release|x86.ActiveCfg = Release|Any CPU - {2FD03D00-6B85-4114-B05E-269668948727}.Release|x86.Build.0 = Release|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Debug|x86.ActiveCfg = Debug|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Debug|x86.Build.0 = Debug|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Release|Any CPU.Build.0 = Release|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Release|x86.ActiveCfg = Release|Any CPU - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B}.Release|x86.Build.0 = Release|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Debug|x86.ActiveCfg = Debug|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Debug|x86.Build.0 = Debug|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Release|Any CPU.Build.0 = Release|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Release|x86.ActiveCfg = Release|Any CPU - {018CFFE3-A56E-496A-B288-E37B7F78EAF1}.Release|x86.Build.0 = Release|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Debug|x86.ActiveCfg = Debug|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Debug|x86.Build.0 = Debug|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Release|Any CPU.Build.0 = Release|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Release|x86.ActiveCfg = Release|Any CPU - {4184709F-DB1A-4A27-ABC2-AE9696E40195}.Release|x86.Build.0 = Release|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Debug|x86.ActiveCfg = Debug|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Debug|x86.Build.0 = Debug|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Release|Any CPU.Build.0 = Release|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Release|x86.ActiveCfg = Release|Any CPU - {66F0739D-790F-4349-BB52-C86086C4D5EB}.Release|x86.Build.0 = Release|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Debug|x86.ActiveCfg = Debug|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Debug|x86.Build.0 = Debug|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Release|Any CPU.Build.0 = Release|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Release|x86.ActiveCfg = Release|Any CPU - {CA50AABD-3B15-460B-8B50-E30815DE0A1D}.Release|x86.Build.0 = Release|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Debug|Any CPU.Build.0 = Debug|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Debug|x86.ActiveCfg = Debug|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Debug|x86.Build.0 = Debug|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Release|Any CPU.ActiveCfg = Release|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Release|Any CPU.Build.0 = Release|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Release|x86.ActiveCfg = Release|Any CPU - {957782C5-7A45-4D16-9539-607C0F397471}.Release|x86.Build.0 = Release|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Debug|x86.ActiveCfg = Debug|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Debug|x86.Build.0 = Debug|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Release|Any CPU.Build.0 = Release|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Release|x86.ActiveCfg = Release|Any CPU - {DDDFF6AC-8237-4A59-A834-CF959419E316}.Release|x86.Build.0 = Release|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Debug|x86.ActiveCfg = Debug|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Debug|x86.Build.0 = Debug|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Release|Any CPU.Build.0 = Release|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Release|x86.ActiveCfg = Release|Any CPU - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18}.Release|x86.Build.0 = Release|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Debug|x86.ActiveCfg = Debug|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Debug|x86.Build.0 = Debug|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Release|Any CPU.Build.0 = Release|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Release|x86.ActiveCfg = Release|Any CPU - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2}.Release|x86.Build.0 = Release|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Debug|x86.ActiveCfg = Debug|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Debug|x86.Build.0 = Debug|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Release|Any CPU.Build.0 = Release|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Release|x86.ActiveCfg = Release|Any CPU - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1}.Release|x86.Build.0 = Release|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Debug|x86.ActiveCfg = Debug|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Debug|x86.Build.0 = Debug|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Release|Any CPU.Build.0 = Release|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Release|x86.ActiveCfg = Release|Any CPU - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1}.Release|x86.Build.0 = Release|Any CPU + {F2804D69-04C9-463D-B5E5-D3185163EBC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2804D69-04C9-463D-B5E5-D3185163EBC0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2804D69-04C9-463D-B5E5-D3185163EBC0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2804D69-04C9-463D-B5E5-D3185163EBC0}.Release|Any CPU.Build.0 = Release|Any CPU + {40EB503E-6E97-4375-A172-6EF55F9E0FF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40EB503E-6E97-4375-A172-6EF55F9E0FF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40EB503E-6E97-4375-A172-6EF55F9E0FF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40EB503E-6E97-4375-A172-6EF55F9E0FF3}.Release|Any CPU.Build.0 = Release|Any CPU + {A9BB7219-B24B-4E40-B10A-69E618BDA272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9BB7219-B24B-4E40-B10A-69E618BDA272}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9BB7219-B24B-4E40-B10A-69E618BDA272}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9BB7219-B24B-4E40-B10A-69E618BDA272}.Release|Any CPU.Build.0 = Release|Any CPU + {327FF68E-E729-4616-B1BE-B262A95890A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {327FF68E-E729-4616-B1BE-B262A95890A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {327FF68E-E729-4616-B1BE-B262A95890A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {327FF68E-E729-4616-B1BE-B262A95890A2}.Release|Any CPU.Build.0 = Release|Any CPU + {387B4948-7754-4F0B-B806-94F68A9824AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {387B4948-7754-4F0B-B806-94F68A9824AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {387B4948-7754-4F0B-B806-94F68A9824AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {387B4948-7754-4F0B-B806-94F68A9824AC}.Release|Any CPU.Build.0 = Release|Any CPU + {F8D7F756-16A1-420B-AED4-8EF9A65B640E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8D7F756-16A1-420B-AED4-8EF9A65B640E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8D7F756-16A1-420B-AED4-8EF9A65B640E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8D7F756-16A1-420B-AED4-8EF9A65B640E}.Release|Any CPU.Build.0 = Release|Any CPU + {8DFC5C6C-830A-481F-8E88-632CF5065793}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8DFC5C6C-830A-481F-8E88-632CF5065793}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8DFC5C6C-830A-481F-8E88-632CF5065793}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8DFC5C6C-830A-481F-8E88-632CF5065793}.Release|Any CPU.Build.0 = Release|Any CPU + {03F9EC99-55D5-4880-9972-592A366418FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03F9EC99-55D5-4880-9972-592A366418FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03F9EC99-55D5-4880-9972-592A366418FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03F9EC99-55D5-4880-9972-592A366418FD}.Release|Any CPU.Build.0 = Release|Any CPU + {6F7ED286-A02C-4E78-9FE3-2689BD94B97C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F7ED286-A02C-4E78-9FE3-2689BD94B97C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F7ED286-A02C-4E78-9FE3-2689BD94B97C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F7ED286-A02C-4E78-9FE3-2689BD94B97C}.Release|Any CPU.Build.0 = Release|Any CPU + {CB1DF439-BFBA-408D-BC09-3FF94E6C4F0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB1DF439-BFBA-408D-BC09-3FF94E6C4F0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB1DF439-BFBA-408D-BC09-3FF94E6C4F0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB1DF439-BFBA-408D-BC09-3FF94E6C4F0F}.Release|Any CPU.Build.0 = Release|Any CPU + {3E650D2D-B018-4056-8922-FC7DF68233B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E650D2D-B018-4056-8922-FC7DF68233B1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E650D2D-B018-4056-8922-FC7DF68233B1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E650D2D-B018-4056-8922-FC7DF68233B1}.Release|Any CPU.Build.0 = Release|Any CPU + {A0E5E7F2-8C9C-4F36-B3FD-C09074893023}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0E5E7F2-8C9C-4F36-B3FD-C09074893023}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0E5E7F2-8C9C-4F36-B3FD-C09074893023}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0E5E7F2-8C9C-4F36-B3FD-C09074893023}.Release|Any CPU.Build.0 = Release|Any CPU + {DC24E506-602F-4FD9-B8C0-CEA6B2AD8888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC24E506-602F-4FD9-B8C0-CEA6B2AD8888}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC24E506-602F-4FD9-B8C0-CEA6B2AD8888}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC24E506-602F-4FD9-B8C0-CEA6B2AD8888}.Release|Any CPU.Build.0 = Release|Any CPU + {F327B8F1-9A13-4924-AE1B-E69788AC73E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F327B8F1-9A13-4924-AE1B-E69788AC73E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F327B8F1-9A13-4924-AE1B-E69788AC73E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F327B8F1-9A13-4924-AE1B-E69788AC73E7}.Release|Any CPU.Build.0 = Release|Any CPU + {E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018}.Release|Any CPU.Build.0 = Release|Any CPU + {91EF9F64-4997-407C-B353-C26B1421D0FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91EF9F64-4997-407C-B353-C26B1421D0FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91EF9F64-4997-407C-B353-C26B1421D0FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91EF9F64-4997-407C-B353-C26B1421D0FB}.Release|Any CPU.Build.0 = Release|Any CPU + {A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0}.Release|Any CPU.Build.0 = Release|Any CPU + {C86F6C8B-5946-433D-9E09-2C0269CE6372}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C86F6C8B-5946-433D-9E09-2C0269CE6372}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C86F6C8B-5946-433D-9E09-2C0269CE6372}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C86F6C8B-5946-433D-9E09-2C0269CE6372}.Release|Any CPU.Build.0 = Release|Any CPU + {1EA900CD-6CDE-4358-8412-060BF2AC3B21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EA900CD-6CDE-4358-8412-060BF2AC3B21}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EA900CD-6CDE-4358-8412-060BF2AC3B21}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EA900CD-6CDE-4358-8412-060BF2AC3B21}.Release|Any CPU.Build.0 = Release|Any CPU {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Debug|x86.ActiveCfg = Debug|Any CPU - {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Debug|x86.Build.0 = Debug|Any CPU {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Release|Any CPU.Build.0 = Release|Any CPU - {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Release|x86.ActiveCfg = Release|Any CPU - {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17}.Release|x86.Build.0 = Release|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Debug|x86.ActiveCfg = Debug|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Debug|x86.Build.0 = Debug|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Release|Any CPU.Build.0 = Release|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Release|x86.ActiveCfg = Release|Any CPU - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {ED2CBFD5-2895-44F7-9F56-CEA41A7AF4F2} = {D789B1B2-073B-4609-948F-25C70148CDFB} - {032F057F-AF44-4E6F-9112-FC3436D24D96} = {FA3BA61B-BBD3-4EF3-8087-09C3D7411540} - {FA3BA61B-BBD3-4EF3-8087-09C3D7411540} = {D789B1B2-073B-4609-948F-25C70148CDFB} - {B436986B-05AF-4F29-840E-54B88020C434} = {ED2CBFD5-2895-44F7-9F56-CEA41A7AF4F2} - {6CCAA18D-7F84-4C8D-91F2-E6F0CF14583E} = {ED2CBFD5-2895-44F7-9F56-CEA41A7AF4F2} - {55AC4DEF-842B-42D9-8AB6-A601E3240584} = {ED2CBFD5-2895-44F7-9F56-CEA41A7AF4F2} - {2672E2E5-32D1-460E-A37B-3E268D70A4E5} = {ED2CBFD5-2895-44F7-9F56-CEA41A7AF4F2} - {19826311-054C-4E93-8B79-080145851939} = {D789B1B2-073B-4609-948F-25C70148CDFB} - {854B5E8B-3105-4CD3-B7DD-279278CAF138} = {19826311-054C-4E93-8B79-080145851939} - {0D5F8417-6AD7-4F3E-9D46-38CB1AC9D9FE} = {D789B1B2-073B-4609-948F-25C70148CDFB} - {E19B66DC-F575-4A18-B8AA-E9D3519EB885} = {0D5F8417-6AD7-4F3E-9D46-38CB1AC9D9FE} - {6DC4553F-E522-4BC3-83C2-EAFD6E2B6852} = {0D5F8417-6AD7-4F3E-9D46-38CB1AC9D9FE} - {37E915CF-9811-4F5F-859F-52F65C5B9487} = {638773F0-B231-4AF0-8E16-E877407E7250} - {1FC34AB7-B31C-47D2-B16C-C677F0794F63} = {6DC4553F-E522-4BC3-83C2-EAFD6E2B6852} - {E3AA9F8A-39ED-445F-9B19-E04DCBEB943A} = {6DC4553F-E522-4BC3-83C2-EAFD6E2B6852} - {8B3AE585-D217-4271-8FE8-2DFBE554CD2B} = {E3AA9F8A-39ED-445F-9B19-E04DCBEB943A} - {2A53C08C-085C-41AE-B5EA-245FFBF6B6EF} = {E3AA9F8A-39ED-445F-9B19-E04DCBEB943A} - {2B488BA0-BB1C-4CFC-81DF-E9228CAC3414} = {1FC34AB7-B31C-47D2-B16C-C677F0794F63} - {7BD25BAF-E55A-4753-AB23-5D4518424970} = {1FC34AB7-B31C-47D2-B16C-C677F0794F63} - {B2FE4BF5-21C8-4055-AE10-68D248CA60CF} = {CCCFA55B-D351-4583-9FB5-D51780A2C033} - {C44235AD-1105-493A-9802-1D3CFB293A04} = {E19B66DC-F575-4A18-B8AA-E9D3519EB885} - {2FD03D00-6B85-4114-B05E-269668948727} = {C44235AD-1105-493A-9802-1D3CFB293A04} - {ADDA4D4B-E342-402C-9A2F-BF7606E2414B} = {C44235AD-1105-493A-9802-1D3CFB293A04} - {638773F0-B231-4AF0-8E16-E877407E7250} = {6DC4553F-E522-4BC3-83C2-EAFD6E2B6852} - {CCCFA55B-D351-4583-9FB5-D51780A2C033} = {E19B66DC-F575-4A18-B8AA-E9D3519EB885} - {0FF3E199-BA7A-4EA4-92B4-8910FF58A103} = {0D5F8417-6AD7-4F3E-9D46-38CB1AC9D9FE} - {A8B7D153-AB02-40EF-9C7F-529BE98357E1} = {0FF3E199-BA7A-4EA4-92B4-8910FF58A103} - {018CFFE3-A56E-496A-B288-E37B7F78EAF1} = {A8B7D153-AB02-40EF-9C7F-529BE98357E1} - {3706E8C4-D534-4709-AD43-2F60E4F02609} = {0FF3E199-BA7A-4EA4-92B4-8910FF58A103} - {4184709F-DB1A-4A27-ABC2-AE9696E40195} = {3706E8C4-D534-4709-AD43-2F60E4F02609} - {50840147-E3CD-42AB-955C-1070F21E81DE} = {6DC4553F-E522-4BC3-83C2-EAFD6E2B6852} - {66F0739D-790F-4349-BB52-C86086C4D5EB} = {50840147-E3CD-42AB-955C-1070F21E81DE} - {02C8E23B-EDCE-4373-96CA-F633AEAD17F6} = {E19B66DC-F575-4A18-B8AA-E9D3519EB885} - {CA50AABD-3B15-460B-8B50-E30815DE0A1D} = {02C8E23B-EDCE-4373-96CA-F633AEAD17F6} - {43E4163F-489D-4F58-BDB9-12813207FC49} = {D789B1B2-073B-4609-948F-25C70148CDFB} - {957782C5-7A45-4D16-9539-607C0F397471} = {43E4163F-489D-4F58-BDB9-12813207FC49} - {DDDFF6AC-8237-4A59-A834-CF959419E316} = {43E4163F-489D-4F58-BDB9-12813207FC49} - {D4BEDBC3-47F8-4F4C-A644-995F10EE1C18} = {43E4163F-489D-4F58-BDB9-12813207FC49} - {758730F9-7E71-410F-9B99-C3B6D9F0B4A2} = {FA3BA61B-BBD3-4EF3-8087-09C3D7411540} - {D16F33A0-AFCB-474E-ADA1-07D3A1FE7B90} = {D789B1B2-073B-4609-948F-25C70148CDFB} - {122C059F-62B2-4A98-BB40-351CE3D0ED98} = {D16F33A0-AFCB-474E-ADA1-07D3A1FE7B90} - {5B2D5EF6-B553-4CA3-8433-5AA4C9738ED1} = {122C059F-62B2-4A98-BB40-351CE3D0ED98} - {F0D8E334-F777-46E0-BBE1-6F03CF4CB3C1} = {122C059F-62B2-4A98-BB40-351CE3D0ED98} - {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17} = {9DCC70EA-2F8F-4895-B9AA-110A98D50C47} - {B6EB8D95-3859-4F6B-8C2C-C0409E5E4CF1} = {FA3BA61B-BBD3-4EF3-8087-09C3D7411540} - {8BA50965-1742-4C74-8768-18B8E1F6707F} = {9DCC70EA-2F8F-4895-B9AA-110A98D50C47} + {9E296C8A-4D78-4592-B046-11A3A953FD25} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {7DF998B8-0FB1-470E-8ED0-EA1CC7B16901} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {D3B3B95D-B598-4B13-B754-4A7E530405A6} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {32EAD165-3D99-42CD-B3AF-05136DCC7F35} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {1DA47E5F-B23A-4D3C-96AA-4BD2662AB946} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {F2804D69-04C9-463D-B5E5-D3185163EBC0} = {9E296C8A-4D78-4592-B046-11A3A953FD25} + {40EB503E-6E97-4375-A172-6EF55F9E0FF3} = {9E296C8A-4D78-4592-B046-11A3A953FD25} + {63715FC0-736D-4972-A865-41126155DF45} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {A9BB7219-B24B-4E40-B10A-69E618BDA272} = {7DF998B8-0FB1-470E-8ED0-EA1CC7B16901} + {327FF68E-E729-4616-B1BE-B262A95890A2} = {63715FC0-736D-4972-A865-41126155DF45} + {387B4948-7754-4F0B-B806-94F68A9824AC} = {63715FC0-736D-4972-A865-41126155DF45} + {F8D7F756-16A1-420B-AED4-8EF9A65B640E} = {63715FC0-736D-4972-A865-41126155DF45} + {8DFC5C6C-830A-481F-8E88-632CF5065793} = {63715FC0-736D-4972-A865-41126155DF45} + {03F9EC99-55D5-4880-9972-592A366418FD} = {9E296C8A-4D78-4592-B046-11A3A953FD25} + {6F7ED286-A02C-4E78-9FE3-2689BD94B97C} = {F041A1CB-45FA-4432-BAD2-DE2EE174C6FC} + {CB1DF439-BFBA-408D-BC09-3FF94E6C4F0F} = {F041A1CB-45FA-4432-BAD2-DE2EE174C6FC} + {3E650D2D-B018-4056-8922-FC7DF68233B1} = {63715FC0-736D-4972-A865-41126155DF45} + {175CE1C5-FE17-4C8B-8823-E812BAD4E527} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {A0E5E7F2-8C9C-4F36-B3FD-C09074893023} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527} + {DC24E506-602F-4FD9-B8C0-CEA6B2AD8888} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527} + {F327B8F1-9A13-4924-AE1B-E69788AC73E7} = {175CE1C5-FE17-4C8B-8823-E812BAD4E527} + {E5FAA9BA-07C3-49CF-AD3B-897AE1D0B018} = {1DA47E5F-B23A-4D3C-96AA-4BD2662AB946} + {91EF9F64-4997-407C-B353-C26B1421D0FB} = {7DF998B8-0FB1-470E-8ED0-EA1CC7B16901} + {A9085F4A-5FDF-4F4A-B267-A03BC5E0FDB0} = {32EAD165-3D99-42CD-B3AF-05136DCC7F35} + {C86F6C8B-5946-433D-9E09-2C0269CE6372} = {D3B3B95D-B598-4B13-B754-4A7E530405A6} + {F6CA60F0-3A4A-4B27-9805-6FA96F64D94D} = {4B9AF05C-9D6D-48C0-994D-D4A5C28FA24D} + {1EA900CD-6CDE-4358-8412-060BF2AC3B21} = {F6CA60F0-3A4A-4B27-9805-6FA96F64D94D} + {A2D3AFB0-C7E0-4778-9D0A-DFCE0E24AF17} = {562C91A3-6E91-4489-9D9D-064E7436D900} + {81124062-334B-4AE3-A538-92B044207A94} = {750922F9-5C47-42FE-945F-576818E6DEC9} + {2A6EE5DF-BD7E-4CC6-BB9B-7BE5FC128302} = {750922F9-5C47-42FE-945F-576818E6DEC9} + {B6A96DC3-E57A-4022-9279-1DB57744C893} = {562C91A3-6E91-4489-9D9D-064E7436D900} + {3F5CCCE8-46F8-4873-8EF9-38E14EAE7E01} = {B6A96DC3-E57A-4022-9279-1DB57744C893} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {C1FCD70A-A848-4567-B59C-D7B076BC2F2E} + SolutionGuid = {2A6C03D6-355A-4B39-9F2B-D0FDE429C0E2} EndGlobalSection EndGlobal diff --git a/assets/fonts/Aquatico-Regular.otf b/assets/fonts/Aquatico-Regular.otf new file mode 100644 index 000000000..62083c8cc Binary files /dev/null and b/assets/fonts/Aquatico-Regular.otf differ diff --git a/assets/fonts/Aquatico-Regular.ttf b/assets/fonts/Aquatico-Regular.ttf new file mode 100644 index 000000000..3ebcfed4d Binary files /dev/null and b/assets/fonts/Aquatico-Regular.ttf differ diff --git a/assets/fonts/Aquatico-Regular.woff b/assets/fonts/Aquatico-Regular.woff new file mode 100644 index 000000000..832f418ab Binary files /dev/null and b/assets/fonts/Aquatico-Regular.woff differ diff --git a/assets/fonts/Aquatico-Regular.woff2 b/assets/fonts/Aquatico-Regular.woff2 new file mode 100644 index 000000000..a392a37e0 Binary files /dev/null and b/assets/fonts/Aquatico-Regular.woff2 differ diff --git a/assets/images/app.diagram.png b/assets/images/app.diagram.png deleted file mode 100644 index a8c7aec6e..000000000 Binary files a/assets/images/app.diagram.png and /dev/null differ diff --git a/assets/images/dashboard.png b/assets/images/dashboard.png deleted file mode 100644 index 9bee6ab8d..000000000 Binary files a/assets/images/dashboard.png and /dev/null differ diff --git a/assets/images/fonts.txt b/assets/images/fonts.txt new file mode 100644 index 000000000..fb6a76b7b --- /dev/null +++ b/assets/images/fonts.txt @@ -0,0 +1,3 @@ +Name: Rajdhani +URL: https://fonts.google.com/specimen/Rajdhani + diff --git a/assets/images/logo.eps b/assets/images/logo.eps new file mode 100644 index 000000000..eedff7dd7 --- /dev/null +++ b/assets/images/logo.eps @@ -0,0 +1,349 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:37:08 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Rajdhani-Regular +11 dict begin +/FontType 42 def +/FontName /Rajdhani-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674201c5d0b3e00000438000000646670676d8c9290580000 +049c00000b70676c796631a0edc00000009c0000039c686561640a7b8eaa0000100c00000036 +6868656109e8fefb0000104400000024686d74780ebc0201000010680000001c6c6f63610000 +0e0400001084000000206d617870018c0c67000010a40000002070726570a2c36a2e000010c4 +0000009100020078000001e0024600030007002f402c00020401010002015e00000303005200 +0000035605010300034a0404000004070407060500030003110605152b1311211101112111aa +0104feca01680218fe1601eafde80246fdba000000010051000001e1028300350030402d0003 +04000403006d0000010400016b0004040258000202224800010105580005052305493b33343b +333206061a2b3735343b01321d01143b01323d01342e033d0134363b0132161d01142b01223d +01342b01221d0114171e021d0114062b012226510b1d0b616861486667484c4174414c0b1d0b +6167607b3367484c4176414c8e240b0b2164652a2c321618403637424c4c421c0b0b19646632 +45180a1a443a31424c4c0001001b000001d20283001500254022130b04030001014702010101 +2248030100002300490200110e0906001502150406142b2123223d0103263b01321713331336 +3b01320703151401051d0bc2060d240905a0059f0509250b04c30ce701840c0cfeb901470c0c +fe7ce70c00010055000001ef0283001b00274024180a02000101470201010122480304020000 +23004902001613100d0805001b021b0506142b3323223511343b013217013311343b01321511 +142b012227012311147c1d0a0a1b09030133040b1c0b0b170907fece040c026b0c06fdf0020a +0c0cfd950c0b020cfdf50c0000000002001e000001f7028300130017002f402c170104010147 +000400030004035f000101224802050200002300490200161511100e0b080500130213060614 +2b3323223713363b01321713162b01222f0121070613033303441f0b04c7030b2f0a04c7030a +200a0336fefa3604bb73eb740c026b0c0cfd950c0ca0a00c024cfe8d01730000000200550000 +01d2028300130021003340302001030401470003000200030260000404015800010122480501 +000023004902001e1b1614100d0805001302130606142b3323223511343b0132161d0114062b +01221d0114133332363d0134262b01221511147c1d0a0ae6414c4c41b40a0ab02e30302eb00a +0c026b0c4c4275424c08de0c0120332f71303309fedb080000010055000001a0028300230034 +40310003000405030460000202015800010122480005050058060100002300490200201d1a17 +14110e0b0805002302230706142b2901223511343321321d01142321221d01143b01321d0114 +2b01221d01143321321d01140194fecb0a0a01350c0cfefd0a0ae70b0be70a0a01030c0c026b +0c0b170b09e6090b170b09f2090b170b00000000000000000000000000000000000000000000 +00300030021d002b002b036a026f026f002bff0d03e8fe0c036a026f026f0000ff0d03e8fe0c +00320032002d002d0283000002bf01fe0000ff7403e8fe0c0283000002bf01fe0000ff6b03e8 +fe0cb0002c20b0005558455920204bb8000d514bb006535a58b0341bb028596066208a5558b0 +022561b908000800636323621b2121b00059b000432344b20001004360422db0012cb0206066 +2db0022c206420b0c050b004265ab228010a43456345525b582123211b8a5820b050505821b0 +40591b20b038505821b038595920b1010a434563456164b028505821b1010a4345634520b030 +505821b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036 +505821b036601b605959591bb0012b595923b00050586559592db0032c204520b00425616420 +b005435058b0052342b00623421b212159b001602db0042c232123212064b105624220b00623 +42b1010a434563b1010a43b002604563b0032a2120b00643208a208ab0012bb1300525b00426 +515860501b6152595823592120b0405358b0012b1b21b0405923b000505865592db0052cb007 +432bb20002004360422db0062cb00723422320b000234261b0026266b00163b00160b0052a2d +b0072c20204520b00b4363b804006220b0005058b040605966b001636044b001602db0082cb2 +070b004345422a21b20001004360422db0092cb000432344b20001004360422db00a2c202045 +20b0012b23b00043b004256020458a2361206420b020505821b0001bb0305058b0201bb04059 +5923b00050586559b0032523614444b001602db00b2c20204520b0012b23b00043b004256020 +458a23612064b0245058b0001bb0405923b00050586559b0032523614444b001602db00c2c20 +b0002342b20b0a034558211b2321592a212db00d2cb1020245b06461442db00e2cb001602020 +b00c434ab000505820b00c234259b00d434ab000525820b00d2342592db00f2c20b0106266b0 +016320b80400638a2361b00e4360208a6020b00e2342232db0102c4b5458b10464445924b00d +6523782db0112c4b51584b5358b1046444591b215924b0136523782db0122cb1000f435558b1 +0f0f43b0016142b00f2b59b00043b0022542b10c022542b10d022542b001162320b003255058 +b101004360b00425428a8a208a2361b00e2a2123b00161208a2361b00e2a211bb101004360b0 +022542b0022561b00e2a2159b00c4347b00d434760b0026220b0005058b040605966b0016320 +b00b4363b804006220b0005058b040605966b0016360b10000132344b00143b0003eb2010101 +4360422db0132c00b10002455458b00f23422045b00b2342b00a23b00260422060b00161b510 +1001000e0042428a60b112062bb0722b1b22592db0142cb100132b2db0152cb101132b2db016 +2cb102132b2db0172cb103132b2db0182cb104132b2db0192cb105132b2db01a2cb106132b2d +b01b2cb107132b2db01c2cb108132b2db01d2cb109132b2db01e2c00b00d2bb10002455458b0 +0f23422045b00b2342b00a23b00260422060b00161b5101001000e0042428a60b112062bb072 +2b1b22592db01f2cb1001e2b2db0202cb1011e2b2db0212cb1021e2b2db0222cb1031e2b2db0 +232cb1041e2b2db0242cb1051e2b2db0252cb1061e2b2db0262cb1071e2b2db0272cb1081e2b +2db0282cb1091e2b2db0292c203cb001602db02a2c2060b01060204323b0016043b0022561b0 +0160b0292a212db02b2cb02a2bb02a2a2db02c2c2020472020b00b4363b804006220b0005058 +b040605966b001636023613823208a555820472020b00b4363b804006220b0005058b0406059 +66b00163602361381b21592db02d2c00b10002455458b00116b02c2ab00115301b22592db02e +2c00b00d2bb10002455458b00116b02c2ab00115301b22592db02f2c2035b001602db0302c00 +b0014563b804006220b0005058b040605966b00163b0012bb00b4363b804006220b0005058b0 +40605966b00163b0012bb00016b40000000000443e2338b12f01152a2db0312c203c204720b0 +0b4363b804006220b0005058b040605966b0016360b0004361382db0322c2e173c2db0332c20 +3c204720b00b4363b804006220b0005058b040605966b0016360b0004361b0014363382db034 +2cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b0012342b23301 +0115142a2db0352cb00016b00425b004254723472361b009432b658a2e2320203c8a382db036 +2cb00016b00425b00425202e472347236120b0042342b009432b20b060505820b0405158b302 +2003201bb30226031a5942422320b00843208a234723472361234660b00443b0026220b00050 +58b040605966b001636020b0012b208a8a6120b00243606423b0034361645058b00243611bb0 +03436059b00325b0026220b0005058b040605966b0016361232020b00426234661381b23b008 +4346b00225b0084347234723616020b00443b0026220b0005058b040605966b00163602320b0 +012b23b0044360b0012bb0052561b00525b0026220b0005058b040605966b00163b004266120 +b00425606423b0032560645058211b232159232020b0042623466138592db0372cb000162020 +20b00526202e4723472361233c382db0382cb0001620b0082342202020462347b0012b236138 +2db0392cb00016b00325b002254723472361b00054582e203c23211bb00225b0022547234723 +6120b00525b004254723472361b00625b0052549b0022561b9080008006363232058621b2159 +63b804006220b0005058b040605966b0016360232e2320203c8a382321592db03a2cb0001620 +b00843202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a38 +2db03b2c23202e46b00225465258203c592eb12b01142b2db03c2c23202e46b0022546505820 +3c592eb12b01142b2db03d2c23202e46b00225465258203c5923202e46b00225465058203c59 +2eb12b01142b2db03e2cb0352b23202e46b00225465258203c592eb12b01142b2db03f2cb036 +2b8a20203cb00423428a3823202e46b00225465258203c592eb12b01142bb004432eb02b2b2d +b0402cb00016b00425b00426202e4723472361b009432b23203c202e2338b12b01142b2db041 +2cb108042542b00016b00425b00425202e472347236120b0042342b009432b20b060505820b0 +405158b3022003201bb30226031a594242232047b00443b0026220b0005058b040605966b001 +636020b0012b208a8a6120b00243606423b0034361645058b00243611bb003436059b00325b0 +026220b0005058b040605966b0016361b0022546613823203c23381b212020462347b0012b23 +61382159b12b01142b2db0422cb0352b2eb12b01142b2db0432cb0362b212320203cb0042342 +2338b12b01142bb004432eb02b2b2db0442cb000152047b0002342b20001011514132eb0312a +2db0452cb000152047b0002342b20001011514132eb0312a2db0462cb100011413b0322a2db0 +472cb0342a2db0482cb000164523202e20468a236138b12b01142b2db0492cb0082342b0482b +2db04a2cb20000412b2db04b2cb20001412b2db04c2cb20100412b2db04d2cb20101412b2db0 +4e2cb20000422b2db04f2cb20001422b2db0502cb20100422b2db0512cb20101422b2db0522c +b200003e2b2db0532cb200013e2b2db0542cb201003e2b2db0552cb201013e2b2db0562cb200 +00402b2db0572cb20001402b2db0582cb20100402b2db0592cb20101402b2db05a2cb2000043 +2b2db05b2cb20001432b2db05c2cb20100432b2db05d2cb20101432b2db05e2cb200003f2b2d +b05f2cb200013f2b2db0602cb201003f2b2db0612cb201013f2b2db0622cb0372b2eb12b0114 +2b2db0632cb0372bb03b2b2db0642cb0372bb03c2b2db0652cb00016b0372bb03d2b2db0662c +b0382b2eb12b01142b2db0672cb0382bb03b2b2db0682cb0382bb03c2b2db0692cb0382bb03d +2b2db06a2cb0392b2eb12b01142b2db06b2cb0392bb03b2b2db06c2cb0392bb03c2b2db06d2c +b0392bb03d2b2db06e2cb03a2b2eb12b01142b2db06f2cb03a2bb03b2b2db0702cb03a2bb03c +2b2db0712cb03a2bb03d2b2db0722cb3090402034558211b232159422bb00865b003245078b0 +0115302d0001000000013375b8d025c45f0f3cf5000b03e800000000cfe607f000000000d532 +1021fe60fec607e403780000000800020001000000000001000003a2fea60000074afe60f903 +07e4000100000000000000000000000000000007025800780232005101ed001b024300550215 +001e020c005501e10055000000000000005c000001100000017c000001f80000027c0000030c +0000039c00010000000700ab000c000000000002003a004a0073000000cb0b7000000000004b +b800325258b101018e59b001b9080008006370b1000542b32d1902002ab1000542b520080e07 +02082ab1000542b52a06170502082ab1000742bb084003c0000200092ab1000942bb00400040 +000200092ab1030044b12401885158b0408858b1036444b12601885158ba0880000104408863 +5458b103004459595959b522081007020c2ab801ff85b0048db102004400000000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0.54902 0.407843 0.803922 rg +BT +210.190699 0 0 210.190699 119.472656 152.727074 Tm +/f-0-0 1 Tf +[(SY)-3(NA)-4(PS)-4(E)]TJ +ET +759.477 655.707 m 759.477 680.633 744.816 701.156 722.824 709.953 c 722.824 + 711.418 722.824 712.887 722.824 714.352 c 722.824 761.266 684.707 799.383 + 637.793 799.383 c 633.395 799.383 630.461 799.383 626.066 797.918 c 612.871 + 816.977 590.879 828.707 567.422 828.707 c 551.293 828.707 536.633 824.309 + 524.906 815.512 c 511.711 824.309 495.586 828.707 477.992 828.707 c 450.137 + 828.707 426.68 815.512 410.551 796.453 c 409.086 796.453 407.621 796.453 + 406.152 796.453 c 360.707 796.453 324.055 762.73 318.188 718.75 c 280.07 + 708.488 252.215 674.766 252.215 632.25 c 252.215 583.871 291.801 544.285 + 340.18 544.285 c 354.84 544.285 368.035 547.219 381.23 554.551 c 403.223 + 531.094 434.008 517.898 469.195 517.898 c 476.523 517.898 483.855 517.898 + 491.188 519.363 c 501.449 482.711 535.168 454.855 576.219 454.855 c 604.074 + 454.855 628.996 468.051 645.125 487.109 c 652.453 479.781 667.113 462.188 + 661.25 431.398 c 661.25 431.398 690.57 459.254 677.379 500.305 c 719.895 + 506.168 753.613 542.82 753.613 588.27 c 753.613 598.531 752.148 610.262 + 747.75 619.059 c 755.078 629.32 759.477 642.516 759.477 655.707 c h +733.09 614.66 m 736.02 605.863 737.484 598.531 737.484 589.734 c 737.484 + 550.152 705.234 516.434 664.184 516.434 c 661.25 516.434 658.316 516.434 + 655.387 516.434 c 645.125 517.898 l 639.258 509.102 l 626.066 485.645 602.609 + 470.984 576.219 470.984 c 543.965 470.984 514.645 492.973 505.848 523.762 + c 501.449 536.957 l 486.789 534.023 l 480.922 532.559 473.594 532.559 467.73 + 532.559 c 438.406 532.559 412.02 544.285 391.492 564.812 c 384.164 573.609 + l 373.898 569.211 l 363.637 563.348 351.91 561.879 340.18 561.879 c 300.598 + 561.879 266.875 594.133 266.875 635.184 c 266.875 668.902 288.867 698.227 + 321.121 705.555 c 331.383 708.488 l 332.852 718.75 l 337.25 755.402 368.035 + 781.789 404.688 781.789 c 406.152 781.789 407.621 781.789 409.086 781.789 + c 416.418 781.789 l 420.812 787.656 l 434.008 805.25 454.535 814.043 476.523 + 814.043 c 489.719 814.043 502.914 809.645 514.645 802.316 c 523.441 796.453 + l 535.168 803.781 l 545.43 811.113 555.691 814.043 568.887 814.043 c 587.945 + 814.043 604.074 805.25 615.801 790.586 c 621.668 783.258 l 630.461 784.723 + l 633.395 784.723 636.328 784.723 639.258 784.723 c 677.379 784.723 708.164 + 753.934 708.164 715.816 c 708.164 714.352 708.164 712.887 708.164 711.418 + c 708.164 699.691 l 718.426 695.293 l 734.555 687.961 744.816 673.301 744.816 + 655.707 c 744.816 646.914 741.883 636.648 736.02 629.32 c 730.156 621.988 + l h +733.09 614.66 m f +645.125 743.672 m 645.125 729.91 633.965 718.75 620.199 718.75 c 606.438 + 718.75 595.277 729.91 595.277 743.672 c 595.277 757.438 606.438 768.598 + 620.199 768.598 c 633.965 768.598 645.125 757.438 645.125 743.672 c h +645.125 743.672 m f +583.547 783.258 m 583.547 776.781 578.297 771.527 571.82 771.527 c 565.344 + 771.527 560.09 776.781 560.09 783.258 c 560.09 789.734 565.344 794.984 +571.82 794.984 c 578.297 794.984 583.547 789.734 583.547 783.258 c h +583.547 783.258 m f +576.219 731.945 m 576.219 729.516 574.25 727.547 571.82 727.547 c 569.391 + 727.547 567.422 729.516 567.422 731.945 c 567.422 734.375 569.391 736.344 + 571.82 736.344 c 574.25 736.344 576.219 734.375 576.219 731.945 c h +576.219 731.945 m f +491.188 748.07 m 491.188 745.641 489.215 743.672 486.789 743.672 c 484.359 + 743.672 482.391 745.641 482.391 748.07 c 482.391 750.5 484.359 752.469 +486.789 752.469 c 489.215 752.469 491.188 750.5 491.188 748.07 c h +491.188 748.07 m f +708.164 632.25 m 708.164 655.707 689.105 673.301 667.113 673.301 c 655.387 + 673.301 643.656 667.438 636.328 660.105 c 628.996 665.973 620.199 668.902 + 611.402 668.902 c 609.938 668.902 609.938 668.902 608.473 668.902 c 596.742 + 704.09 561.559 708.488 543.965 704.09 c 538.102 702.621 532.234 699.691 + 527.836 696.758 c 520.508 704.09 510.246 709.953 498.516 709.953 c 494.117 + 709.953 491.188 709.953 486.789 708.488 c 480.922 723.148 466.262 733.41 + 448.668 733.41 c 435.477 733.41 423.746 727.547 414.949 717.285 c 407.621 + 723.148 398.824 726.082 390.027 726.082 c 368.035 726.082 350.441 709.953 + 348.977 689.43 c 346.043 689.43 344.578 689.43 341.645 689.43 c 310.859 + 689.43 285.938 664.504 285.938 633.719 c 285.938 602.93 310.859 578.008 + 341.645 578.008 c 360.707 578.008 376.832 586.805 387.094 601.465 c 400.289 + 570.676 432.543 550.152 469.195 550.152 c 486.789 550.152 502.914 554.551 + 517.574 563.348 c 517.574 560.414 516.109 556.016 516.109 553.082 c 516.109 + 519.363 543.965 492.973 576.219 492.973 c 607.004 492.973 633.395 516.434 + 636.328 547.219 c 645.125 539.891 655.387 535.492 668.582 535.492 c 696.438 + 535.492 719.895 557.48 719.895 586.805 c 719.895 599.996 714.027 611.727 + 706.699 621.988 c 706.699 621.988 708.164 626.387 708.164 632.25 c h +343.113 611.727 m 335.781 611.727 329.918 617.59 329.918 624.922 c 329.918 + 632.25 335.781 638.117 343.113 638.117 c 350.441 638.117 356.309 632.25 + 356.309 624.922 c 354.84 617.59 350.441 611.727 343.113 611.727 c h +442.805 655.707 m 432.543 655.707 423.746 664.504 423.746 674.766 c 423.746 + 685.031 432.543 693.828 442.805 693.828 c 453.066 693.828 461.863 685.031 + 461.863 674.766 c 461.863 664.504 453.066 655.707 442.805 655.707 c h +511.711 611.727 m 507.312 611.727 502.914 616.125 502.914 620.523 c 502.914 + 624.922 507.312 629.32 511.711 629.32 c 516.109 629.32 520.508 624.922 +520.508 620.523 c 520.508 616.125 516.109 611.727 511.711 611.727 c h +551.293 589.734 m 549.828 589.734 548.363 591.203 548.363 592.668 c 548.363 + 594.133 549.828 595.602 551.293 595.602 c 552.762 595.602 554.227 594.133 + 554.227 592.668 c 554.227 591.203 552.762 589.734 551.293 589.734 c h +558.625 626.387 m 552.762 626.387 546.898 632.25 546.898 638.117 c 546.898 + 643.98 552.762 649.844 558.625 649.844 c 564.488 649.844 570.355 643.98 + 570.355 638.117 c 570.355 632.25 565.957 626.387 558.625 626.387 c h +626.066 578.008 m 620.199 578.008 617.27 582.406 617.27 586.805 c 617.27 + 591.203 621.668 595.602 626.066 595.602 c 630.461 595.602 634.859 591.203 + 634.859 586.805 c 634.859 582.406 631.93 578.008 626.066 578.008 c h +626.066 578.008 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logo.svg b/assets/images/logo.svg new file mode 100644 index 000000000..724494228 --- /dev/null +++ b/assets/images/logo.svg @@ -0,0 +1,18 @@ + + + +Created with Fabric.js 2.3.6 + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/logo_background.eps b/assets/images/logo_background.eps new file mode 100644 index 000000000..7e38ada81 --- /dev/null +++ b/assets/images/logo_background.eps @@ -0,0 +1,347 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:37:08 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 136 152 893 829 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Rajdhani-Regular +11 dict begin +/FontType 42 def +/FontName /Rajdhani-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674201c5d0b3e00000438000000646670676d8c9290580000 +049c00000b70676c796631a0edc00000009c0000039c686561640a7b8eaa0000100c00000036 +6868656109e8fefb0000104400000024686d74780ebc0201000010680000001c6c6f63610000 +0e0400001084000000206d617870018c0c67000010a40000002070726570a2c36a2e000010c4 +0000009100020078000001e0024600030007002f402c00020401010002015e00000303005200 +0000035605010300034a0404000004070407060500030003110605152b1311211101112111aa +0104feca01680218fe1601eafde80246fdba000000010051000001e1028300350030402d0003 +04000403006d0000010400016b0004040258000202224800010105580005052305493b33343b +333206061a2b3735343b01321d01143b01323d01342e033d0134363b0132161d01142b01223d +01342b01221d0114171e021d0114062b012226510b1d0b616861486667484c4174414c0b1d0b +6167607b3367484c4176414c8e240b0b2164652a2c321618403637424c4c421c0b0b19646632 +45180a1a443a31424c4c0001001b000001d20283001500254022130b04030001014702010101 +2248030100002300490200110e0906001502150406142b2123223d0103263b01321713331336 +3b01320703151401051d0bc2060d240905a0059f0509250b04c30ce701840c0cfeb901470c0c +fe7ce70c00010055000001ef0283001b00274024180a02000101470201010122480304020000 +23004902001613100d0805001b021b0506142b3323223511343b013217013311343b01321511 +142b012227012311147c1d0a0a1b09030133040b1c0b0b170907fece040c026b0c06fdf0020a +0c0cfd950c0b020cfdf50c0000000002001e000001f7028300130017002f402c170104010147 +000400030004035f000101224802050200002300490200161511100e0b080500130213060614 +2b3323223713363b01321713162b01222f0121070613033303441f0b04c7030b2f0a04c7030a +200a0336fefa3604bb73eb740c026b0c0cfd950c0ca0a00c024cfe8d01730000000200550000 +01d2028300130021003340302001030401470003000200030260000404015800010122480501 +000023004902001e1b1614100d0805001302130606142b3323223511343b0132161d0114062b +01221d0114133332363d0134262b01221511147c1d0a0ae6414c4c41b40a0ab02e30302eb00a +0c026b0c4c4275424c08de0c0120332f71303309fedb080000010055000001a0028300230034 +40310003000405030460000202015800010122480005050058060100002300490200201d1a17 +14110e0b0805002302230706142b2901223511343321321d01142321221d01143b01321d0114 +2b01221d01143321321d01140194fecb0a0a01350c0cfefd0a0ae70b0be70a0a01030c0c026b +0c0b170b09e6090b170b09f2090b170b00000000000000000000000000000000000000000000 +00300030021d002b002b036a026f026f002bff0d03e8fe0c036a026f026f0000ff0d03e8fe0c +00320032002d002d0283000002bf01fe0000ff7403e8fe0c0283000002bf01fe0000ff6b03e8 +fe0cb0002c20b0005558455920204bb8000d514bb006535a58b0341bb028596066208a5558b0 +022561b908000800636323621b2121b00059b000432344b20001004360422db0012cb0206066 +2db0022c206420b0c050b004265ab228010a43456345525b582123211b8a5820b050505821b0 +40591b20b038505821b038595920b1010a434563456164b028505821b1010a4345634520b030 +505821b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036 +505821b036601b605959591bb0012b595923b00050586559592db0032c204520b00425616420 +b005435058b0052342b00623421b212159b001602db0042c232123212064b105624220b00623 +42b1010a434563b1010a43b002604563b0032a2120b00643208a208ab0012bb1300525b00426 +515860501b6152595823592120b0405358b0012b1b21b0405923b000505865592db0052cb007 +432bb20002004360422db0062cb00723422320b000234261b0026266b00163b00160b0052a2d +b0072c20204520b00b4363b804006220b0005058b040605966b001636044b001602db0082cb2 +070b004345422a21b20001004360422db0092cb000432344b20001004360422db00a2c202045 +20b0012b23b00043b004256020458a2361206420b020505821b0001bb0305058b0201bb04059 +5923b00050586559b0032523614444b001602db00b2c20204520b0012b23b00043b004256020 +458a23612064b0245058b0001bb0405923b00050586559b0032523614444b001602db00c2c20 +b0002342b20b0a034558211b2321592a212db00d2cb1020245b06461442db00e2cb001602020 +b00c434ab000505820b00c234259b00d434ab000525820b00d2342592db00f2c20b0106266b0 +016320b80400638a2361b00e4360208a6020b00e2342232db0102c4b5458b10464445924b00d +6523782db0112c4b51584b5358b1046444591b215924b0136523782db0122cb1000f435558b1 +0f0f43b0016142b00f2b59b00043b0022542b10c022542b10d022542b001162320b003255058 +b101004360b00425428a8a208a2361b00e2a2123b00161208a2361b00e2a211bb101004360b0 +022542b0022561b00e2a2159b00c4347b00d434760b0026220b0005058b040605966b0016320 +b00b4363b804006220b0005058b040605966b0016360b10000132344b00143b0003eb2010101 +4360422db0132c00b10002455458b00f23422045b00b2342b00a23b00260422060b00161b510 +1001000e0042428a60b112062bb0722b1b22592db0142cb100132b2db0152cb101132b2db016 +2cb102132b2db0172cb103132b2db0182cb104132b2db0192cb105132b2db01a2cb106132b2d +b01b2cb107132b2db01c2cb108132b2db01d2cb109132b2db01e2c00b00d2bb10002455458b0 +0f23422045b00b2342b00a23b00260422060b00161b5101001000e0042428a60b112062bb072 +2b1b22592db01f2cb1001e2b2db0202cb1011e2b2db0212cb1021e2b2db0222cb1031e2b2db0 +232cb1041e2b2db0242cb1051e2b2db0252cb1061e2b2db0262cb1071e2b2db0272cb1081e2b +2db0282cb1091e2b2db0292c203cb001602db02a2c2060b01060204323b0016043b0022561b0 +0160b0292a212db02b2cb02a2bb02a2a2db02c2c2020472020b00b4363b804006220b0005058 +b040605966b001636023613823208a555820472020b00b4363b804006220b0005058b0406059 +66b00163602361381b21592db02d2c00b10002455458b00116b02c2ab00115301b22592db02e +2c00b00d2bb10002455458b00116b02c2ab00115301b22592db02f2c2035b001602db0302c00 +b0014563b804006220b0005058b040605966b00163b0012bb00b4363b804006220b0005058b0 +40605966b00163b0012bb00016b40000000000443e2338b12f01152a2db0312c203c204720b0 +0b4363b804006220b0005058b040605966b0016360b0004361382db0322c2e173c2db0332c20 +3c204720b00b4363b804006220b0005058b040605966b0016360b0004361b0014363382db034 +2cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b0012342b23301 +0115142a2db0352cb00016b00425b004254723472361b009432b658a2e2320203c8a382db036 +2cb00016b00425b00425202e472347236120b0042342b009432b20b060505820b0405158b302 +2003201bb30226031a5942422320b00843208a234723472361234660b00443b0026220b00050 +58b040605966b001636020b0012b208a8a6120b00243606423b0034361645058b00243611bb0 +03436059b00325b0026220b0005058b040605966b0016361232020b00426234661381b23b008 +4346b00225b0084347234723616020b00443b0026220b0005058b040605966b00163602320b0 +012b23b0044360b0012bb0052561b00525b0026220b0005058b040605966b00163b004266120 +b00425606423b0032560645058211b232159232020b0042623466138592db0372cb000162020 +20b00526202e4723472361233c382db0382cb0001620b0082342202020462347b0012b236138 +2db0392cb00016b00325b002254723472361b00054582e203c23211bb00225b0022547234723 +6120b00525b004254723472361b00625b0052549b0022561b9080008006363232058621b2159 +63b804006220b0005058b040605966b0016360232e2320203c8a382321592db03a2cb0001620 +b00843202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a38 +2db03b2c23202e46b00225465258203c592eb12b01142b2db03c2c23202e46b0022546505820 +3c592eb12b01142b2db03d2c23202e46b00225465258203c5923202e46b00225465058203c59 +2eb12b01142b2db03e2cb0352b23202e46b00225465258203c592eb12b01142b2db03f2cb036 +2b8a20203cb00423428a3823202e46b00225465258203c592eb12b01142bb004432eb02b2b2d +b0402cb00016b00425b00426202e4723472361b009432b23203c202e2338b12b01142b2db041 +2cb108042542b00016b00425b00425202e472347236120b0042342b009432b20b060505820b0 +405158b3022003201bb30226031a594242232047b00443b0026220b0005058b040605966b001 +636020b0012b208a8a6120b00243606423b0034361645058b00243611bb003436059b00325b0 +026220b0005058b040605966b0016361b0022546613823203c23381b212020462347b0012b23 +61382159b12b01142b2db0422cb0352b2eb12b01142b2db0432cb0362b212320203cb0042342 +2338b12b01142bb004432eb02b2b2db0442cb000152047b0002342b20001011514132eb0312a +2db0452cb000152047b0002342b20001011514132eb0312a2db0462cb100011413b0322a2db0 +472cb0342a2db0482cb000164523202e20468a236138b12b01142b2db0492cb0082342b0482b +2db04a2cb20000412b2db04b2cb20001412b2db04c2cb20100412b2db04d2cb20101412b2db0 +4e2cb20000422b2db04f2cb20001422b2db0502cb20100422b2db0512cb20101422b2db0522c +b200003e2b2db0532cb200013e2b2db0542cb201003e2b2db0552cb201013e2b2db0562cb200 +00402b2db0572cb20001402b2db0582cb20100402b2db0592cb20101402b2db05a2cb2000043 +2b2db05b2cb20001432b2db05c2cb20100432b2db05d2cb20101432b2db05e2cb200003f2b2d +b05f2cb200013f2b2db0602cb201003f2b2db0612cb201013f2b2db0622cb0372b2eb12b0114 +2b2db0632cb0372bb03b2b2db0642cb0372bb03c2b2db0652cb00016b0372bb03d2b2db0662c +b0382b2eb12b01142b2db0672cb0382bb03b2b2db0682cb0382bb03c2b2db0692cb0382bb03d +2b2db06a2cb0392b2eb12b01142b2db06b2cb0392bb03b2b2db06c2cb0392bb03c2b2db06d2c +b0392bb03d2b2db06e2cb03a2b2eb12b01142b2db06f2cb03a2bb03b2b2db0702cb03a2bb03c +2b2db0712cb03a2bb03d2b2db0722cb3090402034558211b232159422bb00865b003245078b0 +0115302d0001000000013375b8d025c45f0f3cf5000b03e800000000cfe607f000000000d532 +1021fe60fec607e403780000000800020001000000000001000003a2fea60000074afe60f903 +07e4000100000000000000000000000000000007025800780232005101ed001b024300550215 +001e020c005501e10055000000000000005c000001100000017c000001f80000027c0000030c +0000039c00010000000700ab000c000000000002003a004a0073000000cb0b7000000000004b +b800325258b101018e59b001b9080008006370b1000542b32d1902002ab1000542b520080e07 +02082ab1000542b52a06170502082ab1000742bb084003c0000200092ab1000942bb00400040 +000200092ab1030044b12401885158b0408858b1036444b12601885158ba0880000104408863 +5458b103004459595959b522081007020c2ab801ff85b0048db102004400000000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 136 152 893 829 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 136 152 757 677 rectclip q +0.54902 0.407843 0.803922 rg +BT +210.190699 0 0 210.190699 119.472656 152.727074 Tm +/f-0-0 1 Tf +[(SY)-3(NA)-4(PS)-4(E)]TJ +ET +759.477 655.707 m 759.477 680.633 744.816 701.156 722.824 709.953 c 722.824 + 711.418 722.824 712.887 722.824 714.352 c 722.824 761.266 684.707 799.383 + 637.793 799.383 c 633.395 799.383 630.461 799.383 626.066 797.918 c 612.871 + 816.977 590.879 828.707 567.422 828.707 c 551.293 828.707 536.633 824.309 + 524.906 815.512 c 511.711 824.309 495.586 828.707 477.992 828.707 c 450.137 + 828.707 426.68 815.512 410.551 796.453 c 409.086 796.453 407.621 796.453 + 406.152 796.453 c 360.707 796.453 324.055 762.73 318.188 718.75 c 280.07 + 708.488 252.215 674.766 252.215 632.25 c 252.215 583.871 291.801 544.285 + 340.18 544.285 c 354.84 544.285 368.035 547.219 381.23 554.551 c 403.223 + 531.094 434.008 517.898 469.195 517.898 c 476.523 517.898 483.855 517.898 + 491.188 519.363 c 501.449 482.711 535.168 454.855 576.219 454.855 c 604.074 + 454.855 628.996 468.051 645.125 487.109 c 652.453 479.781 667.113 462.188 + 661.25 431.398 c 661.25 431.398 690.57 459.254 677.379 500.305 c 719.895 + 506.168 753.613 542.82 753.613 588.27 c 753.613 598.531 752.148 610.262 + 747.75 619.059 c 755.078 629.32 759.477 642.516 759.477 655.707 c h +733.09 614.66 m 736.02 605.863 737.484 598.531 737.484 589.734 c 737.484 + 550.152 705.234 516.434 664.184 516.434 c 661.25 516.434 658.316 516.434 + 655.387 516.434 c 645.125 517.898 l 639.258 509.102 l 626.066 485.645 602.609 + 470.984 576.219 470.984 c 543.965 470.984 514.645 492.973 505.848 523.762 + c 501.449 536.957 l 486.789 534.023 l 480.922 532.559 473.594 532.559 467.73 + 532.559 c 438.406 532.559 412.02 544.285 391.492 564.812 c 384.164 573.609 + l 373.898 569.211 l 363.637 563.348 351.91 561.879 340.18 561.879 c 300.598 + 561.879 266.875 594.133 266.875 635.184 c 266.875 668.902 288.867 698.227 + 321.121 705.555 c 331.383 708.488 l 332.852 718.75 l 337.25 755.402 368.035 + 781.789 404.688 781.789 c 406.152 781.789 407.621 781.789 409.086 781.789 + c 416.418 781.789 l 420.812 787.656 l 434.008 805.25 454.535 814.043 476.523 + 814.043 c 489.719 814.043 502.914 809.645 514.645 802.316 c 523.441 796.453 + l 535.168 803.781 l 545.43 811.113 555.691 814.043 568.887 814.043 c 587.945 + 814.043 604.074 805.25 615.801 790.586 c 621.668 783.258 l 630.461 784.723 + l 633.395 784.723 636.328 784.723 639.258 784.723 c 677.379 784.723 708.164 + 753.934 708.164 715.816 c 708.164 714.352 708.164 712.887 708.164 711.418 + c 708.164 699.691 l 718.426 695.293 l 734.555 687.961 744.816 673.301 744.816 + 655.707 c 744.816 646.914 741.883 636.648 736.02 629.32 c 730.156 621.988 + l h +733.09 614.66 m f +645.125 743.672 m 645.125 729.91 633.965 718.75 620.199 718.75 c 606.438 + 718.75 595.277 729.91 595.277 743.672 c 595.277 757.438 606.438 768.598 + 620.199 768.598 c 633.965 768.598 645.125 757.438 645.125 743.672 c h +645.125 743.672 m f +583.547 783.258 m 583.547 776.781 578.297 771.527 571.82 771.527 c 565.344 + 771.527 560.09 776.781 560.09 783.258 c 560.09 789.734 565.344 794.984 +571.82 794.984 c 578.297 794.984 583.547 789.734 583.547 783.258 c h +583.547 783.258 m f +576.219 731.945 m 576.219 729.516 574.25 727.547 571.82 727.547 c 569.391 + 727.547 567.422 729.516 567.422 731.945 c 567.422 734.375 569.391 736.344 + 571.82 736.344 c 574.25 736.344 576.219 734.375 576.219 731.945 c h +576.219 731.945 m f +491.188 748.07 m 491.188 745.641 489.215 743.672 486.789 743.672 c 484.359 + 743.672 482.391 745.641 482.391 748.07 c 482.391 750.5 484.359 752.469 +486.789 752.469 c 489.215 752.469 491.188 750.5 491.188 748.07 c h +491.188 748.07 m f +708.164 632.25 m 708.164 655.707 689.105 673.301 667.113 673.301 c 655.387 + 673.301 643.656 667.438 636.328 660.105 c 628.996 665.973 620.199 668.902 + 611.402 668.902 c 609.938 668.902 609.938 668.902 608.473 668.902 c 596.742 + 704.09 561.559 708.488 543.965 704.09 c 538.102 702.621 532.234 699.691 + 527.836 696.758 c 520.508 704.09 510.246 709.953 498.516 709.953 c 494.117 + 709.953 491.188 709.953 486.789 708.488 c 480.922 723.148 466.262 733.41 + 448.668 733.41 c 435.477 733.41 423.746 727.547 414.949 717.285 c 407.621 + 723.148 398.824 726.082 390.027 726.082 c 368.035 726.082 350.441 709.953 + 348.977 689.43 c 346.043 689.43 344.578 689.43 341.645 689.43 c 310.859 + 689.43 285.938 664.504 285.938 633.719 c 285.938 602.93 310.859 578.008 + 341.645 578.008 c 360.707 578.008 376.832 586.805 387.094 601.465 c 400.289 + 570.676 432.543 550.152 469.195 550.152 c 486.789 550.152 502.914 554.551 + 517.574 563.348 c 517.574 560.414 516.109 556.016 516.109 553.082 c 516.109 + 519.363 543.965 492.973 576.219 492.973 c 607.004 492.973 633.395 516.434 + 636.328 547.219 c 645.125 539.891 655.387 535.492 668.582 535.492 c 696.438 + 535.492 719.895 557.48 719.895 586.805 c 719.895 599.996 714.027 611.727 + 706.699 621.988 c 706.699 621.988 708.164 626.387 708.164 632.25 c h +343.113 611.727 m 335.781 611.727 329.918 617.59 329.918 624.922 c 329.918 + 632.25 335.781 638.117 343.113 638.117 c 350.441 638.117 356.309 632.25 + 356.309 624.922 c 354.84 617.59 350.441 611.727 343.113 611.727 c h +442.805 655.707 m 432.543 655.707 423.746 664.504 423.746 674.766 c 423.746 + 685.031 432.543 693.828 442.805 693.828 c 453.066 693.828 461.863 685.031 + 461.863 674.766 c 461.863 664.504 453.066 655.707 442.805 655.707 c h +511.711 611.727 m 507.312 611.727 502.914 616.125 502.914 620.523 c 502.914 + 624.922 507.312 629.32 511.711 629.32 c 516.109 629.32 520.508 624.922 +520.508 620.523 c 520.508 616.125 516.109 611.727 511.711 611.727 c h +551.293 589.734 m 549.828 589.734 548.363 591.203 548.363 592.668 c 548.363 + 594.133 549.828 595.602 551.293 595.602 c 552.762 595.602 554.227 594.133 + 554.227 592.668 c 554.227 591.203 552.762 589.734 551.293 589.734 c h +558.625 626.387 m 552.762 626.387 546.898 632.25 546.898 638.117 c 546.898 + 643.98 552.762 649.844 558.625 649.844 c 564.488 649.844 570.355 643.98 + 570.355 638.117 c 570.355 632.25 565.957 626.387 558.625 626.387 c h +626.066 578.008 m 620.199 578.008 617.27 582.406 617.27 586.805 c 617.27 + 591.203 621.668 595.602 626.066 595.602 c 630.461 595.602 634.859 591.203 + 634.859 586.805 c 634.859 582.406 631.93 578.008 626.066 578.008 c h +626.066 578.008 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logo_background.svg b/assets/images/logo_background.svg new file mode 100644 index 000000000..4d679e608 --- /dev/null +++ b/assets/images/logo_background.svg @@ -0,0 +1,19 @@ + + + +Created with Fabric.js 2.3.6 + + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/logo_black.eps b/assets/images/logo_black.eps new file mode 100644 index 000000000..97604fcff --- /dev/null +++ b/assets/images/logo_black.eps @@ -0,0 +1,349 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:37:09 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Rajdhani-Regular +11 dict begin +/FontType 42 def +/FontName /Rajdhani-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674201c5d0b3e00000438000000646670676d8c9290580000 +049c00000b70676c796631a0edc00000009c0000039c686561640a7b8eaa0000100c00000036 +6868656109e8fefb0000104400000024686d74780ebc0201000010680000001c6c6f63610000 +0e0400001084000000206d617870018c0c67000010a40000002070726570a2c36a2e000010c4 +0000009100020078000001e0024600030007002f402c00020401010002015e00000303005200 +0000035605010300034a0404000004070407060500030003110605152b1311211101112111aa +0104feca01680218fe1601eafde80246fdba000000010051000001e1028300350030402d0003 +04000403006d0000010400016b0004040258000202224800010105580005052305493b33343b +333206061a2b3735343b01321d01143b01323d01342e033d0134363b0132161d01142b01223d +01342b01221d0114171e021d0114062b012226510b1d0b616861486667484c4174414c0b1d0b +6167607b3367484c4176414c8e240b0b2164652a2c321618403637424c4c421c0b0b19646632 +45180a1a443a31424c4c0001001b000001d20283001500254022130b04030001014702010101 +2248030100002300490200110e0906001502150406142b2123223d0103263b01321713331336 +3b01320703151401051d0bc2060d240905a0059f0509250b04c30ce701840c0cfeb901470c0c +fe7ce70c00010055000001ef0283001b00274024180a02000101470201010122480304020000 +23004902001613100d0805001b021b0506142b3323223511343b013217013311343b01321511 +142b012227012311147c1d0a0a1b09030133040b1c0b0b170907fece040c026b0c06fdf0020a +0c0cfd950c0b020cfdf50c0000000002001e000001f7028300130017002f402c170104010147 +000400030004035f000101224802050200002300490200161511100e0b080500130213060614 +2b3323223713363b01321713162b01222f0121070613033303441f0b04c7030b2f0a04c7030a +200a0336fefa3604bb73eb740c026b0c0cfd950c0ca0a00c024cfe8d01730000000200550000 +01d2028300130021003340302001030401470003000200030260000404015800010122480501 +000023004902001e1b1614100d0805001302130606142b3323223511343b0132161d0114062b +01221d0114133332363d0134262b01221511147c1d0a0ae6414c4c41b40a0ab02e30302eb00a +0c026b0c4c4275424c08de0c0120332f71303309fedb080000010055000001a0028300230034 +40310003000405030460000202015800010122480005050058060100002300490200201d1a17 +14110e0b0805002302230706142b2901223511343321321d01142321221d01143b01321d0114 +2b01221d01143321321d01140194fecb0a0a01350c0cfefd0a0ae70b0be70a0a01030c0c026b +0c0b170b09e6090b170b09f2090b170b00000000000000000000000000000000000000000000 +00300030021d002b002b036a026f026f002bff0d03e8fe0c036a026f026f0000ff0d03e8fe0c +00320032002d002d0283000002bf01fe0000ff7403e8fe0c0283000002bf01fe0000ff6b03e8 +fe0cb0002c20b0005558455920204bb8000d514bb006535a58b0341bb028596066208a5558b0 +022561b908000800636323621b2121b00059b000432344b20001004360422db0012cb0206066 +2db0022c206420b0c050b004265ab228010a43456345525b582123211b8a5820b050505821b0 +40591b20b038505821b038595920b1010a434563456164b028505821b1010a4345634520b030 +505821b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036 +505821b036601b605959591bb0012b595923b00050586559592db0032c204520b00425616420 +b005435058b0052342b00623421b212159b001602db0042c232123212064b105624220b00623 +42b1010a434563b1010a43b002604563b0032a2120b00643208a208ab0012bb1300525b00426 +515860501b6152595823592120b0405358b0012b1b21b0405923b000505865592db0052cb007 +432bb20002004360422db0062cb00723422320b000234261b0026266b00163b00160b0052a2d +b0072c20204520b00b4363b804006220b0005058b040605966b001636044b001602db0082cb2 +070b004345422a21b20001004360422db0092cb000432344b20001004360422db00a2c202045 +20b0012b23b00043b004256020458a2361206420b020505821b0001bb0305058b0201bb04059 +5923b00050586559b0032523614444b001602db00b2c20204520b0012b23b00043b004256020 +458a23612064b0245058b0001bb0405923b00050586559b0032523614444b001602db00c2c20 +b0002342b20b0a034558211b2321592a212db00d2cb1020245b06461442db00e2cb001602020 +b00c434ab000505820b00c234259b00d434ab000525820b00d2342592db00f2c20b0106266b0 +016320b80400638a2361b00e4360208a6020b00e2342232db0102c4b5458b10464445924b00d +6523782db0112c4b51584b5358b1046444591b215924b0136523782db0122cb1000f435558b1 +0f0f43b0016142b00f2b59b00043b0022542b10c022542b10d022542b001162320b003255058 +b101004360b00425428a8a208a2361b00e2a2123b00161208a2361b00e2a211bb101004360b0 +022542b0022561b00e2a2159b00c4347b00d434760b0026220b0005058b040605966b0016320 +b00b4363b804006220b0005058b040605966b0016360b10000132344b00143b0003eb2010101 +4360422db0132c00b10002455458b00f23422045b00b2342b00a23b00260422060b00161b510 +1001000e0042428a60b112062bb0722b1b22592db0142cb100132b2db0152cb101132b2db016 +2cb102132b2db0172cb103132b2db0182cb104132b2db0192cb105132b2db01a2cb106132b2d +b01b2cb107132b2db01c2cb108132b2db01d2cb109132b2db01e2c00b00d2bb10002455458b0 +0f23422045b00b2342b00a23b00260422060b00161b5101001000e0042428a60b112062bb072 +2b1b22592db01f2cb1001e2b2db0202cb1011e2b2db0212cb1021e2b2db0222cb1031e2b2db0 +232cb1041e2b2db0242cb1051e2b2db0252cb1061e2b2db0262cb1071e2b2db0272cb1081e2b +2db0282cb1091e2b2db0292c203cb001602db02a2c2060b01060204323b0016043b0022561b0 +0160b0292a212db02b2cb02a2bb02a2a2db02c2c2020472020b00b4363b804006220b0005058 +b040605966b001636023613823208a555820472020b00b4363b804006220b0005058b0406059 +66b00163602361381b21592db02d2c00b10002455458b00116b02c2ab00115301b22592db02e +2c00b00d2bb10002455458b00116b02c2ab00115301b22592db02f2c2035b001602db0302c00 +b0014563b804006220b0005058b040605966b00163b0012bb00b4363b804006220b0005058b0 +40605966b00163b0012bb00016b40000000000443e2338b12f01152a2db0312c203c204720b0 +0b4363b804006220b0005058b040605966b0016360b0004361382db0322c2e173c2db0332c20 +3c204720b00b4363b804006220b0005058b040605966b0016360b0004361b0014363382db034 +2cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b0012342b23301 +0115142a2db0352cb00016b00425b004254723472361b009432b658a2e2320203c8a382db036 +2cb00016b00425b00425202e472347236120b0042342b009432b20b060505820b0405158b302 +2003201bb30226031a5942422320b00843208a234723472361234660b00443b0026220b00050 +58b040605966b001636020b0012b208a8a6120b00243606423b0034361645058b00243611bb0 +03436059b00325b0026220b0005058b040605966b0016361232020b00426234661381b23b008 +4346b00225b0084347234723616020b00443b0026220b0005058b040605966b00163602320b0 +012b23b0044360b0012bb0052561b00525b0026220b0005058b040605966b00163b004266120 +b00425606423b0032560645058211b232159232020b0042623466138592db0372cb000162020 +20b00526202e4723472361233c382db0382cb0001620b0082342202020462347b0012b236138 +2db0392cb00016b00325b002254723472361b00054582e203c23211bb00225b0022547234723 +6120b00525b004254723472361b00625b0052549b0022561b9080008006363232058621b2159 +63b804006220b0005058b040605966b0016360232e2320203c8a382321592db03a2cb0001620 +b00843202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a38 +2db03b2c23202e46b00225465258203c592eb12b01142b2db03c2c23202e46b0022546505820 +3c592eb12b01142b2db03d2c23202e46b00225465258203c5923202e46b00225465058203c59 +2eb12b01142b2db03e2cb0352b23202e46b00225465258203c592eb12b01142b2db03f2cb036 +2b8a20203cb00423428a3823202e46b00225465258203c592eb12b01142bb004432eb02b2b2d +b0402cb00016b00425b00426202e4723472361b009432b23203c202e2338b12b01142b2db041 +2cb108042542b00016b00425b00425202e472347236120b0042342b009432b20b060505820b0 +405158b3022003201bb30226031a594242232047b00443b0026220b0005058b040605966b001 +636020b0012b208a8a6120b00243606423b0034361645058b00243611bb003436059b00325b0 +026220b0005058b040605966b0016361b0022546613823203c23381b212020462347b0012b23 +61382159b12b01142b2db0422cb0352b2eb12b01142b2db0432cb0362b212320203cb0042342 +2338b12b01142bb004432eb02b2b2db0442cb000152047b0002342b20001011514132eb0312a +2db0452cb000152047b0002342b20001011514132eb0312a2db0462cb100011413b0322a2db0 +472cb0342a2db0482cb000164523202e20468a236138b12b01142b2db0492cb0082342b0482b +2db04a2cb20000412b2db04b2cb20001412b2db04c2cb20100412b2db04d2cb20101412b2db0 +4e2cb20000422b2db04f2cb20001422b2db0502cb20100422b2db0512cb20101422b2db0522c +b200003e2b2db0532cb200013e2b2db0542cb201003e2b2db0552cb201013e2b2db0562cb200 +00402b2db0572cb20001402b2db0582cb20100402b2db0592cb20101402b2db05a2cb2000043 +2b2db05b2cb20001432b2db05c2cb20100432b2db05d2cb20101432b2db05e2cb200003f2b2d +b05f2cb200013f2b2db0602cb201003f2b2db0612cb201013f2b2db0622cb0372b2eb12b0114 +2b2db0632cb0372bb03b2b2db0642cb0372bb03c2b2db0652cb00016b0372bb03d2b2db0662c +b0382b2eb12b01142b2db0672cb0382bb03b2b2db0682cb0382bb03c2b2db0692cb0382bb03d +2b2db06a2cb0392b2eb12b01142b2db06b2cb0392bb03b2b2db06c2cb0392bb03c2b2db06d2c +b0392bb03d2b2db06e2cb03a2b2eb12b01142b2db06f2cb03a2bb03b2b2db0702cb03a2bb03c +2b2db0712cb03a2bb03d2b2db0722cb3090402034558211b232159422bb00865b003245078b0 +0115302d0001000000013375b8d025c45f0f3cf5000b03e800000000cfe607f000000000d532 +1021fe60fec607e403780000000800020001000000000001000003a2fea60000074afe60f903 +07e4000100000000000000000000000000000007025800780232005101ed001b024300550215 +001e020c005501e10055000000000000005c000001100000017c000001f80000027c0000030c +0000039c00010000000700ab000c000000000002003a004a0073000000cb0b7000000000004b +b800325258b101018e59b001b9080008006370b1000542b32d1902002ab1000542b520080e07 +02082ab1000542b52a06170502082ab1000742bb084003c0000200092ab1000942bb00400040 +000200092ab1030044b12401885158b0408858b1036444b12601885158ba0880000104408863 +5458b103004459595959b522081007020c2ab801ff85b0048db102004400000000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0 g +BT +210.190699 0 0 210.190699 119.472656 152.727074 Tm +/f-0-0 1 Tf +[(SY)-3(NA)-4(PS)-4(E)]TJ +ET +759.477 655.707 m 759.477 680.633 744.816 701.156 722.824 709.953 c 722.824 + 711.418 722.824 712.887 722.824 714.352 c 722.824 761.266 684.707 799.383 + 637.793 799.383 c 633.395 799.383 630.461 799.383 626.066 797.918 c 612.871 + 816.977 590.879 828.707 567.422 828.707 c 551.293 828.707 536.633 824.309 + 524.906 815.512 c 511.711 824.309 495.586 828.707 477.992 828.707 c 450.137 + 828.707 426.68 815.512 410.551 796.453 c 409.086 796.453 407.621 796.453 + 406.152 796.453 c 360.707 796.453 324.055 762.73 318.188 718.75 c 280.07 + 708.488 252.215 674.766 252.215 632.25 c 252.215 583.871 291.801 544.285 + 340.18 544.285 c 354.84 544.285 368.035 547.219 381.23 554.551 c 403.223 + 531.094 434.008 517.898 469.195 517.898 c 476.523 517.898 483.855 517.898 + 491.188 519.363 c 501.449 482.711 535.168 454.855 576.219 454.855 c 604.074 + 454.855 628.996 468.051 645.125 487.109 c 652.453 479.781 667.113 462.188 + 661.25 431.398 c 661.25 431.398 690.57 459.254 677.379 500.305 c 719.895 + 506.168 753.613 542.82 753.613 588.27 c 753.613 598.531 752.148 610.262 + 747.75 619.059 c 755.078 629.32 759.477 642.516 759.477 655.707 c h +733.09 614.66 m 736.02 605.863 737.484 598.531 737.484 589.734 c 737.484 + 550.152 705.234 516.434 664.184 516.434 c 661.25 516.434 658.316 516.434 + 655.387 516.434 c 645.125 517.898 l 639.258 509.102 l 626.066 485.645 602.609 + 470.984 576.219 470.984 c 543.965 470.984 514.645 492.973 505.848 523.762 + c 501.449 536.957 l 486.789 534.023 l 480.922 532.559 473.594 532.559 467.73 + 532.559 c 438.406 532.559 412.02 544.285 391.492 564.812 c 384.164 573.609 + l 373.898 569.211 l 363.637 563.348 351.91 561.879 340.18 561.879 c 300.598 + 561.879 266.875 594.133 266.875 635.184 c 266.875 668.902 288.867 698.227 + 321.121 705.555 c 331.383 708.488 l 332.852 718.75 l 337.25 755.402 368.035 + 781.789 404.688 781.789 c 406.152 781.789 407.621 781.789 409.086 781.789 + c 416.418 781.789 l 420.812 787.656 l 434.008 805.25 454.535 814.043 476.523 + 814.043 c 489.719 814.043 502.914 809.645 514.645 802.316 c 523.441 796.453 + l 535.168 803.781 l 545.43 811.113 555.691 814.043 568.887 814.043 c 587.945 + 814.043 604.074 805.25 615.801 790.586 c 621.668 783.258 l 630.461 784.723 + l 633.395 784.723 636.328 784.723 639.258 784.723 c 677.379 784.723 708.164 + 753.934 708.164 715.816 c 708.164 714.352 708.164 712.887 708.164 711.418 + c 708.164 699.691 l 718.426 695.293 l 734.555 687.961 744.816 673.301 744.816 + 655.707 c 744.816 646.914 741.883 636.648 736.02 629.32 c 730.156 621.988 + l h +733.09 614.66 m f +645.125 743.672 m 645.125 729.91 633.965 718.75 620.199 718.75 c 606.438 + 718.75 595.277 729.91 595.277 743.672 c 595.277 757.438 606.438 768.598 + 620.199 768.598 c 633.965 768.598 645.125 757.438 645.125 743.672 c h +645.125 743.672 m f +583.547 783.258 m 583.547 776.781 578.297 771.527 571.82 771.527 c 565.344 + 771.527 560.09 776.781 560.09 783.258 c 560.09 789.734 565.344 794.984 +571.82 794.984 c 578.297 794.984 583.547 789.734 583.547 783.258 c h +583.547 783.258 m f +576.219 731.945 m 576.219 729.516 574.25 727.547 571.82 727.547 c 569.391 + 727.547 567.422 729.516 567.422 731.945 c 567.422 734.375 569.391 736.344 + 571.82 736.344 c 574.25 736.344 576.219 734.375 576.219 731.945 c h +576.219 731.945 m f +491.188 748.07 m 491.188 745.641 489.215 743.672 486.789 743.672 c 484.359 + 743.672 482.391 745.641 482.391 748.07 c 482.391 750.5 484.359 752.469 +486.789 752.469 c 489.215 752.469 491.188 750.5 491.188 748.07 c h +491.188 748.07 m f +708.164 632.25 m 708.164 655.707 689.105 673.301 667.113 673.301 c 655.387 + 673.301 643.656 667.438 636.328 660.105 c 628.996 665.973 620.199 668.902 + 611.402 668.902 c 609.938 668.902 609.938 668.902 608.473 668.902 c 596.742 + 704.09 561.559 708.488 543.965 704.09 c 538.102 702.621 532.234 699.691 + 527.836 696.758 c 520.508 704.09 510.246 709.953 498.516 709.953 c 494.117 + 709.953 491.188 709.953 486.789 708.488 c 480.922 723.148 466.262 733.41 + 448.668 733.41 c 435.477 733.41 423.746 727.547 414.949 717.285 c 407.621 + 723.148 398.824 726.082 390.027 726.082 c 368.035 726.082 350.441 709.953 + 348.977 689.43 c 346.043 689.43 344.578 689.43 341.645 689.43 c 310.859 + 689.43 285.938 664.504 285.938 633.719 c 285.938 602.93 310.859 578.008 + 341.645 578.008 c 360.707 578.008 376.832 586.805 387.094 601.465 c 400.289 + 570.676 432.543 550.152 469.195 550.152 c 486.789 550.152 502.914 554.551 + 517.574 563.348 c 517.574 560.414 516.109 556.016 516.109 553.082 c 516.109 + 519.363 543.965 492.973 576.219 492.973 c 607.004 492.973 633.395 516.434 + 636.328 547.219 c 645.125 539.891 655.387 535.492 668.582 535.492 c 696.438 + 535.492 719.895 557.48 719.895 586.805 c 719.895 599.996 714.027 611.727 + 706.699 621.988 c 706.699 621.988 708.164 626.387 708.164 632.25 c h +343.113 611.727 m 335.781 611.727 329.918 617.59 329.918 624.922 c 329.918 + 632.25 335.781 638.117 343.113 638.117 c 350.441 638.117 356.309 632.25 + 356.309 624.922 c 354.84 617.59 350.441 611.727 343.113 611.727 c h +442.805 655.707 m 432.543 655.707 423.746 664.504 423.746 674.766 c 423.746 + 685.031 432.543 693.828 442.805 693.828 c 453.066 693.828 461.863 685.031 + 461.863 674.766 c 461.863 664.504 453.066 655.707 442.805 655.707 c h +511.711 611.727 m 507.312 611.727 502.914 616.125 502.914 620.523 c 502.914 + 624.922 507.312 629.32 511.711 629.32 c 516.109 629.32 520.508 624.922 +520.508 620.523 c 520.508 616.125 516.109 611.727 511.711 611.727 c h +551.293 589.734 m 549.828 589.734 548.363 591.203 548.363 592.668 c 548.363 + 594.133 549.828 595.602 551.293 595.602 c 552.762 595.602 554.227 594.133 + 554.227 592.668 c 554.227 591.203 552.762 589.734 551.293 589.734 c h +558.625 626.387 m 552.762 626.387 546.898 632.25 546.898 638.117 c 546.898 + 643.98 552.762 649.844 558.625 649.844 c 564.488 649.844 570.355 643.98 + 570.355 638.117 c 570.355 632.25 565.957 626.387 558.625 626.387 c h +626.066 578.008 m 620.199 578.008 617.27 582.406 617.27 586.805 c 617.27 + 591.203 621.668 595.602 626.066 595.602 c 630.461 595.602 634.859 591.203 + 634.859 586.805 c 634.859 582.406 631.93 578.008 626.066 578.008 c h +626.066 578.008 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logo_black.svg b/assets/images/logo_black.svg new file mode 100644 index 000000000..43bbd9741 --- /dev/null +++ b/assets/images/logo_black.svg @@ -0,0 +1,18 @@ + + + +Created with Fabric.js 2.3.6 + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/logo_white.eps b/assets/images/logo_white.eps new file mode 100644 index 000000000..fe2365d7b --- /dev/null +++ b/assets/images/logo_white.eps @@ -0,0 +1,349 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:37:09 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Rajdhani-Regular +11 dict begin +/FontType 42 def +/FontName /Rajdhani-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674201c5d0b3e00000438000000646670676d8c9290580000 +049c00000b70676c796631a0edc00000009c0000039c686561640a7b8eaa0000100c00000036 +6868656109e8fefb0000104400000024686d74780ebc0201000010680000001c6c6f63610000 +0e0400001084000000206d617870018c0c67000010a40000002070726570a2c36a2e000010c4 +0000009100020078000001e0024600030007002f402c00020401010002015e00000303005200 +0000035605010300034a0404000004070407060500030003110605152b1311211101112111aa +0104feca01680218fe1601eafde80246fdba000000010051000001e1028300350030402d0003 +04000403006d0000010400016b0004040258000202224800010105580005052305493b33343b +333206061a2b3735343b01321d01143b01323d01342e033d0134363b0132161d01142b01223d +01342b01221d0114171e021d0114062b012226510b1d0b616861486667484c4174414c0b1d0b +6167607b3367484c4176414c8e240b0b2164652a2c321618403637424c4c421c0b0b19646632 +45180a1a443a31424c4c0001001b000001d20283001500254022130b04030001014702010101 +2248030100002300490200110e0906001502150406142b2123223d0103263b01321713331336 +3b01320703151401051d0bc2060d240905a0059f0509250b04c30ce701840c0cfeb901470c0c +fe7ce70c00010055000001ef0283001b00274024180a02000101470201010122480304020000 +23004902001613100d0805001b021b0506142b3323223511343b013217013311343b01321511 +142b012227012311147c1d0a0a1b09030133040b1c0b0b170907fece040c026b0c06fdf0020a +0c0cfd950c0b020cfdf50c0000000002001e000001f7028300130017002f402c170104010147 +000400030004035f000101224802050200002300490200161511100e0b080500130213060614 +2b3323223713363b01321713162b01222f0121070613033303441f0b04c7030b2f0a04c7030a +200a0336fefa3604bb73eb740c026b0c0cfd950c0ca0a00c024cfe8d01730000000200550000 +01d2028300130021003340302001030401470003000200030260000404015800010122480501 +000023004902001e1b1614100d0805001302130606142b3323223511343b0132161d0114062b +01221d0114133332363d0134262b01221511147c1d0a0ae6414c4c41b40a0ab02e30302eb00a +0c026b0c4c4275424c08de0c0120332f71303309fedb080000010055000001a0028300230034 +40310003000405030460000202015800010122480005050058060100002300490200201d1a17 +14110e0b0805002302230706142b2901223511343321321d01142321221d01143b01321d0114 +2b01221d01143321321d01140194fecb0a0a01350c0cfefd0a0ae70b0be70a0a01030c0c026b +0c0b170b09e6090b170b09f2090b170b00000000000000000000000000000000000000000000 +00300030021d002b002b036a026f026f002bff0d03e8fe0c036a026f026f0000ff0d03e8fe0c +00320032002d002d0283000002bf01fe0000ff7403e8fe0c0283000002bf01fe0000ff6b03e8 +fe0cb0002c20b0005558455920204bb8000d514bb006535a58b0341bb028596066208a5558b0 +022561b908000800636323621b2121b00059b000432344b20001004360422db0012cb0206066 +2db0022c206420b0c050b004265ab228010a43456345525b582123211b8a5820b050505821b0 +40591b20b038505821b038595920b1010a434563456164b028505821b1010a4345634520b030 +505821b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036 +505821b036601b605959591bb0012b595923b00050586559592db0032c204520b00425616420 +b005435058b0052342b00623421b212159b001602db0042c232123212064b105624220b00623 +42b1010a434563b1010a43b002604563b0032a2120b00643208a208ab0012bb1300525b00426 +515860501b6152595823592120b0405358b0012b1b21b0405923b000505865592db0052cb007 +432bb20002004360422db0062cb00723422320b000234261b0026266b00163b00160b0052a2d +b0072c20204520b00b4363b804006220b0005058b040605966b001636044b001602db0082cb2 +070b004345422a21b20001004360422db0092cb000432344b20001004360422db00a2c202045 +20b0012b23b00043b004256020458a2361206420b020505821b0001bb0305058b0201bb04059 +5923b00050586559b0032523614444b001602db00b2c20204520b0012b23b00043b004256020 +458a23612064b0245058b0001bb0405923b00050586559b0032523614444b001602db00c2c20 +b0002342b20b0a034558211b2321592a212db00d2cb1020245b06461442db00e2cb001602020 +b00c434ab000505820b00c234259b00d434ab000525820b00d2342592db00f2c20b0106266b0 +016320b80400638a2361b00e4360208a6020b00e2342232db0102c4b5458b10464445924b00d +6523782db0112c4b51584b5358b1046444591b215924b0136523782db0122cb1000f435558b1 +0f0f43b0016142b00f2b59b00043b0022542b10c022542b10d022542b001162320b003255058 +b101004360b00425428a8a208a2361b00e2a2123b00161208a2361b00e2a211bb101004360b0 +022542b0022561b00e2a2159b00c4347b00d434760b0026220b0005058b040605966b0016320 +b00b4363b804006220b0005058b040605966b0016360b10000132344b00143b0003eb2010101 +4360422db0132c00b10002455458b00f23422045b00b2342b00a23b00260422060b00161b510 +1001000e0042428a60b112062bb0722b1b22592db0142cb100132b2db0152cb101132b2db016 +2cb102132b2db0172cb103132b2db0182cb104132b2db0192cb105132b2db01a2cb106132b2d +b01b2cb107132b2db01c2cb108132b2db01d2cb109132b2db01e2c00b00d2bb10002455458b0 +0f23422045b00b2342b00a23b00260422060b00161b5101001000e0042428a60b112062bb072 +2b1b22592db01f2cb1001e2b2db0202cb1011e2b2db0212cb1021e2b2db0222cb1031e2b2db0 +232cb1041e2b2db0242cb1051e2b2db0252cb1061e2b2db0262cb1071e2b2db0272cb1081e2b +2db0282cb1091e2b2db0292c203cb001602db02a2c2060b01060204323b0016043b0022561b0 +0160b0292a212db02b2cb02a2bb02a2a2db02c2c2020472020b00b4363b804006220b0005058 +b040605966b001636023613823208a555820472020b00b4363b804006220b0005058b0406059 +66b00163602361381b21592db02d2c00b10002455458b00116b02c2ab00115301b22592db02e +2c00b00d2bb10002455458b00116b02c2ab00115301b22592db02f2c2035b001602db0302c00 +b0014563b804006220b0005058b040605966b00163b0012bb00b4363b804006220b0005058b0 +40605966b00163b0012bb00016b40000000000443e2338b12f01152a2db0312c203c204720b0 +0b4363b804006220b0005058b040605966b0016360b0004361382db0322c2e173c2db0332c20 +3c204720b00b4363b804006220b0005058b040605966b0016360b0004361b0014363382db034 +2cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b0012342b23301 +0115142a2db0352cb00016b00425b004254723472361b009432b658a2e2320203c8a382db036 +2cb00016b00425b00425202e472347236120b0042342b009432b20b060505820b0405158b302 +2003201bb30226031a5942422320b00843208a234723472361234660b00443b0026220b00050 +58b040605966b001636020b0012b208a8a6120b00243606423b0034361645058b00243611bb0 +03436059b00325b0026220b0005058b040605966b0016361232020b00426234661381b23b008 +4346b00225b0084347234723616020b00443b0026220b0005058b040605966b00163602320b0 +012b23b0044360b0012bb0052561b00525b0026220b0005058b040605966b00163b004266120 +b00425606423b0032560645058211b232159232020b0042623466138592db0372cb000162020 +20b00526202e4723472361233c382db0382cb0001620b0082342202020462347b0012b236138 +2db0392cb00016b00325b002254723472361b00054582e203c23211bb00225b0022547234723 +6120b00525b004254723472361b00625b0052549b0022561b9080008006363232058621b2159 +63b804006220b0005058b040605966b0016360232e2320203c8a382321592db03a2cb0001620 +b00843202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a38 +2db03b2c23202e46b00225465258203c592eb12b01142b2db03c2c23202e46b0022546505820 +3c592eb12b01142b2db03d2c23202e46b00225465258203c5923202e46b00225465058203c59 +2eb12b01142b2db03e2cb0352b23202e46b00225465258203c592eb12b01142b2db03f2cb036 +2b8a20203cb00423428a3823202e46b00225465258203c592eb12b01142bb004432eb02b2b2d +b0402cb00016b00425b00426202e4723472361b009432b23203c202e2338b12b01142b2db041 +2cb108042542b00016b00425b00425202e472347236120b0042342b009432b20b060505820b0 +405158b3022003201bb30226031a594242232047b00443b0026220b0005058b040605966b001 +636020b0012b208a8a6120b00243606423b0034361645058b00243611bb003436059b00325b0 +026220b0005058b040605966b0016361b0022546613823203c23381b212020462347b0012b23 +61382159b12b01142b2db0422cb0352b2eb12b01142b2db0432cb0362b212320203cb0042342 +2338b12b01142bb004432eb02b2b2db0442cb000152047b0002342b20001011514132eb0312a +2db0452cb000152047b0002342b20001011514132eb0312a2db0462cb100011413b0322a2db0 +472cb0342a2db0482cb000164523202e20468a236138b12b01142b2db0492cb0082342b0482b +2db04a2cb20000412b2db04b2cb20001412b2db04c2cb20100412b2db04d2cb20101412b2db0 +4e2cb20000422b2db04f2cb20001422b2db0502cb20100422b2db0512cb20101422b2db0522c +b200003e2b2db0532cb200013e2b2db0542cb201003e2b2db0552cb201013e2b2db0562cb200 +00402b2db0572cb20001402b2db0582cb20100402b2db0592cb20101402b2db05a2cb2000043 +2b2db05b2cb20001432b2db05c2cb20100432b2db05d2cb20101432b2db05e2cb200003f2b2d +b05f2cb200013f2b2db0602cb201003f2b2db0612cb201013f2b2db0622cb0372b2eb12b0114 +2b2db0632cb0372bb03b2b2db0642cb0372bb03c2b2db0652cb00016b0372bb03d2b2db0662c +b0382b2eb12b01142b2db0672cb0382bb03b2b2db0682cb0382bb03c2b2db0692cb0382bb03d +2b2db06a2cb0392b2eb12b01142b2db06b2cb0392bb03b2b2db06c2cb0392bb03c2b2db06d2c +b0392bb03d2b2db06e2cb03a2b2eb12b01142b2db06f2cb03a2bb03b2b2db0702cb03a2bb03c +2b2db0712cb03a2bb03d2b2db0722cb3090402034558211b232159422bb00865b003245078b0 +0115302d0001000000013375b8d025c45f0f3cf5000b03e800000000cfe607f000000000d532 +1021fe60fec607e403780000000800020001000000000001000003a2fea60000074afe60f903 +07e4000100000000000000000000000000000007025800780232005101ed001b024300550215 +001e020c005501e10055000000000000005c000001100000017c000001f80000027c0000030c +0000039c00010000000700ab000c000000000002003a004a0073000000cb0b7000000000004b +b800325258b101018e59b001b9080008006370b1000542b32d1902002ab1000542b520080e07 +02082ab1000542b52a06170502082ab1000742bb084003c0000200092ab1000942bb00400040 +000200092ab1030044b12401885158b0408858b1036444b12601885158ba0880000104408863 +5458b103004459595959b522081007020c2ab801ff85b0048db102004400000000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +0 g +0 0 1024 1024 rectfill +1 g +BT +210.190699 0 0 210.190699 119.472656 152.727074 Tm +/f-0-0 1 Tf +[(SY)-3(NA)-4(PS)-4(E)]TJ +ET +759.477 655.707 m 759.477 680.633 744.816 701.156 722.824 709.953 c 722.824 + 711.418 722.824 712.887 722.824 714.352 c 722.824 761.266 684.707 799.383 + 637.793 799.383 c 633.395 799.383 630.461 799.383 626.066 797.918 c 612.871 + 816.977 590.879 828.707 567.422 828.707 c 551.293 828.707 536.633 824.309 + 524.906 815.512 c 511.711 824.309 495.586 828.707 477.992 828.707 c 450.137 + 828.707 426.68 815.512 410.551 796.453 c 409.086 796.453 407.621 796.453 + 406.152 796.453 c 360.707 796.453 324.055 762.73 318.188 718.75 c 280.07 + 708.488 252.215 674.766 252.215 632.25 c 252.215 583.871 291.801 544.285 + 340.18 544.285 c 354.84 544.285 368.035 547.219 381.23 554.551 c 403.223 + 531.094 434.008 517.898 469.195 517.898 c 476.523 517.898 483.855 517.898 + 491.188 519.363 c 501.449 482.711 535.168 454.855 576.219 454.855 c 604.074 + 454.855 628.996 468.051 645.125 487.109 c 652.453 479.781 667.113 462.188 + 661.25 431.398 c 661.25 431.398 690.57 459.254 677.379 500.305 c 719.895 + 506.168 753.613 542.82 753.613 588.27 c 753.613 598.531 752.148 610.262 + 747.75 619.059 c 755.078 629.32 759.477 642.516 759.477 655.707 c h +733.09 614.66 m 736.02 605.863 737.484 598.531 737.484 589.734 c 737.484 + 550.152 705.234 516.434 664.184 516.434 c 661.25 516.434 658.316 516.434 + 655.387 516.434 c 645.125 517.898 l 639.258 509.102 l 626.066 485.645 602.609 + 470.984 576.219 470.984 c 543.965 470.984 514.645 492.973 505.848 523.762 + c 501.449 536.957 l 486.789 534.023 l 480.922 532.559 473.594 532.559 467.73 + 532.559 c 438.406 532.559 412.02 544.285 391.492 564.812 c 384.164 573.609 + l 373.898 569.211 l 363.637 563.348 351.91 561.879 340.18 561.879 c 300.598 + 561.879 266.875 594.133 266.875 635.184 c 266.875 668.902 288.867 698.227 + 321.121 705.555 c 331.383 708.488 l 332.852 718.75 l 337.25 755.402 368.035 + 781.789 404.688 781.789 c 406.152 781.789 407.621 781.789 409.086 781.789 + c 416.418 781.789 l 420.812 787.656 l 434.008 805.25 454.535 814.043 476.523 + 814.043 c 489.719 814.043 502.914 809.645 514.645 802.316 c 523.441 796.453 + l 535.168 803.781 l 545.43 811.113 555.691 814.043 568.887 814.043 c 587.945 + 814.043 604.074 805.25 615.801 790.586 c 621.668 783.258 l 630.461 784.723 + l 633.395 784.723 636.328 784.723 639.258 784.723 c 677.379 784.723 708.164 + 753.934 708.164 715.816 c 708.164 714.352 708.164 712.887 708.164 711.418 + c 708.164 699.691 l 718.426 695.293 l 734.555 687.961 744.816 673.301 744.816 + 655.707 c 744.816 646.914 741.883 636.648 736.02 629.32 c 730.156 621.988 + l h +733.09 614.66 m f +645.125 743.672 m 645.125 729.91 633.965 718.75 620.199 718.75 c 606.438 + 718.75 595.277 729.91 595.277 743.672 c 595.277 757.438 606.438 768.598 + 620.199 768.598 c 633.965 768.598 645.125 757.438 645.125 743.672 c h +645.125 743.672 m f +583.547 783.258 m 583.547 776.781 578.297 771.527 571.82 771.527 c 565.344 + 771.527 560.09 776.781 560.09 783.258 c 560.09 789.734 565.344 794.984 +571.82 794.984 c 578.297 794.984 583.547 789.734 583.547 783.258 c h +583.547 783.258 m f +576.219 731.945 m 576.219 729.516 574.25 727.547 571.82 727.547 c 569.391 + 727.547 567.422 729.516 567.422 731.945 c 567.422 734.375 569.391 736.344 + 571.82 736.344 c 574.25 736.344 576.219 734.375 576.219 731.945 c h +576.219 731.945 m f +491.188 748.07 m 491.188 745.641 489.215 743.672 486.789 743.672 c 484.359 + 743.672 482.391 745.641 482.391 748.07 c 482.391 750.5 484.359 752.469 +486.789 752.469 c 489.215 752.469 491.188 750.5 491.188 748.07 c h +491.188 748.07 m f +708.164 632.25 m 708.164 655.707 689.105 673.301 667.113 673.301 c 655.387 + 673.301 643.656 667.438 636.328 660.105 c 628.996 665.973 620.199 668.902 + 611.402 668.902 c 609.938 668.902 609.938 668.902 608.473 668.902 c 596.742 + 704.09 561.559 708.488 543.965 704.09 c 538.102 702.621 532.234 699.691 + 527.836 696.758 c 520.508 704.09 510.246 709.953 498.516 709.953 c 494.117 + 709.953 491.188 709.953 486.789 708.488 c 480.922 723.148 466.262 733.41 + 448.668 733.41 c 435.477 733.41 423.746 727.547 414.949 717.285 c 407.621 + 723.148 398.824 726.082 390.027 726.082 c 368.035 726.082 350.441 709.953 + 348.977 689.43 c 346.043 689.43 344.578 689.43 341.645 689.43 c 310.859 + 689.43 285.938 664.504 285.938 633.719 c 285.938 602.93 310.859 578.008 + 341.645 578.008 c 360.707 578.008 376.832 586.805 387.094 601.465 c 400.289 + 570.676 432.543 550.152 469.195 550.152 c 486.789 550.152 502.914 554.551 + 517.574 563.348 c 517.574 560.414 516.109 556.016 516.109 553.082 c 516.109 + 519.363 543.965 492.973 576.219 492.973 c 607.004 492.973 633.395 516.434 + 636.328 547.219 c 645.125 539.891 655.387 535.492 668.582 535.492 c 696.438 + 535.492 719.895 557.48 719.895 586.805 c 719.895 599.996 714.027 611.727 + 706.699 621.988 c 706.699 621.988 708.164 626.387 708.164 632.25 c h +343.113 611.727 m 335.781 611.727 329.918 617.59 329.918 624.922 c 329.918 + 632.25 335.781 638.117 343.113 638.117 c 350.441 638.117 356.309 632.25 + 356.309 624.922 c 354.84 617.59 350.441 611.727 343.113 611.727 c h +442.805 655.707 m 432.543 655.707 423.746 664.504 423.746 674.766 c 423.746 + 685.031 432.543 693.828 442.805 693.828 c 453.066 693.828 461.863 685.031 + 461.863 674.766 c 461.863 664.504 453.066 655.707 442.805 655.707 c h +511.711 611.727 m 507.312 611.727 502.914 616.125 502.914 620.523 c 502.914 + 624.922 507.312 629.32 511.711 629.32 c 516.109 629.32 520.508 624.922 +520.508 620.523 c 520.508 616.125 516.109 611.727 511.711 611.727 c h +551.293 589.734 m 549.828 589.734 548.363 591.203 548.363 592.668 c 548.363 + 594.133 549.828 595.602 551.293 595.602 c 552.762 595.602 554.227 594.133 + 554.227 592.668 c 554.227 591.203 552.762 589.734 551.293 589.734 c h +558.625 626.387 m 552.762 626.387 546.898 632.25 546.898 638.117 c 546.898 + 643.98 552.762 649.844 558.625 649.844 c 564.488 649.844 570.355 643.98 + 570.355 638.117 c 570.355 632.25 565.957 626.387 558.625 626.387 c h +626.066 578.008 m 620.199 578.008 617.27 582.406 617.27 586.805 c 617.27 + 591.203 621.668 595.602 626.066 595.602 c 630.461 595.602 634.859 591.203 + 634.859 586.805 c 634.859 582.406 631.93 578.008 626.066 578.008 c h +626.066 578.008 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logo_white.svg b/assets/images/logo_white.svg new file mode 100644 index 000000000..8e160cce2 --- /dev/null +++ b/assets/images/logo_white.svg @@ -0,0 +1,18 @@ + + + +Created with Fabric.js 2.3.6 + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/logomark.eps b/assets/images/logomark.eps new file mode 100644 index 000000000..eb93f177c --- /dev/null +++ b/assets/images/logomark.eps @@ -0,0 +1,198 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:14:04 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0.54902 0.407843 0.803922 rg +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logomark.svg b/assets/images/logomark.svg new file mode 100644 index 000000000..b9cfb2256 --- /dev/null +++ b/assets/images/logomark.svg @@ -0,0 +1,15 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/logomark_background.eps b/assets/images/logomark_background.eps new file mode 100644 index 000000000..38434bbbd --- /dev/null +++ b/assets/images/logomark_background.eps @@ -0,0 +1,196 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:14:05 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 155 231 869 791 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 155 231 869 791 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 155 231 714 560 rectclip q +0.54902 0.407843 0.803922 rg +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logomark_background.svg b/assets/images/logomark_background.svg new file mode 100644 index 000000000..9be5b1f16 --- /dev/null +++ b/assets/images/logomark_background.svg @@ -0,0 +1,16 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/logomark_black.eps b/assets/images/logomark_black.eps new file mode 100644 index 000000000..b853174a6 --- /dev/null +++ b/assets/images/logomark_black.eps @@ -0,0 +1,198 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:14:06 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0 g +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logomark_black.svg b/assets/images/logomark_black.svg new file mode 100644 index 000000000..95018e132 --- /dev/null +++ b/assets/images/logomark_black.svg @@ -0,0 +1,15 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/logomark_white.eps b/assets/images/logomark_white.eps new file mode 100644 index 000000000..58d0daec0 --- /dev/null +++ b/assets/images/logomark_white.eps @@ -0,0 +1,198 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Fri Aug 30 13:14:06 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +0 g +0 0 1024 1024 rectfill +1 g +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/assets/images/logomark_white.svg b/assets/images/logomark_white.svg new file mode 100644 index 000000000..314461f52 --- /dev/null +++ b/assets/images/logomark_white.svg @@ -0,0 +1,15 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/synapse-color.png b/assets/images/synapse-color.png deleted file mode 100644 index 037fd3236..000000000 Binary files a/assets/images/synapse-color.png and /dev/null differ diff --git a/assets/images/synapse-headless.png b/assets/images/synapse-headless.png deleted file mode 100644 index 80bd2fa96..000000000 Binary files a/assets/images/synapse-headless.png and /dev/null differ diff --git a/assets/images/synapse-icon.ico b/assets/images/synapse-icon.ico deleted file mode 100644 index 836a0e277..000000000 Binary files a/assets/images/synapse-icon.ico and /dev/null differ diff --git a/assets/images/synapse_demo.gif b/assets/images/synapse_demo.gif deleted file mode 100644 index c8a278e68..000000000 Binary files a/assets/images/synapse_demo.gif and /dev/null differ diff --git a/assets/images/transparent_logo.png b/assets/images/transparent_logo.png new file mode 100644 index 000000000..2b9232394 Binary files /dev/null and b/assets/images/transparent_logo.png differ diff --git a/assets/images/transparent_logo_black.png b/assets/images/transparent_logo_black.png new file mode 100644 index 000000000..a22a004dc Binary files /dev/null and b/assets/images/transparent_logo_black.png differ diff --git a/assets/images/transparent_logo_white.png b/assets/images/transparent_logo_white.png new file mode 100644 index 000000000..5c07ba309 Binary files /dev/null and b/assets/images/transparent_logo_white.png differ diff --git a/assets/images/transparent_logomark.ico b/assets/images/transparent_logomark.ico new file mode 100644 index 000000000..002673e7a Binary files /dev/null and b/assets/images/transparent_logomark.ico differ diff --git a/assets/images/transparent_logomark.png b/assets/images/transparent_logomark.png new file mode 100644 index 000000000..b526f8e61 Binary files /dev/null and b/assets/images/transparent_logomark.png differ diff --git a/assets/images/transparent_logomark_black.png b/assets/images/transparent_logomark_black.png new file mode 100644 index 000000000..14de36832 Binary files /dev/null and b/assets/images/transparent_logomark_black.png differ diff --git a/assets/images/transparent_logomark_white.png b/assets/images/transparent_logomark_white.png new file mode 100644 index 000000000..ff56e4f8d Binary files /dev/null and b/assets/images/transparent_logomark_white.png differ diff --git a/code-of-conduct.md b/code-of-conduct.md deleted file mode 100644 index 97a8526a6..000000000 --- a/code-of-conduct.md +++ /dev/null @@ -1,11 +0,0 @@ -# Code of Conduct - -We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). - - -Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io) -in order to report violations of the Code of Conduct. diff --git a/community/README.md b/community/README.md new file mode 100644 index 000000000..662e08da9 --- /dev/null +++ b/community/README.md @@ -0,0 +1,15 @@ +# Community + +This document acknowledges the contributions of community members to the Synapse project. The Synapse community thrives on collaboration and the valuable input of its contributors. + +## Contributors + +The list below recognizes individuals and organizations who have made significant contributions to the Synapse project. + +We encourage everyone to join the Synapse community and contribute to the project. For more information on how to get involved, please refer to the ['how to contribute'](../CONTRIBUTING.md) guide. + +We strive to include all contributors in this list. If your name has been missed, feel free to add it through a pull request to this document, or notify us through our communication channels or during a team meeting. + +### [**Neuroglia**](https://neuroglia.io/) + * [Charles d'Avernas](https://github.com/cdavernas) + * [Jean-Baptiste Bianchi](https://github.com/JBBianchi) \ No newline at end of file diff --git a/deployment/docker-compose/docker-compose.yml b/deployment/docker-compose/docker-compose.yml deleted file mode 100644 index fa5c483a2..000000000 --- a/deployment/docker-compose/docker-compose.yml +++ /dev/null @@ -1,61 +0,0 @@ -version: '3.4' - -services: - - mongo: - image: mongo:latest - ports: - - 27017:27017 - volumes: - - mongo_data:/data/db - - eventstore: - image: eventstore/eventstore - environment: - - EVENTSTORE_CLUSTER_SIZE=1 - - EVENTSTORE_RUN_PROJECTIONS=All - - EVENTSTORE_START_STANDARD_PROJECTIONS=true - - EVENTSTORE_EXT_TCP_PORT=1113 - - EVENTSTORE_HTTP_PORT=2113 - - EVENTSTORE_INSECURE=true - - EVENTSTORE_ENABLE_EXTERNAL_TCP=true - - EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true - ports: - - 6113:1113 - - 7113:2113 - volumes: - - eventstore_data:/var/lib/eventstore - - synapse: - image: ghcr.io/serverlessworkflow/synapse:latest - environment: - - ASPNETCORE_ENVIRONMENT=Development - - SYNAPSE_API_HOSTNAME=synapse - - SYNAPSE_PERSISTENCE_WRITEMODEL_DEFAULT_REPOSITORY=EventStore - - SYNAPSE_PERSISTENCE_READMODEL_DEFAULT_REPOSITORY=MongoDB - - CONNECTIONSTRINGS__EVENTSTORE=esdb://eventstore:2113?tls=false - - CONNECTIONSTRINGS__MONGODB=mongodb://mongo:27017 - ports: - - 42286:42286 - - 41387:41387 - extra_hosts: - - host.docker.internal:host-gateway - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./plugins:/app/plugins - depends_on: - - mongo - - eventstore - -volumes: - - mongo_data: - driver: local - - eventstore_data: - driver: local - -networks: - - default: - name: synapse \ No newline at end of file diff --git a/deployment/docker-compose/eventstore+mongo.yml b/deployment/docker-compose/eventstore+mongo.yml deleted file mode 100644 index 21e5451bc..000000000 --- a/deployment/docker-compose/eventstore+mongo.yml +++ /dev/null @@ -1,59 +0,0 @@ -version: '3.4' - -services: - - mongo: - image: mongo:latest - ports: - - 27017:27017 - volumes: - - mongo_data:/data/db - - eventstore: - image: eventstore/eventstore - environment: - - EVENTSTORE_CLUSTER_SIZE=1 - - EVENTSTORE_RUN_PROJECTIONS=All - - EVENTSTORE_START_STANDARD_PROJECTIONS=true - - EVENTSTORE_EXT_TCP_PORT=1113 - - EVENTSTORE_HTTP_PORT=2113 - - EVENTSTORE_INSECURE=true - - EVENTSTORE_ENABLE_EXTERNAL_TCP=true - - EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=true - ports: - - 6113:1113 - - 7113:2113 - volumes: - - eventstore_data:/var/lib/eventstore - - synapse: - image: ghcr.io/serverlessworkflow/synapse:latest - environment: - - ASPNETCORE_ENVIRONMENT=Development - - SYNAPSE_API_HOSTNAME=synapse - - SYNAPSE_PERSISTENCE_WRITEMODEL_DEFAULT_REPOSITORY=EventStore - - SYNAPSE_PERSISTENCE_READMODEL_DEFAULT_REPOSITORY=MongoDB - ports: - - 42286:42286 - - 41387:41387 - extra_hosts: - - host.docker.internal:host-gateway - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./plugins:/app/plugins - depends_on: - - mongo - - eventstore - -volumes: - - mongo_data: - driver: local - - eventstore_data: - driver: local - -networks: - - default: - name: synapse \ No newline at end of file diff --git a/deployment/kubernetes/eventstore+mongo.yaml b/deployment/kubernetes/eventstore+mongo.yaml deleted file mode 100644 index 46f1fb840..000000000 --- a/deployment/kubernetes/eventstore+mongo.yaml +++ /dev/null @@ -1,191 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: synapse - ---- - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: synapse - namespace: synapse - ---- - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: mongo - namespace: synapse -spec: - selector: - matchLabels: - app: mongo - template: - metadata: - labels: - app: mongo - spec: - containers: - - name: mongo - image: mongo:latest - ---- - -apiVersion: v1 -kind: Service -metadata: - name: mongo - namespace: synapse - labels: - app: mongo -spec: - ports: - - name: tcp - port: 27017 - protocol: TCP - selector: - app: mongo - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: event-store - namespace: synapse -spec: - selector: - matchLabels: - app: event-store - template: - metadata: - labels: - app: event-store - spec: - containers: - - name: event-store - image: eventstore/eventstore:latest - env: - - name: EVENTSTORE_CLUSTER_SIZE - value: '1' - - name: EVENTSTORE_RUN_PROJECTIONS - value: All - - name: EVENTSTORE_START_STANDARD_PROJECTIONS - value: 'true' - - name: EVENTSTORE_EXT_TCP_PORT - value: '1113' - - name: EVENTSTORE_HTTP_PORT - value: '2113' - - name: EVENTSTORE_INSECURE - value: 'true' - - name: EVENTSTORE_ENABLE_EXTERNAL_TCP - value: 'true' - - name: EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP - value: 'true' ---- - -apiVersion: v1 -kind: Service -metadata: - name: event-store - namespace: synapse - labels: - app: event-store -spec: - ports: - - name: tcp - port: 1113 - protocol: TCP - - name: http - port: 2113 - protocol: TCP - selector: - app: event-store - ---- - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: synapse - namespace: synapse - annotations: - traffic.sidecar.istio.io/excludeOutboundIPRanges: 10.96.0.1/32 -spec: - selector: - matchLabels: - app: synapse - template: - metadata: - labels: - app: synapse - version: '0.1.3' - annotations: - traffic.sidecar.istio.io/excludeOutboundIPRanges: 10.96.0.1/32 - spec: - serviceAccountName: synapse - containers: - - name: synapse - image: ghcr.io/serverlessworkflow/synapse:latest - imagePullPolicy: IfNotPresent - env: - - name: KUBERNETES_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: KUBERNETES_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - ---- - -apiVersion: v1 -kind: Service -metadata: - name: synapse - namespace: synapse - labels: - app: synapse -spec: - ports: - - name: http - port: 42286 - protocol: TCP - - name: http-2 - port: 41387 - protocol: TCP - selector: - app: synapse - ---- - -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: pod-manager - namespace: synapse - labels: - rbac.authorization.k8s.io/aggregate-to-admin: "true" - rbac.authorization.k8s.io/aggregate-to-edit: "true" -rules: -- apiGroups: [""] - resources: ["pods", "pods/log"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - ---- - -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: synapse - namespace: synapse -subjects: - - kind: ServiceAccount - name: synapse - apiGroup: "" -roleRef: - kind: Role - name: pod-manager - apiGroup: rbac.authorization.k8s.io diff --git a/deployment/kubernetes/stand-alone.yaml b/deployment/kubernetes/stand-alone.yaml deleted file mode 100644 index 205472f37..000000000 --- a/deployment/kubernetes/stand-alone.yaml +++ /dev/null @@ -1,99 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: synapse - ---- - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: synapse - namespace: synapse - ---- - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: synapse - namespace: synapse - annotations: - traffic.sidecar.istio.io/excludeOutboundIPRanges: 10.96.0.1/32 -spec: - selector: - matchLabels: - app: synapse - template: - metadata: - labels: - app: synapse - version: '0.1.3' - annotations: - traffic.sidecar.istio.io/excludeOutboundIPRanges: 10.96.0.1/32 - spec: - serviceAccountName: synapse - containers: - - name: synapse - image: ghcr.io/serverlessworkflow/synapse:latest - imagePullPolicy: IfNotPresent - env: - - name: KUBERNETES_POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: KUBERNETES_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - ---- - -apiVersion: v1 -kind: Service -metadata: - name: synapse - namespace: synapse - labels: - app: synapse -spec: - ports: - - name: http - port: 42286 - protocol: TCP - - name: http-2 - port: 41387 - protocol: TCP - selector: - app: synapse - ---- - -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: pod-manager - namespace: synapse - labels: - rbac.authorization.k8s.io/aggregate-to-admin: "true" - rbac.authorization.k8s.io/aggregate-to-edit: "true" -rules: -- apiGroups: [""] - resources: ["pods", "pods/log"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - ---- - -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: synapse - namespace: synapse -subjects: - - kind: ServiceAccount - name: synapse - apiGroup: "" -roleRef: - kind: Role - name: pod-manager - apiGroup: rbac.authorization.k8s.io diff --git a/deployments/docker-compose/.env b/deployments/docker-compose/.env new file mode 100644 index 000000000..57e3307da --- /dev/null +++ b/deployments/docker-compose/.env @@ -0,0 +1 @@ +GARNET_URI=garnet:6379 \ No newline at end of file diff --git a/deployment/docker-compose/.gitignore b/deployments/docker-compose/.gitignore similarity index 100% rename from deployment/docker-compose/.gitignore rename to deployments/docker-compose/.gitignore diff --git a/deployments/docker-compose/config/tokens.yaml b/deployments/docker-compose/config/tokens.yaml new file mode 100644 index 000000000..447f3a84a --- /dev/null +++ b/deployments/docker-compose/config/tokens.yaml @@ -0,0 +1,4 @@ +h0v0fixhkg9z4bgtxele7892x9sjtw7o: + sub: fc4c86cd-e4ff-463c-b0e4-36f34f795d7e + name: root + role: admin \ No newline at end of file diff --git a/deployments/docker-compose/docker-compose.build.yml b/deployments/docker-compose/docker-compose.build.yml new file mode 100644 index 000000000..6b263f8db --- /dev/null +++ b/deployments/docker-compose/docker-compose.build.yml @@ -0,0 +1,62 @@ +services: + + garnet: + image: ghcr.io/microsoft/garnet + volumes: + - garnet_data:/data + + api: + build: + context: ../../ + dockerfile: ./src/api/Synapse.Api.Server/Dockerfile + environment: + CONNECTIONSTRINGS__REDIS: ${GARNET_URI} + SYNAPSE_DASHBOARD_SERVE: true + SYNAPSE_API_AUTH_TOKEN_FILE: /app/tokens.yaml + volumes: + - ./config/tokens.yaml:/app/tokens.yaml + ports: + - 8080:8080 + depends_on: + - garnet + + operator: + build: + context: ../../ + dockerfile: ./src/operator/Synapse.Operator/Dockerfile + environment: + CONNECTIONSTRINGS__REDIS: ${GARNET_URI} + SYNAPSE_OPERATOR_NAMESPACE: default + SYNAPSE_OPERATOR_NAME: operator-1 + SYNAPSE_OPERATOR_RUNNER_API: http://api:8080 + DOCKER_HOST: unix:///var/run/docker.sock + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + user: root + depends_on: + - garnet + + correlator: + build: + context: ../../ + dockerfile: ./src/correlator/Synapse.Correlator/Dockerfile + environment: + CONNECTIONSTRINGS__REDIS: ${GARNET_URI} + SYNAPSE_CORRELATOR_NAMESPACE: default + SYNAPSE_CORRELATOR_NAME: correlator-1 + ports: + - 8081:8080 + depends_on: + - garnet + +volumes: + + garnet_data: + driver: local + +networks: + + default: + name: synapse \ No newline at end of file diff --git a/deployment/docker-compose/docker-compose.dcproj b/deployments/docker-compose/docker-compose.dcproj similarity index 82% rename from deployment/docker-compose/docker-compose.dcproj rename to deployments/docker-compose/docker-compose.dcproj index 5995c80d0..a1488cf79 100644 --- a/deployment/docker-compose/docker-compose.dcproj +++ b/deployments/docker-compose/docker-compose.dcproj @@ -9,7 +9,9 @@ synapse - + + + \ No newline at end of file diff --git a/deployments/docker-compose/docker-compose.yml b/deployments/docker-compose/docker-compose.yml new file mode 100644 index 000000000..0cefac916 --- /dev/null +++ b/deployments/docker-compose/docker-compose.yml @@ -0,0 +1,56 @@ +services: + + garnet: + image: ghcr.io/microsoft/garnet + volumes: + - garnet_data:/data + + api: + image: ghcr.io/serverlessworkflow/synapse/api + environment: + CONNECTIONSTRINGS__REDIS: ${GARNET_URI} + SYNAPSE_DASHBOARD_SERVE: true + SYNAPSE_API_AUTH_TOKEN_FILE: /app/tokens.yaml + volumes: + - ./config/tokens.yaml:/app/tokens.yaml + ports: + - 8080:8080 + depends_on: + - garnet + + operator: + image: ghcr.io/serverlessworkflow/synapse/operator + environment: + CONNECTIONSTRINGS__REDIS: ${GARNET_URI} + SYNAPSE_OPERATOR_NAMESPACE: default + SYNAPSE_OPERATOR_NAME: operator-1 + SYNAPSE_OPERATOR_RUNNER_API: http://api:8080 + DOCKER_HOST: unix:///var/run/docker.sock + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + user: root + depends_on: + - garnet + + correlator: + image: ghcr.io/serverlessworkflow/synapse/correlator + environment: + CONNECTIONSTRINGS__REDIS: ${GARNET_URI} + SYNAPSE_CORRELATOR_NAMESPACE: default + SYNAPSE_CORRELATOR_NAME: correlator-1 + ports: + - 8081:8080 + depends_on: + - garnet + +volumes: + + garnet_data: + driver: local + +networks: + + default: + name: synapse \ No newline at end of file diff --git a/deployments/helm/Chart.yaml b/deployments/helm/Chart.yaml new file mode 100644 index 000000000..da6a5db6b --- /dev/null +++ b/deployments/helm/Chart.yaml @@ -0,0 +1,5 @@ +apiVersion: v2 +name: synapse +description: A Helm chart for deploying Synapse +version: 0.1.0 +appVersion: "1.0.0" \ No newline at end of file diff --git a/deployments/helm/templates/configmap.yaml b/deployments/helm/templates/configmap.yaml new file mode 100644 index 000000000..4a09614a0 --- /dev/null +++ b/deployments/helm/templates/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }}-tokens +data: + tokens.yaml: | + # Add your token configuration here \ No newline at end of file diff --git a/deployments/helm/templates/deployment.yaml b/deployments/helm/templates/deployment.yaml new file mode 100644 index 000000000..3395d97f0 --- /dev/null +++ b/deployments/helm/templates/deployment.yaml @@ -0,0 +1,144 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-garnet + labels: + app: {{ .Release.Name }}-garnet +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Release.Name }}-garnet + template: + metadata: + labels: + app: {{ .Release.Name }}-garnet + spec: + containers: + - name: garnet + image: "{{ .Values.garnet.image.repository }}:{{ .Values.garnet.image.tag }}" + imagePullPolicy: {{ .Values.garnet.image.pullPolicy }} + ports: + - containerPort: 6379 + volumeMounts: + - name: garnet-data + mountPath: /data + volumes: + - name: garnet-data + persistentVolumeClaim: + claimName: {{ .Release.Name }}-garnet-data +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-api + labels: + app: {{ .Release.Name }}-api +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Release.Name }}-api + template: + metadata: + labels: + app: {{ .Release.Name }}-api + spec: + containers: + - name: api + image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}" + ports: + - containerPort: {{ .Values.api.service.port }} + env: + - name: CONNECTIONSTRINGS__REDIS + value: {{ .Values.global.redisConnectionString }} + - name: SYNAPSE_DASHBOARD_SERVE + value: "{{ .Values.api.env.SYNAPSE_DASHBOARD_SERVE }}" + - name: SYNAPSE_API_AUTH_TOKEN_FILE + value: {{ .Values.api.env.SYNAPSE_API_AUTH_TOKEN_FILE }} + volumeMounts: + - name: tokens + mountPath: /app/tokens.yaml + subPath: tokens.yaml + volumes: + - name: tokens + configMap: + name: {{ .Release.Name }}-tokens +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-operator + labels: + app: {{ .Release.Name }}-operator +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Release.Name }}-operator + template: + metadata: + labels: + app: {{ .Release.Name }}-operator + spec: + containers: + - name: operator + image: "{{ .Values.operator.image.repository }}:{{ .Values.operator.image.tag }}" + env: + - name: CONNECTIONSTRINGS__REDIS + value: {{ .Values.global.redisConnectionString }} + - name: SYNAPSE_OPERATOR_NAMESPACE + value: {{ .Values.operator.env.SYNAPSE_OPERATOR_NAMESPACE }} + - name: SYNAPSE_OPERATOR_NAME + value: {{ .Values.operator.env.SYNAPSE_OPERATOR_NAME }} + - name: SYNAPSE_OPERATOR_RUNNER_API + value: {{ .Values.operator.env.SYNAPSE_OPERATOR_RUNNER_API }} + - name: DOCKER_HOST + value: {{ .Values.operator.env.DOCKER_HOST }} + volumeMounts: + - name: docker-sock + mountPath: /var/run/docker.sock + volumes: + - name: docker-sock + hostPath: + path: /var/run/docker.sock +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-correlator + labels: + app: {{ .Release.Name }}-correlator +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ .Release.Name }}-correlator + template: + metadata: + labels: + app: {{ .Release.Name }}-correlator + spec: + containers: + - name: correlator + image: "{{ .Values.correlator.image.repository }}:{{ .Values.correlator.image.tag }}" + ports: + - containerPort: {{ .Values.correlator.service.port }} + env: + - name: CONNECTIONSTRINGS__REDIS + value: {{ .Values.global.redisConnectionString }} + - name: SYNAPSE_CORRELATOR_NAMESPACE + value: {{ .Values.correlator.env.SYNAPSE_CORRELATOR_NAMESPACE }} + - name: SYNAPSE_CORRELATOR_NAME + value: {{ .Values.correlator.env.SYNAPSE_CORRELATOR_NAME }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Release.Name }}-garnet-data +spec: + accessModes: + - {{ .Values.garnet.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.garnet.persistence.size }} \ No newline at end of file diff --git a/deployments/helm/templates/ingress.yaml b/deployments/helm/templates/ingress.yaml new file mode 100644 index 000000000..ca73554b8 --- /dev/null +++ b/deployments/helm/templates/ingress.yaml @@ -0,0 +1,16 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-api-ingress +spec: + rules: + - host: synapse.local + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-api + port: + number: 8080 \ No newline at end of file diff --git a/deployments/helm/templates/services.yaml b/deployments/helm/templates/services.yaml new file mode 100644 index 000000000..65c55da18 --- /dev/null +++ b/deployments/helm/templates/services.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-api +spec: + type: {{ .Values.api.service.type }} + ports: + - port: {{ .Values.api.service.port }} + targetPort: 8080 + selector: + app: {{ .Release.Name }}-api +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-correlator +spec: + type: {{ .Values.correlator.service.type }} + ports: + - port: {{ .Values.correlator.service.port }} + targetPort: 8080 + selector: + app: {{ .Release.Name }}-correlator diff --git a/deployments/helm/values.yaml b/deployments/helm/values.yaml new file mode 100644 index 000000000..458c43c56 --- /dev/null +++ b/deployments/helm/values.yaml @@ -0,0 +1,58 @@ +global: + redisConnectionString: garnet:6379 + +garnet: + image: + repository: ghcr.io/microsoft/garnet + tag: latest + pullPolicy: IfNotPresent + service: + type: ClusterIP + port: 6379 + persistence: + enabled: true + storageClass: "" + accessMode: ReadWriteOnce + size: 10Gi + +api: + image: + repository: synapse-api + tag: latest + service: + type: ClusterIP + port: 8080 + env: + SYNAPSE_DASHBOARD_SERVE: true + SYNAPSE_API_AUTH_TOKEN_FILE: /app/tokens.yaml + +operator: + image: + repository: synapse-operator + tag: latest + service: + type: ClusterIP + env: + SYNAPSE_OPERATOR_NAMESPACE: default + SYNAPSE_OPERATOR_NAME: operator-1 + SYNAPSE_OPERATOR_RUNNER_API: http://api:8080 + DOCKER_HOST: unix:///var/run/docker.sock + +correlator: + image: + repository: synapse-correlator + tag: latest + service: + type: ClusterIP + port: 8081 + env: + SYNAPSE_CORRELATOR_NAMESPACE: default + SYNAPSE_CORRELATOR_NAME: correlator-1 + +serviceAccount: + create: true + annotations: {} + name: "" + +networkPolicy: + enabled: false diff --git a/git/synapse b/git/synapse deleted file mode 160000 index 4fe17b256..000000000 --- a/git/synapse +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4fe17b2569575f19757e894308e18f6fa7fb3e7a diff --git a/src/api/Synapse.Api.Application/Commands/Documents/CreateDocumentCommand.cs b/src/api/Synapse.Api.Application/Commands/Documents/CreateDocumentCommand.cs new file mode 100644 index 000000000..f567c0d43 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Documents/CreateDocumentCommand.cs @@ -0,0 +1,50 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; +using Neuroglia.Data.Infrastructure.Services; + +namespace Synapse.Api.Application.Commands.Documents; + +/// +/// Represents the used to create a new +/// +/// The document to create +public class CreateDocumentCommand(Document document) + : Command +{ + + /// + /// Gets the document to create + /// + public Document Document { get; } = document; + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class CreateDocumentCommandHandler(IRepository documents) + : ICommandHandler +{ + + /// + public virtual async Task> HandleAsync(CreateDocumentCommand command, CancellationToken cancellationToken = default) + { + var document = await documents.AddAsync(command.Document, cancellationToken).ConfigureAwait(false); + await documents.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return this.Ok(document); + } + +} diff --git a/src/api/Synapse.Api.Application/Commands/Documents/UpdateDocumentCommand.cs b/src/api/Synapse.Api.Application/Commands/Documents/UpdateDocumentCommand.cs new file mode 100644 index 000000000..90334e802 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Documents/UpdateDocumentCommand.cs @@ -0,0 +1,59 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.Services; +using Synapse.Resources; + +namespace Synapse.Api.Application.Commands.Documents; + +/// +/// Represents the used to update the content of an existing +/// +/// The id of the document to update +/// The specified document's updated content +public class UpdateDocumentCommand(string id, object content) + : Command +{ + + /// + /// Gets the id of the document to update the content of + /// + public string Id { get; } = id; + + /// + /// Gets the updated content of the specified document + /// + public object Content { get; } = content; + +} + + /// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class UpdateDocumentCommandHandler(IRepository documents) + : ICommandHandler +{ + + /// + public virtual async Task> HandleAsync(UpdateDocumentCommand command, CancellationToken cancellationToken = default) + { + var document = await documents.GetAsync(command.Id, cancellationToken).ConfigureAwait(false); + if (document == null) return this.NotFound(); + document.Content = command.Content; + document = await documents.UpdateAsync(document, cancellationToken).ConfigureAwait(false); + await documents.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return this.Ok(document); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Commands/Events/PublishCloudEventCommand.cs b/src/api/Synapse.Api.Application/Commands/Events/PublishCloudEventCommand.cs new file mode 100644 index 000000000..e49ed2200 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Events/PublishCloudEventCommand.cs @@ -0,0 +1,66 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Neuroglia.Eventing.CloudEvents; +using Neuroglia.Serialization; +using Synapse.Api.Application.Configuration; +using System.Text; + +namespace Synapse.Api.Application.Commands.Events; + +/// +/// Represents the used to publish a to the configured sink +/// +/// The to publish +public class PublishCloudEventCommand(CloudEvent e) + : Command +{ + + /// + /// Gets the to publish + /// + public virtual CloudEvent CloudEvent { get; } = e; + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to perform logging +/// The service used to access the current +/// The service used to serialize/deserialize data to/from JSON +/// The service used to perform HTTP requests +public class PublishCloudEventCommandHandler(ILogger logger, IOptions options, IJsonSerializer jsonSerializer, HttpClient httpClient) + : ICommandHandler +{ + + /// + public virtual async Task HandleAsync(PublishCloudEventCommand command, CancellationToken cancellationToken = default) + { + if (options.Value.Events?.Endpoint == null) return this.Ok(); + var json = jsonSerializer.SerializeToText(command.CloudEvent); + using var content = new StringContent(json, Encoding.UTF8, CloudEventContentType.Json); + using var request = new HttpRequestMessage(HttpMethod.Post, options.Value.Events.Endpoint) { Content = content }; + using var response = await httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + if (!response.IsSuccessStatusCode) + { + logger.LogError("An error occurred while publishing the cloud event with id '{eventId}' to the configure endpoint '{endpoint}': {ex}", command.CloudEvent.Id, options.Value.Events.Endpoint, json); + response.EnsureSuccessStatusCode(); + } + return this.Ok(); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Commands/Resources/CreateResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/CreateResourceCommand.cs new file mode 100644 index 000000000..f821af895 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/CreateResourceCommand.cs @@ -0,0 +1,87 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources; + +/// +/// Represents the command used to create a new +/// +public class CreateResourceCommand + : Command +{ + + /// + /// Initializes a new + /// + /// The resource to create + /// The API group the resource to create belongs to + /// The version of the resource to create + /// The plural name of the type of resource to create + /// A boolean indicating whether or not to persist the changes induces by the command + public CreateResourceCommand(IResource resource, string group, string version, string plural, bool dryRun) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + this.Resource = resource ?? throw new ArgumentNullException(nameof(resource)); + + this.Group = group; + this.Version = version; + this.Plural = plural; + this.DryRun = dryRun; + } + + /// + /// Gets the resource to create + /// + public IResource Resource { get; } + + /// + /// Gets the API group the resource to create belongs to + /// + public string Group { get; } + + /// + /// Gets the version of the resource to create + /// + public string Version { get; } + + /// + /// Gets the plural name of the type of resource to create + /// + public string Plural { get; } + + /// + /// Gets a boolean indicating whether or not to persist the changes induces by the command + /// + public bool DryRun { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class CreateResourceCommandHandler(IResourceRepository repository) + : ICommandHandler +{ + + /// + public virtual async Task> HandleAsync(CreateResourceCommand command, CancellationToken cancellationToken) + { + if (command.Resource.GetName().Trim().EndsWith('-')) command.Resource.Metadata.Name = $"{command.Resource.GetName().Trim()}{Guid.NewGuid().ToString("N")[..15]}"; + var resource = await repository.AddAsync(command.Resource, command.Group, command.Version, command.Plural, command.DryRun, cancellationToken); + return new OperationResult((int)HttpStatusCode.Created, resource); + } + +} diff --git a/src/api/Synapse.Api.Application/Commands/Resources/DeleteResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/DeleteResourceCommand.cs new file mode 100644 index 000000000..260d1474b --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/DeleteResourceCommand.cs @@ -0,0 +1,93 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources; + +/// +/// Represents the used to delete an existing +/// +public class DeleteResourceCommand + : Command +{ + + /// + /// Initializes a new + /// + /// The API group the resource to delete belongs to + /// The version of the resource to delete + /// The plural name of the type of resource to delete + /// The name of the to delete + /// The namespace the to delete belongs to + /// A boolean indicating whether or not to persist changes + public DeleteResourceCommand(string group, string version, string plural, string name, string? @namespace, bool dryRun) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Name = name; + this.Namespace = @namespace; + this.DryRun = dryRun; + } + + /// + /// Gets the API group the resource to delete belongs to + /// + public string Group { get; } + + /// + /// Gets the version of the resource to delete + /// + public string Version { get; } + + /// + /// Gets the plural name of the type of resource to delete + /// + public string Plural { get; } + + /// + /// Gets the name of the to delete + /// + public string Name { get; } + + /// + /// Gets the namespace the to delete belongs to + /// + public string? Namespace { get; } + + /// + /// Gets a boolean indicating whether or not to persist changes + /// + public bool DryRun { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class DeleteResourceCommandHandler(IResourceRepository repository) + : ICommandHandler +{ + + /// + public virtual async Task> HandleAsync(DeleteResourceCommand command, CancellationToken cancellationToken) + { + var resource = await repository.RemoveAsync(command.Group, command.Version, command.Plural, command.Name, command.Namespace, command.DryRun, cancellationToken).ConfigureAwait(false); + return this.Ok(resource); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Commands/Resources/Generic/CreateResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/Generic/CreateResourceCommand.cs new file mode 100644 index 000000000..3d4c383ed --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/Generic/CreateResourceCommand.cs @@ -0,0 +1,54 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources.Generic; + +/// +/// Represents the command used to create a new +/// +/// The type of to create +/// +/// Initializes a new +/// +/// The resource to create +public class CreateResourceCommand(TResource resource) + : Command + where TResource : class, IResource, new() +{ + + /// + /// Gets the resource to create + /// + public TResource Resource { get; } = resource ?? throw new ArgumentNullException(nameof(resource)); + +} + +/// +/// Represents the service used to handle s +/// +/// The type of to create +/// The service used to manage s +public class CreateResourceCommandHandler(IResourceRepository repository) + : ICommandHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(CreateResourceCommand command, CancellationToken cancellationToken) + { + if (command.Resource.GetName().Trim().EndsWith('-')) command.Resource.Metadata.Name = $"{command.Resource.GetName().Trim()}{Guid.NewGuid().ToString("N")[..15]}"; + var resource = await repository.AddAsync(command.Resource, false, cancellationToken).ConfigureAwait(false); + return new OperationResult((int)HttpStatusCode.Created, resource); + } + +} diff --git a/src/api/Synapse.Api.Application/Commands/Resources/Generic/DeleteResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/Generic/DeleteResourceCommand.cs new file mode 100644 index 000000000..d51162236 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/Generic/DeleteResourceCommand.cs @@ -0,0 +1,66 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources.Generic; + +/// +/// Represents the used to delete an existing +/// +/// The type of to create +public class DeleteResourceCommand + : Command + where TResource : class, IResource, new() +{ + + /// + /// Initializes a new + /// + /// The name of the to delete + /// The namespace the to delete belongs to + public DeleteResourceCommand(string name, string? @namespace) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + this.Namespace = @namespace; + } + + /// + /// Gets the name of the to delete + /// + public string Name { get; } + + /// + /// Gets the namespace the to delete belongs to + /// + public string? Namespace { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The type of to create +/// The service used to manage s +public class DeleteResourceCommandHandler(IResourceRepository repository) + : ICommandHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(DeleteResourceCommand command, CancellationToken cancellationToken) + { + var resource = await repository.RemoveAsync(command.Name, command.Namespace, false, cancellationToken).ConfigureAwait(false); + return this.Ok(resource); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Commands/Resources/Generic/PatchResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/Generic/PatchResourceCommand.cs new file mode 100644 index 000000000..e6da67e0b --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/Generic/PatchResourceCommand.cs @@ -0,0 +1,80 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources.Generic; + +/// +/// Represents the used to patch an existing +/// +/// The type of to patch +public class PatchResourceCommand + : Command + where TResource : class, IResource, new() +{ + + /// + /// Initializes a new + /// + /// The name of the to patch + /// The namespace the to patch belongs to + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + public PatchResourceCommand(string name, string? @namespace, Patch patch, string? resourceVersion) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + this.Namespace = @namespace; + this.Patch = patch ?? throw new ArgumentNullException(nameof(patch)); + this.ResourceVersion = resourceVersion; + } + + /// + /// Gets the name of the to patch + /// + public string Name { get; } + + /// + /// Gets the name of the to patch + /// + public string? Namespace { get; } + + /// + /// Gets the patch to apply + /// + public Patch Patch { get; } + + /// + /// Gets the expected resource version, if any, used for optimistic concurrency + /// + public string? ResourceVersion { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The type of to patch +/// The service used to manage s +public class PatchResourceCommandHandler(IResourceRepository repository) + : ICommandHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(PatchResourceCommand command, CancellationToken cancellationToken) + { + var resource = await repository.PatchAsync(command.Patch, command.Name, command.Namespace, command.ResourceVersion, false, cancellationToken).ConfigureAwait(false); + return this.Ok(resource); + } + +} diff --git a/src/api/Synapse.Api.Application/Commands/Resources/Generic/PatchResourceStatusCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/Generic/PatchResourceStatusCommand.cs new file mode 100644 index 000000000..3465b4c48 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/Generic/PatchResourceStatusCommand.cs @@ -0,0 +1,82 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Api.Application.Commands.Resources.Generic; + +/// +/// Represents the used to patch the status of an +/// +/// The type of to patch +public class PatchResourceStatusCommand + : Command + where TResource : class, IResource, new() +{ + + /// + /// Initializes a new + /// + /// The name of the to patch + /// The namespace the to patch belongs to + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + public PatchResourceStatusCommand(string name, string? @namespace, Patch patch, string? resourceVersion) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + this.Namespace = @namespace; + this.Patch = patch ?? throw new ArgumentNullException(nameof(patch)); + this.ResourceVersion = resourceVersion; + } + + /// + /// Gets the name of the to patch + /// + public string Name { get; } + + /// + /// Gets the name of the to patch + /// + public string? Namespace { get; } + + /// + /// Gets the patch to apply + /// + public Patch Patch { get; } + + /// + /// Gets the expected resource version, if any, used for optimistic concurrency + /// + public string? ResourceVersion { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The type of to patch +/// The service used to manage s +public class PatchResourceStatusCommandHandler(IResourceRepository repository) + : ICommandHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(PatchResourceStatusCommand command, CancellationToken cancellationToken) + { + var resource = await repository.PatchStatusAsync(command.Patch, command.Name, command.Namespace, command.ResourceVersion, false, cancellationToken).ConfigureAwait(false); + return this.Ok(resource); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Commands/Resources/Generic/ReplaceResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/Generic/ReplaceResourceCommand.cs new file mode 100644 index 000000000..0c1c99ccf --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/Generic/ReplaceResourceCommand.cs @@ -0,0 +1,53 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources.Generic; + +/// +/// Represents the used to replace an existing +/// +/// The type of to replace +/// +/// Initializes a new +/// +/// The updated to replace +public class ReplaceResourceCommand(TResource resource) + : Command + where TResource : class, IResource, new() +{ + + /// + /// Gets the updated to replace + /// + public TResource Resource { get; } = resource; + +} + +/// +/// Represents the service used to handle s +/// +/// The type of to replace +/// The service used to manage s +public class ReplaceResourceCommandHandler(IResourceRepository repository) + : ICommandHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(ReplaceResourceCommand command, CancellationToken cancellationToken) + { + var resource = await repository.ReplaceAsync(command.Resource, false, cancellationToken).ConfigureAwait(false); + return new OperationResult((int)HttpStatusCode.OK, resource); + } + +} diff --git a/src/api/Synapse.Api.Application/Commands/Resources/Generic/ReplaceResourceStatusCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/Generic/ReplaceResourceStatusCommand.cs new file mode 100644 index 000000000..86865d26f --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/Generic/ReplaceResourceStatusCommand.cs @@ -0,0 +1,53 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources.Generic; + +/// +/// Represents the used to replace the status of an +/// +/// The type of to replace +/// +/// Initializes a new +/// +/// The updated to replace +public class ReplaceResourceStatusCommand(TResource resource) + : Command + where TResource : class, IResource, new() +{ + + /// + /// Gets the updated to replace + /// + public TResource Resource { get; } = resource; + +} + +/// +/// Represents the service used to handle s +/// +/// The type of to replace +/// The service used to manage s +public class ReplaceResourceStatusCommandHandler(IResourceRepository repository) + : ICommandHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(ReplaceResourceStatusCommand command, CancellationToken cancellationToken) + { + var resource = await repository.ReplaceStatusAsync(command.Resource, false, cancellationToken).ConfigureAwait(false); + return new OperationResult((int)HttpStatusCode.OK, resource); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Commands/Resources/PatchResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/PatchResourceCommand.cs new file mode 100644 index 000000000..bd1e8d577 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/PatchResourceCommand.cs @@ -0,0 +1,98 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources; + +/// +/// Represents the used to patch an +/// +public class PatchResourceCommand + : Command +{ + + /// + /// Initializes a new + /// + protected PatchResourceCommand() { } + + /// + /// Initializes a new + /// + /// The API group the resource to patch belongs to + /// The version of the resource to patch + /// The plural name of the type of resource to patch + /// The name of the to patch + /// The namespace the to patch belongs to + /// The patch to apply + public PatchResourceCommand(string group, string version, string plural, string name, string? @namespace, Patch patch) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Name = name; + this.Namespace = @namespace; + this.Patch = patch ?? throw new ArgumentNullException(nameof(patch)); + } + + /// + /// Gets the API group the resource to patch belongs to + /// + public string Group { get; } = null!; + + /// + /// Gets the version of the resource to patch + /// + public string Version { get; } = null!; + + /// + /// Gets the plural name of the type of resource to patch + /// + public string Plural { get; } = null!; + + /// + /// Gets the name of the to patch + /// + public string Name { get; } = null!; + + /// + /// Gets the name of the to patch + /// + public string? Namespace { get; } + + /// + /// Gets the patch to apply + /// + public Patch Patch { get; } = null!; + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class PatchResourceCommandHandler(IResourceRepository repository) + : ICommandHandler +{ + + /// + public virtual async Task> HandleAsync(PatchResourceCommand command, CancellationToken cancellationToken) + { + var resource = await repository.PatchAsync(command.Patch, command.Group, command.Version, command.Plural, command.Name, command.Namespace, null, false, cancellationToken).ConfigureAwait(false); + return this.Ok(resource); + } + +} diff --git a/src/api/Synapse.Api.Application/Commands/Resources/ReplaceResourceCommand.cs b/src/api/Synapse.Api.Application/Commands/Resources/ReplaceResourceCommand.cs new file mode 100644 index 000000000..ebeea8e22 --- /dev/null +++ b/src/api/Synapse.Api.Application/Commands/Resources/ReplaceResourceCommand.cs @@ -0,0 +1,78 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Commands.Resources; + +/// +/// Represents the used to replace an existing +/// +public class ReplaceResourceCommand + : Command +{ + + /// + /// Initializes a new + /// + /// The API group the resource to replace belongs to + /// The version of the resource to replace + /// The plural name of the type of resource to replace + /// The updated to replace + public ReplaceResourceCommand(string group, string version, string plural, IResource resource) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Resource = resource; + } + + /// + /// Gets the API group the resource to replace belongs to + /// + public string Group { get; } + + /// + /// Gets the version of the resource to replace + /// + public string Version { get; } + + /// + /// Gets the plural name of the type of resource to replace + /// + public string Plural { get; } + + /// + /// Gets the updated to replace + /// + public IResource Resource { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class ReplaceResourceCommandHandler(IResourceRepository repository) + : ICommandHandler +{ + + /// + public virtual async Task> HandleAsync(ReplaceResourceCommand command, CancellationToken cancellationToken) + { + var resource = await repository.ReplaceAsync(command.Resource, command.Group, command.Version, command.Plural, false, cancellationToken).ConfigureAwait(false); + return new OperationResult((int)HttpStatusCode.OK, resource); + } + +} diff --git a/src/api/Synapse.Api.Application/Configuration/ApiServerOptions.cs b/src/api/Synapse.Api.Application/Configuration/ApiServerOptions.cs new file mode 100644 index 000000000..1434e9ecc --- /dev/null +++ b/src/api/Synapse.Api.Application/Configuration/ApiServerOptions.cs @@ -0,0 +1,46 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Configuration; + +/// +/// Represents the options used to configure a Synapse API server +/// +public class ApiServerOptions +{ + + /// + /// Initializes a new + /// + public ApiServerOptions() + { + var env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Dashboard.Serve); + if (!string.IsNullOrWhiteSpace(env) && bool.TryParse(env, out var serveDashboard)) this.ServeDashboard = serveDashboard; + } + + /// + /// Gets/sets a boolean indicating whether or not to serve the Synapse Dashboard + /// + public virtual bool ServeDashboard { get; set; } = true; + + /// + /// Gets/sets the application's authentication policy + /// + public virtual AuthenticationPolicyOptions Authentication { get; set; } = new(); + + /// + /// Gets/sets the options used to configure the cloud events published by the Synapse API Server + /// + public virtual CloudEventOptions? Events { get; set; } + +} diff --git a/src/api/Synapse.Api.Application/Configuration/AuthenticationPolicyOptions.cs b/src/api/Synapse.Api.Application/Configuration/AuthenticationPolicyOptions.cs new file mode 100644 index 000000000..882e46165 --- /dev/null +++ b/src/api/Synapse.Api.Application/Configuration/AuthenticationPolicyOptions.cs @@ -0,0 +1,95 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Serialization.Yaml; + +namespace Synapse.Api.Application.Configuration; + +/// +/// Represents the options used to configure the authentication policy of a Synapse API server +/// +public class AuthenticationPolicyOptions +{ + + /// + /// Initializes a new + /// + public AuthenticationPolicyOptions() + { + var env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.File); + if (!string.IsNullOrWhiteSpace(env)) + { + if (!File.Exists(env)) throw new FileNotFoundException($"The specified file '{env}' does not exist or cannot be found", env); + var yaml = File.ReadAllText(env); + this.Tokens = YamlSerializer.Default.Deserialize>>(yaml) ?? []; + } + env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.Jwt.Authority); + if(!string.IsNullOrWhiteSpace(env)) + { + this.Jwt ??= new(); + this.Jwt.Authority = new(env); + } + env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.Jwt.Audience); + if (!string.IsNullOrWhiteSpace(env)) + { + this.Jwt ??= new(); + this.Jwt.Audience = env; + } + env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.Oidc.Authority); + if (!string.IsNullOrWhiteSpace(env)) + { + this.Oidc ??= new(); + this.Oidc.Authority = new(env); + } + env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.Oidc.ClientId); + if (!string.IsNullOrWhiteSpace(env)) + { + this.Oidc ??= new(); + this.Oidc.ClientId = env; + } + env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.Oidc.ClientSecret); + if (!string.IsNullOrWhiteSpace(env)) + { + this.Oidc ??= new(); + this.Oidc.ClientSecret = env; + } + env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.Oidc.Scope); + if (!string.IsNullOrWhiteSpace(env)) + { + this.Oidc ??= new(); + if(!string.IsNullOrWhiteSpace(env)) this.Oidc.Scope = [.. env.Split(',', StringSplitOptions.RemoveEmptyEntries)]; + } + env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Authentication.Oidc.SigningKey); + if (!string.IsNullOrWhiteSpace(env)) + { + this.Oidc ??= new(); + this.Oidc.SigningKey = env; + } + } + + /// + /// Gets/sets a token/claims mappings of the static tokens to use + /// + public virtual Dictionary> Tokens { get; set; } = []; + + /// + /// Gets/sets the options used to configure the Jwt Bearer authentication to use, if any + /// + public virtual JwtBearerAuthenticationOptions? Jwt { get; set; } + + /// + /// Gets/sets the options used to configure the Jwt Bearer authentication to use, if any + /// + public virtual OidcAuthenticationOptions? Oidc { get; set; } + +} diff --git a/src/api/Synapse.Api.Application/Configuration/CloudEventOptions.cs b/src/api/Synapse.Api.Application/Configuration/CloudEventOptions.cs new file mode 100644 index 000000000..0ffd8acb5 --- /dev/null +++ b/src/api/Synapse.Api.Application/Configuration/CloudEventOptions.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Eventing.CloudEvents; + +namespace Synapse.Api.Application.Configuration; + +/// +/// Represents the options used to configure the Cloud Events published by the Synapse API server +/// +public class CloudEventOptions +{ + + /// + /// Gets/sets the uri, if any, to which to publish s + /// + public virtual Uri? Endpoint { get; set; } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Configuration/JwtBearerAuthenticationOptions.cs b/src/api/Synapse.Api.Application/Configuration/JwtBearerAuthenticationOptions.cs new file mode 100644 index 000000000..fe9ca9c9f --- /dev/null +++ b/src/api/Synapse.Api.Application/Configuration/JwtBearerAuthenticationOptions.cs @@ -0,0 +1,57 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.IdentityModel.Tokens; +using System.Security.Cryptography; + +namespace Synapse.Api.Application.Configuration; + +/// +/// Represents the options used to configure the application's JWT authentication +/// +public class JwtBearerAuthenticationOptions +{ + + /// + /// Gets/sets the uri of the JWT authority to use + /// + public virtual string Authority { get; set; } = null!; + + /// + /// Gets/sets the application's required JWT audience, if any + /// + public virtual string? Audience { get; set; } + + /// + /// Gets/sets the key used by the JWT authority to sign tokens + /// + public virtual string SigningKey { get; set; } = null!; + + /// + /// Gets/sets the expected JWT issuer, if any + /// + public virtual string? Issuer { get; set; } + + /// + /// Gets the configured issuer signing key + /// + /// A new + public virtual SecurityKey GetSigningKey() + { + var keyBytes = Convert.FromBase64String(Environment.GetEnvironmentVariable("JWT_SIGNING_KEY")!); + var rsa = RSA.Create(); + rsa.ImportSubjectPublicKeyInfo(keyBytes, out _); + return new RsaSecurityKey(rsa); + } + +} diff --git a/src/api/Synapse.Api.Application/Configuration/OidcAuthenticationOptions.cs b/src/api/Synapse.Api.Application/Configuration/OidcAuthenticationOptions.cs new file mode 100644 index 000000000..9cb2fc62a --- /dev/null +++ b/src/api/Synapse.Api.Application/Configuration/OidcAuthenticationOptions.cs @@ -0,0 +1,92 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.IdentityModel.Tokens; +using System.Security.Cryptography; + +namespace Synapse.Api.Application.Configuration; + +/// +/// Represents the options used to configure the OIDC authentication of a Synapse API server +/// +public class OidcAuthenticationOptions +{ + + /// + /// Gets/sets the uri of the OIDC authority to use + /// + public virtual string Authority { get; set; } = null!; + + /// + /// Gets/sets the uri of the OIDC client id to use + /// + public virtual string ClientId { get; set; } = null!; + + /// + /// Gets/sets the uri of the OIDC client secret to use + /// + public virtual string? ClientSecret { get; set; } = null!; + + /// + /// Gets/sets the uri of the OIDC scope(s) to use + /// + public virtual List Scope { get; set; } = []; + + /// + /// Gets or sets the 'resource'. + /// + public string? Resource { get; set; } + + /// + /// Gets or sets the 'response_mode'. + /// + public string ResponseMode { get; set; } = "form_post"; + + /// + /// Gets or sets the 'response_type'. + /// + public string ResponseType { get; set; } = "id_token"; + + /// + /// Gets/sets a boolean indicating whether or not to use of the Proof Key for Code Exchange (PKCE) standard + /// + public virtual bool UsePkce { get; set; } = true; + + /// + /// Gets/sets the key used by the OIDC authority to sign tokens + /// + public virtual string SigningKey { get; set; } = null!; + + /// + /// Gets/sets the expected OIDC audience, if any + /// + public virtual string? Audience { get; set; } + + /// + /// Gets/sets the expected OIDC issuer, if any + /// + public virtual string? Issuer { get; set; } + + /// + /// Gets the configured issuer signing key + /// + /// A new + public virtual SecurityKey GetSigningKey() + { + var keyBytes = Convert.FromBase64String(Environment.GetEnvironmentVariable("JWT_SIGNING_KEY")!); + var rsa = RSA.Create(); + rsa.ImportSubjectPublicKeyInfo(keyBytes, out _); + return new RsaSecurityKey(rsa); + } + +} diff --git a/src/api/Synapse.Api.Application/Configuration/StaticBearerAuthenticationOptions.cs b/src/api/Synapse.Api.Application/Configuration/StaticBearerAuthenticationOptions.cs new file mode 100644 index 000000000..b0146134b --- /dev/null +++ b/src/api/Synapse.Api.Application/Configuration/StaticBearerAuthenticationOptions.cs @@ -0,0 +1,68 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.AspNetCore.Authentication; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; + +namespace Synapse.Api.Application.Configuration; + +/// +/// Represents the used to configure the application's static token based authentication +/// +public class StaticBearerAuthenticationOptions + : AuthenticationSchemeOptions +{ + + /// + /// Gets/sets a token/user mapping of the application's static bearer tokens + /// + public IDictionary Tokens { get; set; } = new Dictionary(); + + /// + /// Adds a new token for the specified + /// + /// The token to add + /// The to build and add a new token for + /// The token generated for the specified + public virtual string AddToken(string token, ClaimsIdentity identity) + { + ArgumentException.ThrowIfNullOrWhiteSpace(token); + ArgumentNullException.ThrowIfNull(identity); + this.Tokens[token] = identity; + return token; + } + + /// + /// Adds a new token for the specified + /// + /// The to build and add a new token for + /// The token generated for the specified + public virtual string AddToken(ClaimsIdentity identity) + { + ArgumentNullException.ThrowIfNull(identity); + var tokenHandler = new JwtSecurityTokenHandler(); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = identity, + Issuer = "synapse-api", + Audience = "synapse-api" + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + var encoded = tokenHandler.WriteToken(token); + this.Tokens[encoded] = identity; + return encoded; + } + +} diff --git a/src/api/Synapse.Api.Application/Extensions/IServiceCollectionExtensions.cs b/src/api/Synapse.Api.Application/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 000000000..e43a4b3d2 --- /dev/null +++ b/src/api/Synapse.Api.Application/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,161 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.DependencyInjection; +using Neuroglia.Data.Infrastructure; +using Neuroglia.Security; +using Synapse.Api.Application.Commands.Documents; +using Synapse.Api.Application.Commands.Events; +using Synapse.Api.Application.Commands.Resources.Generic; +using Synapse.Api.Application.Queries.Documents; +using Synapse.Api.Application.Queries.Resources.Generic; +using Synapse.Api.Application.Queries.Users; +using Synapse.Api.Application.Queries.WorkflowInstances; +using Synapse.Resources; + +namespace Synapse.Api.Application; + +/// +/// Defines extensions for s +/// +public static class IServiceCollectionExtensions +{ + + /// + /// Adds and configures runtime infrastructure services + /// + /// The to configure + /// The configured + public static IServiceCollection AddSynapseApi(this IServiceCollection services) + { + services.AddApiCommands(); + services.AddApiQueries(); + + return services; + } + + /// + /// Registers and configures generic query handlers + /// + /// The to configure + /// The configured + public static IServiceCollection AddApiQueries(this IServiceCollection services) + { + var resourceTypes = typeof(Workflow).Assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType && !t.IsInterface && typeof(Resource).IsAssignableFrom(t)).ToList(); + resourceTypes.Add(typeof(Namespace)); + foreach (var queryableType in resourceTypes) + { + var serviceLifetime = ServiceLifetime.Scoped; + Type queryType; + Type resultType; + Type handlerServiceType; + Type handlerImplementationType; + + queryType = typeof(GetResourceQuery<>).MakeGenericType(queryableType); + resultType = typeof(IOperationResult<>).MakeGenericType(queryableType); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); + handlerImplementationType = typeof(GetResourceQueryHandler<>).MakeGenericType(queryableType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + queryType = typeof(GetResourceDefinitionQuery<>).MakeGenericType(queryableType); + resultType = typeof(IOperationResult); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); + handlerImplementationType = typeof(GetResourceDefinitionQueryHandler<>).MakeGenericType(queryableType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + queryType = typeof(GetResourcesQuery<>).MakeGenericType(queryableType); + resultType = typeof(IOperationResult<>).MakeGenericType(typeof(IAsyncEnumerable<>).MakeGenericType(queryableType)); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); + handlerImplementationType = typeof(GetResourcesQueryHandler<>).MakeGenericType(queryableType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + queryType = typeof(ListResourcesQuery<>).MakeGenericType(queryableType); + resultType = typeof(IOperationResult<>).MakeGenericType(typeof(Neuroglia.Data.Infrastructure.ResourceOriented.ICollection<>).MakeGenericType(queryableType)); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); + handlerImplementationType = typeof(ListResourcesQueryHandler<>).MakeGenericType(queryableType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + queryType = typeof(WatchResourcesQuery<>).MakeGenericType(queryableType); + resultType = typeof(IOperationResult<>).MakeGenericType(typeof(IAsyncEnumerable<>).MakeGenericType(typeof(IResourceWatchEvent<>).MakeGenericType(queryableType))); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); + handlerImplementationType = typeof(WatchResourcesQueryHandler<>).MakeGenericType(queryableType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + queryType = typeof(MonitorResourceQuery<>).MakeGenericType(queryableType); + resultType = typeof(IOperationResult<>).MakeGenericType(typeof(IAsyncEnumerable<>).MakeGenericType(typeof(IResourceWatchEvent<>).MakeGenericType(queryableType))); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); + handlerImplementationType = typeof(MonitorResourceQueryHandler<>).MakeGenericType(queryableType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + } + services.AddScoped>, GetUserProfileQueryHandler>(); + services.AddScoped>, GetDocumentQueryHandler>(); + services.AddScoped>, ReadWorkflowInstanceLogsQueryHandler>(); + services.AddScoped>>, WatchWorkflowInstanceLogsQueryHandler>(); + services.AddScoped, PublishCloudEventCommandHandler>(); + return services; + } + + /// + /// Registers and configures generic command handlers + /// + /// The to configure + /// The configured + public static IServiceCollection AddApiCommands(this IServiceCollection services) + { + var resourceTypes = typeof(Workflow).Assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && !t.IsGenericType && !t.IsInterface && typeof(Resource).IsAssignableFrom(t)).ToList(); + resourceTypes.Add(typeof(Namespace)); + foreach (var resourceType in resourceTypes) + { + var serviceLifetime = ServiceLifetime.Scoped; + Type commandType; + Type resultType; + Type handlerServiceType; + Type handlerImplementationType; + + commandType = typeof(CreateResourceCommand<>).MakeGenericType(resourceType); + resultType = typeof(IOperationResult<>).MakeGenericType(resourceType); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); + handlerImplementationType = typeof(CreateResourceCommandHandler<>).MakeGenericType(resourceType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + commandType = typeof(ReplaceResourceCommand<>).MakeGenericType(resourceType); + resultType = typeof(IOperationResult<>).MakeGenericType(resourceType); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); + handlerImplementationType = typeof(ReplaceResourceCommandHandler<>).MakeGenericType(resourceType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + commandType = typeof(PatchResourceCommand<>).MakeGenericType(resourceType); + resultType = typeof(IOperationResult<>).MakeGenericType(resourceType); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); + handlerImplementationType = typeof(PatchResourceCommandHandler<>).MakeGenericType(resourceType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + commandType = typeof(PatchResourceStatusCommand<>).MakeGenericType(resourceType); + resultType = typeof(IOperationResult<>).MakeGenericType(resourceType); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); + handlerImplementationType = typeof(PatchResourceStatusCommandHandler<>).MakeGenericType(resourceType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + commandType = typeof(DeleteResourceCommand<>).MakeGenericType(resourceType); + resultType = typeof(IOperationResult<>).MakeGenericType(resourceType); + handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); + handlerImplementationType = typeof(DeleteResourceCommandHandler<>).MakeGenericType(resourceType); + services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); + + } + services.AddScoped>, CreateDocumentCommandHandler>(); + services.AddScoped>, UpdateDocumentCommandHandler>(); + return services; + } + +} diff --git a/src/api/Synapse.Api.Application/Properties/launchSettings.json b/src/api/Synapse.Api.Application/Properties/launchSettings.json new file mode 100644 index 000000000..f44eeeb08 --- /dev/null +++ b/src/api/Synapse.Api.Application/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Synapse.Api.Application": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:54995;http://localhost:54996" + } + } +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Queries/Documents/GetDocumentQuery.cs b/src/api/Synapse.Api.Application/Queries/Documents/GetDocumentQuery.cs new file mode 100644 index 000000000..f47166ee9 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Documents/GetDocumentQuery.cs @@ -0,0 +1,48 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; +using Neuroglia.Data.Infrastructure.Services; + +namespace Synapse.Api.Application.Queries.Documents; + +/// +/// Represent the used to get a specific workflow data document +/// +/// The id of the workflow data document to get +public class GetDocumentQuery(string id) + : Query +{ + + /// + /// Gets the id of the workflow data document to get + /// + public string Id { get; } = id; + +} + +/// +/// Represents the service used to handle instances +/// +/// The used to manage workflow data s +public class GetDocumentQueryHandler(IRepository documents) + : IQueryHandler +{ + + /// + public virtual async Task> HandleAsync(GetDocumentQuery query, CancellationToken cancellationToken) + { + return this.Ok(await documents.GetAsync(query.Id, cancellationToken).ConfigureAwait(false)); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourceDefinitionQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourceDefinitionQuery.cs new file mode 100644 index 000000000..de366c135 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourceDefinitionQuery.cs @@ -0,0 +1,46 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources.Generic; + +/// +/// Represents the used to get the definition of the specified type +/// +/// The type of the to get the definition of +public class GetResourceDefinitionQuery + : Query + where TResource : class, IResource, new() +{ + + + +} + +/// +/// Represents the service used to handle s +/// +/// The type of the to replace +/// The service used to manage s +public class GetResourceDefinitionQueryHandler(IResourceRepository repository) + : IQueryHandler, IResourceDefinition> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(GetResourceDefinitionQuery query, CancellationToken cancellationToken) + { + var resourceDefinition = await repository.GetDefinitionAsync(cancellationToken).ConfigureAwait(false); + return this.Ok(resourceDefinition); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourceQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourceQuery.cs new file mode 100644 index 000000000..2858aed16 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourceQuery.cs @@ -0,0 +1,66 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources.Generic; + +/// +/// Represents the used to get an existing +/// +/// The type of the to get +public class GetResourceQuery + : Query + where TResource : class, IResource, new() +{ + + /// + /// Initializes a new + /// + /// The name of the to get + /// The namespace the to get belongs to + public GetResourceQuery(string name, string? @namespace) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + this.Namespace = @namespace; + } + + /// + /// Gets the name of the to get + /// + public string Name { get; } + + /// + /// Gets the namespace the to get belongs to + /// + public string? Namespace { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The type of the to replace +/// The service used to manage s +public class GetResourceQueryHandler(IResourceRepository repository) + : IQueryHandler, TResource> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task> HandleAsync(GetResourceQuery query, CancellationToken cancellationToken) + { + var resource = await repository.GetAsync(query.Name, query.Namespace, cancellationToken).ConfigureAwait(false) ?? throw new ProblemDetailsException(ResourceProblemDetails.ResourceNotFound(new ResourceReference(query.Name, query.Namespace))); + return this.Ok(resource); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourcesQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourcesQuery.cs new file mode 100644 index 000000000..53c221202 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/Generic/GetResourcesQuery.cs @@ -0,0 +1,58 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources.Generic; + +/// +/// Represents the used to retrieves s of the specified type +/// +/// +/// Initializes a new +/// +/// The namespace the s to get belong to, if any +/// A containing the s used to filter s by +public class GetResourcesQuery(string? @namespace, IEnumerable? labelSelectors) + : Query> + where TResource : class, IResource, new() +{ + + /// + /// Gets the namespace the s to get belong to, if any + /// + public string? Namespace { get; } = @namespace; + + /// + /// Gets a containing the s used to filter s by + /// + public IEnumerable? LabelSelectors { get; } = labelSelectors; + +} + +/// +/// Represents the service used to handle instances +/// +/// The type of to get +/// The service used to manage s +public class GetResourcesQueryHandler(IResourceRepository repository) + : IQueryHandler, IAsyncEnumerable> + where TResource : class, IResource, new() +{ + + + /// + public virtual Task>> HandleAsync(GetResourcesQuery query, CancellationToken cancellationToken) + { + return Task.FromResult(this.Ok(repository.GetAllAsync(query.Namespace, query.LabelSelectors, cancellationToken))); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Queries/Resources/Generic/ListResourcesQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/Generic/ListResourcesQuery.cs new file mode 100644 index 000000000..9a2ff5b51 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/Generic/ListResourcesQuery.cs @@ -0,0 +1,70 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources.Generic; + +/// +/// Represents the used to list all s +/// +/// The type of s to list +/// +/// Initializes a new +/// +/// The namespace the s to get belong to, if any +/// A containing the s used to filter s by +/// The maximum amount of results to list +/// The token, if any, used to continue enumerating the results +public class ListResourcesQuery(string? @namespace, IEnumerable? labelSelectors, ulong? maxResults, string? continuationToken) + : Query> + where TResource : class, IResource, new() +{ + + /// + /// Gets the namespace the s to get belong to, if any + /// + public string? Namespace { get; } = @namespace; + + /// + /// Gets a containing the s used to filter s by + /// + public IEnumerable? LabelSelectors { get; } = labelSelectors; + + /// + /// Gets the maximum amount of results to list + /// + public ulong? MaxResults { get; } = maxResults; + + /// + /// Gets the token, if any, used to continue enumerating the results + /// + public string? ContinuationToken { get; } = continuationToken; + +} + +/// +/// Represents the service used to handle instances +/// +/// The type of s to list +/// The service used to manage s +public class ListResourcesQueryHandler(IResourceRepository repository) + : IQueryHandler, Neuroglia.Data.Infrastructure.ResourceOriented.ICollection> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task>> HandleAsync(ListResourcesQuery query, CancellationToken cancellationToken) + { + return this.Ok(await repository.ListAsync(query.Namespace, query.LabelSelectors, query.MaxResults, query.ContinuationToken, cancellationToken).ConfigureAwait(false)); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/Resources/Generic/MonitorResourceQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/Generic/MonitorResourceQuery.cs new file mode 100644 index 000000000..e20ca8d92 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/Generic/MonitorResourceQuery.cs @@ -0,0 +1,64 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources.Generic; + +/// +/// Represents the used to retrieves s of the specified type +/// +public class MonitorResourceQuery + : Query>> + where TResource : class, IResource, new() +{ + + /// + /// Initializes a new + /// + /// The name of the to monitor + /// The namespace the to monitor belongs to, if any + public MonitorResourceQuery(string name, string? @namespace) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + this.Name = name; + this.Namespace = @namespace; + } + + /// + /// Gets the name of the s to monitor + /// + public string Name { get; } + + /// + /// Gets the namespace the s to get belongs to, if any + /// + public string? Namespace { get; } + +} + +/// +/// Represents the service used to handle instances +/// +/// The type of to monitor +/// The service used to manage s +public class MonitorResourceQueryHandler(IResourceRepository repository) + : IQueryHandler, IAsyncEnumerable>> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task>>> HandleAsync(MonitorResourceQuery query, CancellationToken cancellationToken) + { + return this.Ok((await repository.MonitorAsync(query.Name, query.Namespace, false, cancellationToken).ConfigureAwait(false)).ToAsyncEnumerable()); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Queries/Resources/Generic/WatchResourcesQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/Generic/WatchResourcesQuery.cs new file mode 100644 index 000000000..4ef9b7d89 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/Generic/WatchResourcesQuery.cs @@ -0,0 +1,57 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources.Generic; + +/// +/// Represents the used to retrieves s of the specified type +/// +/// +/// Initializes a new +/// +/// The namespace the s to get belong to, if any +/// A containing the s used to filter s by +public class WatchResourcesQuery(string? @namespace, IEnumerable? labelSelectors) + : Query>> + where TResource : class, IResource, new() +{ + + /// + /// Gets the namespace the s to get belong to, if any + /// + public string? Namespace { get; } = @namespace; + + /// + /// Gets a containing the s used to filter s by + /// + public IEnumerable? LabelSelectors { get; } = labelSelectors; + +} + +/// +/// Represents the service used to handle instances +/// +/// The type of to get +/// The service used to manage s +public class WatchResourcesQueryHandler(IResourceRepository repository) + : IQueryHandler, IAsyncEnumerable>> + where TResource : class, IResource, new() +{ + + /// + public virtual async Task>>> HandleAsync(WatchResourcesQuery query, CancellationToken cancellationToken) + { + return this.Ok((await repository.WatchAsync(query.Namespace, query.LabelSelectors, cancellationToken).ConfigureAwait(false)).ToAsyncEnumerable()); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/Resources/GetResourceQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/GetResourceQuery.cs new file mode 100644 index 000000000..6312bdf0a --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/GetResourceQuery.cs @@ -0,0 +1,85 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources; + +/// +/// Represents the used to get an existing +/// +public class GetResourceQuery + : Query +{ + + /// + /// Initializes a new + /// + /// The API group the resource to get belongs to + /// The version of the resource to get + /// The plural name of the type of resource to get + /// The name of the to get + /// The namespace the to get belongs to + public GetResourceQuery(string group, string version, string plural, string name, string? @namespace) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Name = name; + this.Namespace = @namespace; + } + + /// + /// Gets the API group the resource to get belongs to + /// + public string Group { get; } + + /// + /// Gets the version of the resource to get + /// + public string Version { get; } + + /// + /// Gets the plural name of the type of resource to get + /// + public string Plural { get; } + + /// + /// Gets the name of the to get + /// + public string Name { get; } + + /// + /// Gets the namespace the to get belongs to + /// + public string? Namespace { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage s +public class GetResourceQueryHandler(IResourceRepository repository) + : IQueryHandler +{ + + /// + public virtual async Task> HandleAsync(GetResourceQuery query, CancellationToken cancellationToken) + { + return this.Ok(await repository.GetAsync(query.Group, query.Version, query.Plural, query.Name, query.Namespace, cancellationToken).ConfigureAwait(false)); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/Resources/GetResourcesQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/GetResourcesQuery.cs new file mode 100644 index 000000000..3aef7223c --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/GetResourcesQuery.cs @@ -0,0 +1,84 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources; + +/// +/// Represents the used to retrieves s of the specified type +/// +public class GetResourcesQuery + : Query> +{ + + /// + /// Initializes a new + /// + /// The API group the resources to get belongs to + /// The version of the resources to get + /// The plural name of the type of resources to get + /// The namespace the s to get belong to, if any + /// A containing the s used to filter s by + public GetResourcesQuery(string group, string version, string plural, string? @namespace, List? labelSelectors) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Namespace = @namespace; + this.LabelSelectors = labelSelectors; + } + + /// + /// Gets the API group the resources to get belongs to + /// + public string Group { get; } + + /// + /// Gets the version of the resources to get + /// + public string Version { get; } + + /// + /// Gets the plural name of the type of resources to get + /// + public string Plural { get; } + + /// + /// Gets the namespace the s to get belong to, if any + /// + public string? Namespace { get; } + + /// + /// Gets a containing the s used to filter s by + /// + public List? LabelSelectors { get; } + +} + +/// +/// Represents the service used to handle instances +/// +/// The service used to manage s +public class GetResourcesQueryHandler(IResourceRepository repository) + : IQueryHandler> +{ + + /// + public virtual Task>> HandleAsync(GetResourcesQuery query, CancellationToken cancellationToken) + { + return Task.FromResult(this.Ok(repository.GetAllAsync(query.Group, query.Version, query.Plural, query.Namespace, query.LabelSelectors, cancellationToken))); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Queries/Resources/ListResourcesQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/ListResourcesQuery.cs new file mode 100644 index 000000000..11d7a65ad --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/ListResourcesQuery.cs @@ -0,0 +1,98 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources; + +/// +/// Represents the used to list all s +/// +public class ListResourcesQuery + : Query +{ + + /// + /// Initializes a new + /// + /// The API group the resources to list belongs to + /// The version of the resources to list + /// The plural name of the type of resources to list + /// The namespace the s to list belong to, if any + /// A containing the s used to filter s by + /// The maximum amount of results to list + /// The token, if any, used to continue enumerating the results + public ListResourcesQuery(string group, string version, string plural, string? @namespace, List? labelSelectors, ulong? maxResults, string? continuationToken) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Namespace = @namespace; + this.LabelSelectors = labelSelectors; + this.MaxResults = maxResults; + this.ContinuationToken = continuationToken; + } + + /// + /// Gets the API group the resources to list belongs to + /// + public string Group { get; } + + /// + /// Gets the version of the resources to list + /// + public string Version { get; } + + /// + /// Gets the plural name of the type of resources to list + /// + public string Plural { get; } + + /// + /// Gets the namespace the s to list belong to, if any + /// + public string? Namespace { get; } + + /// + /// Gets a containing the s used to filter s by + /// + public List? LabelSelectors { get; } + + /// + /// Gets the maximum amount of results to list + /// + public ulong? MaxResults { get; } + + /// + /// Gets the token, if any, used to continue enumerating the results + /// + public string? ContinuationToken { get; } + +} + +/// +/// Represents the service used to handle instances +/// +/// The service used to manage s +public class ListResourcesQueryHandler(IResourceRepository repository) + : IQueryHandler +{ + + /// + public virtual async Task> HandleAsync(ListResourcesQuery query, CancellationToken cancellationToken) + { + return this.Ok(await repository.ListAsync(query.Group, query.Version, query.Plural, query.Namespace, query.LabelSelectors, query.MaxResults, query.ContinuationToken, cancellationToken).ConfigureAwait(false)); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/Resources/WatchResourcesQuery.cs b/src/api/Synapse.Api.Application/Queries/Resources/WatchResourcesQuery.cs new file mode 100644 index 000000000..1fe0bec4b --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Resources/WatchResourcesQuery.cs @@ -0,0 +1,84 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Application.Queries.Resources; + +/// +/// Represents the used to retrieves s of the specified type +/// +public class WatchResourcesQuery + : Query +{ + + /// + /// Initializes a new + /// + /// The API group the resources to watch belongs to + /// The version of the resources to watch + /// The plural name of the type of resources to watch + /// The namespace the s to get belong to, if any + /// A containing the s used to filter s by + public WatchResourcesQuery(string group, string version, string plural, string? @namespace, List? labelSelectors) + { + if (string.IsNullOrWhiteSpace(group)) throw new ArgumentNullException(nameof(group)); + if (string.IsNullOrWhiteSpace(version)) throw new ArgumentNullException(nameof(version)); + if (string.IsNullOrWhiteSpace(plural)) throw new ArgumentNullException(nameof(plural)); + this.Group = group; + this.Version = version; + this.Plural = plural; + this.Namespace = @namespace; + this.LabelSelectors = labelSelectors; + } + + /// + /// Gets the API group the resources to watch belongs to + /// + public string Group { get; } + + /// + /// Gets the version of the resources to watch + /// + public string Version { get; } + + /// + /// Gets the plural name of the type of resources to watch + /// + public string Plural { get; } + + /// + /// Gets the namespace the s to get belong to, if any + /// + public string? Namespace { get; } + + /// + /// Gets a containing the s used to filter s by + /// + public List? LabelSelectors { get; } + +} + +/// +/// Represents the service used to handle instances +/// +/// The service used to manage s +public class WatchResourcesQueryHandler(IResourceRepository repository) + : IQueryHandler +{ + + /// + public virtual async Task> HandleAsync(WatchResourcesQuery query, CancellationToken cancellationToken) + { + return this.Ok(await repository.WatchAsync(query.Group, query.Version, query.Plural, query.Namespace, query.LabelSelectors, cancellationToken).ConfigureAwait(false)); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Queries/Users/GetUserProfileQuery.cs b/src/api/Synapse.Api.Application/Queries/Users/GetUserProfileQuery.cs new file mode 100644 index 000000000..777aaab3b --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/Users/GetUserProfileQuery.cs @@ -0,0 +1,63 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Security; +using Neuroglia.Security.Services; + +namespace Synapse.Api.Application.Queries.Users; + +/// +/// Represents the query used to get the current user's profile +/// +public class GetUserProfileQuery + : Query +{ + + +} + +/// +/// Represents the service used to handle instances +/// +/// The service used to describe the current user +public class GetUserProfileQueryHandler(IUserInfoProvider userInfoProvider) + : IQueryHandler +{ + + /// + /// Gets the service used to describe the current user + /// + protected IUserInfoProvider UserInfoProvider { get; } = userInfoProvider; + + /// + public virtual Task> HandleAsync(GetUserProfileQuery query, CancellationToken cancellationToken = default) + { + var userInfo = this.UserInfoProvider.GetCurrentUser(); + if (userInfo == null) return Task.FromResult(this.Forbidden()); + else return Task.FromResult(this.Ok(userInfo)); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/WorkflowInstances/ReadWorkflowInstanceLogsQuery.cs b/src/api/Synapse.Api.Application/Queries/WorkflowInstances/ReadWorkflowInstanceLogsQuery.cs new file mode 100644 index 000000000..34a8b8cc9 --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/WorkflowInstances/ReadWorkflowInstanceLogsQuery.cs @@ -0,0 +1,65 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.Services; +using Synapse.Resources; + +namespace Synapse.Api.Application.Queries.WorkflowInstances; + +/// +/// Represents the query used to read the logs of the specified workflow instance +/// +public class ReadWorkflowInstanceLogsQuery + : Query +{ + + /// + /// Initializes a new + /// + /// The name of the to read the logs of + /// The namespace the to read the logs of belongs to + public ReadWorkflowInstanceLogsQuery(string name, string? @namespace) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + this.Namespace = @namespace; + } + + /// + /// Gets the name of the to read the logs of + /// + public string Name { get; } + + /// + /// Gets the namespace the to read the logs of belongs to + /// + public string? Namespace { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage log documents +public class ReadWorkflowInstanceLogsQueryHandler(ITextDocumentRepository logDocuments) + : IQueryHandler +{ + + /// + public virtual async Task> HandleAsync(ReadWorkflowInstanceLogsQuery query, CancellationToken cancellationToken) + { + var logs = await logDocuments.ReadToEndAsync($"{query.Name}.{query.Namespace}", cancellationToken).ConfigureAwait(false); + return this.Ok(logs); + } + +} diff --git a/src/api/Synapse.Api.Application/Queries/WorkflowInstances/WatchWorkflowInstanceLogsQuery.cs b/src/api/Synapse.Api.Application/Queries/WorkflowInstances/WatchWorkflowInstanceLogsQuery.cs new file mode 100644 index 000000000..dcc83ea6f --- /dev/null +++ b/src/api/Synapse.Api.Application/Queries/WorkflowInstances/WatchWorkflowInstanceLogsQuery.cs @@ -0,0 +1,66 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure; +using Neuroglia.Data.Infrastructure.Services; +using Synapse.Resources; + +namespace Synapse.Api.Application.Queries.WorkflowInstances; + +/// +/// Represents the query used to watch the logs of a specified +/// +public class WatchWorkflowInstanceLogsQuery + : Query> +{ + + /// + /// Initializes a new + /// + /// The name of the to watch the logs of + /// The namespace the to watch the logs of belongs to + public WatchWorkflowInstanceLogsQuery(string name, string? @namespace) + { + if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); + this.Name = name; + this.Namespace = @namespace; + } + + /// + /// Gets the name of the to watch the logs of + /// + public string Name { get; } + + /// + /// Gets the namespace the to watch the logs of belongs to + /// + public string? Namespace { get; } + +} + +/// +/// Represents the service used to handle s +/// +/// The service used to manage log documents +public class WatchWorkflowInstanceLogsQueryHandler(ITextDocumentRepository logDocuments) + : IQueryHandler> +{ + + /// + public virtual async Task>> HandleAsync(WatchWorkflowInstanceLogsQuery query, CancellationToken cancellationToken) + { + var logs = await logDocuments.WatchAsync($"{query.Name}.{query.Namespace}", cancellationToken).ConfigureAwait(false); + return this.Ok(logs.ToAsyncEnumerable()); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/ServiceAccountSigningKey.cs b/src/api/Synapse.Api.Application/ServiceAccountSigningKey.cs new file mode 100644 index 000000000..132de7c36 --- /dev/null +++ b/src/api/Synapse.Api.Application/ServiceAccountSigningKey.cs @@ -0,0 +1,100 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.IdentityModel.Tokens; +using System.Security.Cryptography; + +namespace Synapse.Api.Application; + +/// +/// Expose static methods used to help manage the key used for signing service account JwTs +/// +public static class ServiceAccountSigningKey +{ + + const string KeyDirectory = "keys"; + const string PrivateKeyFile = "service_account_private.pem"; + const string PublicKeyFile = "service_account_public.pem"; + + /// + /// Creates the Service Account JWT signing key if it does not exist + /// + public static void Initialize() + { + var keyPath = Path.Combine(AppContext.BaseDirectory, KeyDirectory); + if (!Directory.Exists(keyPath)) Directory.CreateDirectory(keyPath); + var privateKeyPath = Path.Combine(keyPath, PrivateKeyFile); + var publicKeyPath = Path.Combine(keyPath, PublicKeyFile); + if (!File.Exists(privateKeyPath) || !File.Exists(publicKeyPath)) GenerateKeyPair(privateKeyPath, publicKeyPath); + } + + /// + /// Loads the private service account signing key + /// + /// The private service account signing key + public static SigningCredentials LoadPrivateKey() + { + var keyPath = Path.Combine(AppContext.BaseDirectory, KeyDirectory); + if (!Directory.Exists(keyPath)) Directory.CreateDirectory(keyPath); + var privateKeyPath = Path.Combine(keyPath, PrivateKeyFile); + var privateKey = File.ReadAllText(privateKeyPath) + .Replace("-----BEGIN PRIVATE KEY-----\n", string.Empty) + .Replace("-----END PRIVATE KEY-----", string.Empty) + .Replace("\n", string.Empty); + var privateKeyBytes = Convert.FromBase64String(privateKey); + var rsa = RSA.Create(); + rsa.ImportRSAPrivateKey(privateKeyBytes, out _); + return new SigningCredentials(new RsaSecurityKey(rsa), SecurityAlgorithms.RsaSha256); + } + + /// + /// Loads the public service account signing key + /// + /// The public service account signing key + public static SecurityKey LoadPublicKey() + { + var keyPath = Path.Combine(AppContext.BaseDirectory, KeyDirectory); + if (!Directory.Exists(keyPath)) Directory.CreateDirectory(keyPath); + var publicKeyPath = Path.Combine(keyPath, PublicKeyFile); + var publicKey = File.ReadAllText(publicKeyPath) + .Replace("-----BEGIN PUBLIC KEY-----\n", string.Empty) + .Replace("-----END PUBLIC KEY-----", string.Empty) + .Replace("\n", string.Empty); + var publicKeyBytes = Convert.FromBase64String(publicKey); + var rsa = RSA.Create(); + rsa.ImportRSAPublicKey(publicKeyBytes, out _); + return new RsaSecurityKey(rsa); + } + + static void GenerateKeyPair(string privateKeyPath, string publicKeyPath) + { + using var rsa = RSA.Create(2048); + var privateKey = ExportPrivateKey(rsa); + File.WriteAllText(privateKeyPath, privateKey); + var publicKey = ExportPublicKey(rsa); + File.WriteAllText(publicKeyPath, publicKey); + } + + static string ExportPrivateKey(RSA rsa) + { + var privateKeyBytes = rsa.ExportRSAPrivateKey(); + return $"-----BEGIN PRIVATE KEY-----\n{Convert.ToBase64String(privateKeyBytes, Base64FormattingOptions.InsertLineBreaks)}\n-----END PRIVATE KEY-----"; + } + + static string ExportPublicKey(RSA rsa) + { + var publicKeyBytes = rsa.ExportRSAPublicKey(); + return $"-----BEGIN PUBLIC KEY-----\n{Convert.ToBase64String(publicKeyBytes, Base64FormattingOptions.InsertLineBreaks)}\n-----END PUBLIC KEY-----"; + } + +} diff --git a/src/api/Synapse.Api.Application/Services/ServiceAccountClientStore.cs b/src/api/Synapse.Api.Application/Services/ServiceAccountClientStore.cs new file mode 100644 index 000000000..66a2a3437 --- /dev/null +++ b/src/api/Synapse.Api.Application/Services/ServiceAccountClientStore.cs @@ -0,0 +1,59 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using IdentityModel; +using IdentityServer4.Models; +using IdentityServer4.Stores; +using Synapse.Resources; + +namespace Synapse.Api.Application.Services; + +/// +/// Represents a -based implementation +/// +/// The service used to manage s +public class ServiceAccountClientStore(IResourceRepository resources) + : IClientStore +{ + + /// + /// Gets the service used to manage s + /// + protected IResourceRepository Resources { get; } = resources; + + /// + public virtual async Task FindClientByIdAsync(string clientId) + { + var qualifiedNameComponents = clientId.Split('.', StringSplitOptions.RemoveEmptyEntries); + if (qualifiedNameComponents.Length != 2) return null!; + var name = qualifiedNameComponents[0]; + var @namespace = qualifiedNameComponents[1]; + var serviceAccount = await Resources.GetAsync(name, @namespace); + if (serviceAccount == null) return null!; + return new() + { + AllowedGrantTypes = GrantTypes.ClientCredentials, + ClientId = clientId, + ClientSecrets = [new(serviceAccount.Spec.Key.Sha256())], + Claims = + [ + new ClientClaim(JwtClaimTypes.Name, clientId), + .. serviceAccount.Spec.Claims.Select(c => new ClientClaim(c.Key, c.Value)) + ], + AlwaysSendClientClaims = true, + ClientClaimsPrefix = null, + AllowedScopes = ["api"] + }; + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Services/ServiceAccountProfileService.cs b/src/api/Synapse.Api.Application/Services/ServiceAccountProfileService.cs new file mode 100644 index 000000000..eec076c7f --- /dev/null +++ b/src/api/Synapse.Api.Application/Services/ServiceAccountProfileService.cs @@ -0,0 +1,65 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using IdentityModel; +using IdentityServer4.Models; +using IdentityServer4.Services; +using Synapse.Resources; + +namespace Synapse.Api.Application.Services; + +/// +/// Represents a -based implementation +/// +/// The service used to manage s +public class ServiceAccountProfileService(IResourceRepository resources) + : IProfileService +{ + + /// + /// Gets the service used to manage s + /// + protected IResourceRepository Resources { get; } = resources; + + /// + public virtual async Task GetProfileDataAsync(ProfileDataRequestContext context) + { + if (context.Subject.Identity == null || !context.Subject.Identity.IsAuthenticated || string.IsNullOrWhiteSpace(context.Subject.Identity.Name)) return; + var qualifiedName = context.Subject.Identity.Name; + var qualifiedNameComponents = qualifiedName.Split('.', StringSplitOptions.RemoveEmptyEntries); + if (qualifiedNameComponents.Length != 2) return; + var name = qualifiedNameComponents[0]; + var @namespace = qualifiedNameComponents[1]; + var serviceAccount = await Resources.GetAsync(name, @namespace); + if (serviceAccount == null) return; + context.IssuedClaims = + [ + new(JwtClaimTypes.Subject, serviceAccount.GetQualifiedName()), + new(JwtClaimTypes.Name, serviceAccount.GetQualifiedName()) + ]; + foreach (var claim in serviceAccount.Spec.Claims) context.IssuedClaims.Add(new(claim.Key, claim.Value)); + } + + /// + public virtual async Task IsActiveAsync(IsActiveContext context) + { + if (context.Subject.Identity == null || !context.Subject.Identity.IsAuthenticated || string.IsNullOrWhiteSpace(context.Subject.Identity.Name)) return; + var qualifiedName = context.Subject.Identity.Name; + var qualifiedNameComponents = qualifiedName.Split('.', StringSplitOptions.RemoveEmptyEntries); + if (qualifiedNameComponents.Length != 2) return; + var name = qualifiedNameComponents[0]; + var @namespace = qualifiedNameComponents[1]; + context.IsActive = await Resources.GetAsync(name, @namespace) != null; + } + +} diff --git a/src/api/Synapse.Api.Application/Services/StaticBearerAuthenticationHandler.cs b/src/api/Synapse.Api.Application/Services/StaticBearerAuthenticationHandler.cs new file mode 100644 index 000000000..d028e4d79 --- /dev/null +++ b/src/api/Synapse.Api.Application/Services/StaticBearerAuthenticationHandler.cs @@ -0,0 +1,47 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Synapse.Api.Application.Configuration; +using System.Security.Claims; +using System.Text.Encodings.Web; + +namespace Synapse.Api.Application.Services; + +/// +/// Represents the service used to handle the application's static token authentication +/// +/// The service used to access the current +/// The service used to create s +/// The service used to encode URLs +public class StaticBearerAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : AuthenticationHandler(options, logger, encoder) +{ + + /// + protected override Task HandleAuthenticateAsync() + { + var authorization = this.Request.Headers.Authorization.ToString(); + if (string.IsNullOrWhiteSpace(authorization)) return Task.FromResult(AuthenticateResult.Fail("Authorization header not found")); + var token = this.Request.Headers.Authorization.ToString().Replace("Bearer ", ""); + if (this.Options.Tokens.TryGetValue(token, out var user)) + { + var principal = new ClaimsPrincipal(user); + var ticket = new AuthenticationTicket(principal, this.Scheme.Name); + return Task.FromResult(AuthenticateResult.Success(ticket)); + } + return Task.FromResult(AuthenticateResult.Fail("Invalid token")); + } +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Application/Synapse.Api.Application.csproj b/src/api/Synapse.Api.Application/Synapse.Api.Application.csproj new file mode 100644 index 000000000..029329d27 --- /dev/null +++ b/src/api/Synapse.Api.Application/Synapse.Api.Application.csproj @@ -0,0 +1,40 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse api application + true + true + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + + + + + + + + + + + + + diff --git a/src/api/Synapse.Api.Application/SynapseApiDefaults.cs b/src/api/Synapse.Api.Application/SynapseApiDefaults.cs new file mode 100644 index 000000000..b074690fc --- /dev/null +++ b/src/api/Synapse.Api.Application/SynapseApiDefaults.cs @@ -0,0 +1,103 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using IdentityModel; +using IdentityServer4.Models; + +namespace Synapse.Api; + +/// +/// Exposes constants used by the Synapse API +/// +public class SynapseApiDefaults +{ + + /// + /// Exposes constants about OIDC related constants + /// + public static class OpenIDConnect + { + + /// + /// Exposes the s used by the Synapse API + /// + public static class IdentityResources + { + /// + /// Gets the OpenID identity resource + /// + public static readonly IdentityResource OpenId = new IdentityServer4.Models.IdentityResources.OpenId(); + /// + /// Gets the Profile identity resource + /// + public static readonly IdentityResource Profile = new IdentityServer4.Models.IdentityResources.Profile(); + + /// + /// Gets an containing all default s + /// + /// A new containing all default s + public static IEnumerable AsEnumerable() + { + yield return OpenId; + yield return Profile; + } + + } + + /// + /// Exposes the OIDC resources used by the Synapse API + /// + public static class ApiResources + { + + /// + /// Gets the name identity resource + /// + public static readonly ApiResource Api = new("api", [JwtClaimTypes.Subject, JwtClaimTypes.Name, JwtClaimTypes.Email, JwtClaimTypes.Role, JwtClaimTypes.Roles]); + + /// + /// Gets an containing all default s + /// + /// A new containing all default s + public static IEnumerable AsEnumerable() + { + yield return Api; + } + + } + + /// + /// Exposes the OIDC scopes used by the Synapse API + /// + public static class ApiScopes + { + + /// + /// Gets the Synapse API scope + /// + public static readonly ApiScope Api = new("api", "Synapse API"); + + /// + /// Gets an containing all default API scopes + /// + /// A new containing all default API scopes + public static IEnumerable AsEnumerable() + { + yield return Api; + } + + } + + } + +} diff --git a/src/api/Synapse.Api.Application/Usings.cs b/src/api/Synapse.Api.Application/Usings.cs new file mode 100644 index 000000000..3c93ca7f7 --- /dev/null +++ b/src/api/Synapse.Api.Application/Usings.cs @@ -0,0 +1,19 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using Neuroglia; +global using Neuroglia.Data; +global using Neuroglia.Data.Infrastructure.ResourceOriented; +global using Neuroglia.Data.Infrastructure.ResourceOriented.Services; +global using Neuroglia.Mediation; +global using System.Net; \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Core/Services/ICloudEventApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/ICloudEventApiClient.cs new file mode 100644 index 000000000..e5e2b8287 --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/ICloudEventApiClient.cs @@ -0,0 +1,32 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Eventing.CloudEvents; + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of the Synapse API used to publish s +/// +public interface ICloudEventApiClient +{ + + /// + /// Publishes the specified + /// + /// The to publish + /// A + /// A new awaitable + Task PublishAsync(CloudEvent e, CancellationToken cancellationToken = default); + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Core/Services/IClusterResourceApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/IClusterResourceApiClient.cs new file mode 100644 index 000000000..19806bcb8 --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/IClusterResourceApiClient.cs @@ -0,0 +1,87 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data; + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a service used to manage cluster s +/// +/// The type of to manage +public interface IClusterResourceApiClient + : IResourceApiClient + where TResource : class, IResource, new() +{ + + /// + /// Lists s + /// + /// An containing the s used to select the s to list by, if any + /// A + /// A new used to asynchronously enumerate resulting s + Task> ListAsync(IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default); + + /// + /// Watches matching resources + /// + /// Defines the expected labels, if any, of the resources to watch + /// A + /// A new used to asynchronously enumerate resulting s + Task>> WatchAsync(IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default); + + /// + /// Monitors the resource with the specified name + /// + /// The name of the resource to monitor + /// A + /// A new used to asynchronously enumerate resulting s + Task>> MonitorAsync(string name, CancellationToken cancellationToken = default); + + /// + /// Gets the resource with the specified name + /// + /// The name of the resource to get + /// A + /// The resource with the specified name + Task GetAsync(string name, CancellationToken cancellationToken = default); + + /// + /// Patches the specified resource + /// + /// The name of the resource to patch + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// The patched resource + Task PatchAsync(string name, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default); + + /// + /// Patches the specified resource's status + /// + /// The name of the resource to patch the status of + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// The patched resource + Task PatchStatusAsync(string name, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default); + + /// + /// Deletes the specified resource + /// + /// The name of the resource to delete + /// A + /// A new awaitable + Task DeleteAsync(string name, CancellationToken cancellationToken = default); + +} diff --git a/src/api/Synapse.Api.Client.Core/Services/IDocumentApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/IDocumentApiClient.cs new file mode 100644 index 000000000..5d5cd87a5 --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/IDocumentApiClient.cs @@ -0,0 +1,56 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of the Synapse API used to manage s +/// +public interface IDocumentApiClient +{ + + /// + /// Creates a new + /// + /// The 's name + /// The 's content + /// A + /// The newly created + Task CreateAsync(string name, object content, CancellationToken cancellationToken = default); + + /// + /// Gets the with the specified id + /// + /// The id of the to get + /// A + /// The with the specified id + Task GetAsync(string id, CancellationToken cancellationToken = default); + + /// + /// Updates the contents of the with the specified id + /// + /// The id of the to update the content of + /// The 's content + /// A + /// The with the specified id + Task UpdateAsync(string id, object content, CancellationToken cancellationToken = default); + + /// + /// Deletes the with the specified id + /// + /// The id of the to delete + /// A + /// The with the specified id + Task DeletesAsync(string id, CancellationToken cancellationToken = default); + +} diff --git a/src/api/Synapse.Api.Client.Core/Services/INamespacedResourceApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/INamespacedResourceApiClient.cs new file mode 100644 index 000000000..2283fac9e --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/INamespacedResourceApiClient.cs @@ -0,0 +1,94 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data; + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a service used to manage namespaced s +/// +/// The type of to manage +public interface INamespacedResourceApiClient + : IResourceApiClient + where TResource : class, IResource, new() +{ + + /// + /// Lists s + /// + /// The namespace the s to list belong to + /// An containing the s used to select the s to list by, if any + /// A + /// A new used to asynchronously enumerate resulting s + Task> ListAsync(string? @namespace = null, IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default); + + /// + /// Watches matching resources + /// + /// The namespace the resources to watch belong to + /// Defines the expected labels, if any, of the resources to watch + /// A + /// A new used to asynchronously enumerate resulting s + Task>> WatchAsync(string? @namespace = null, IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default); + + /// + /// Monitors the resource with the specified name + /// + /// The name of the resource to monitor + /// The namespace the resource to monitor belongs to + /// A + /// A new used to asynchronously enumerate resulting s + Task>> MonitorAsync(string name, string @namespace, CancellationToken cancellationToken = default); + + /// + /// Gets the resource with the specified name + /// + /// The name of the resource to get + /// The namespace the resource to get belongs to + /// A + /// The resource with the specified name + Task GetAsync(string name, string @namespace, CancellationToken cancellationToken = default); + + /// + /// Patches the specified resource + /// + /// The name of the resource to patch + /// The namespace the resource to patch belongs to + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// The patched resource + Task PatchAsync(string name, string @namespace, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default); + + /// + /// Patches the specified resource's status + /// + /// The name of the resource to patch the status of + /// The namespace the resource to patch the status of belongs to + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// The patched resource + Task PatchStatusAsync(string name, string @namespace, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default); + + /// + /// Deletes the specified resource + /// + /// The name of the resource to delete + /// The namespace the resource to delete belongs to + /// A + /// A new awaitable + Task DeleteAsync(string name, string @namespace, CancellationToken cancellationToken = default); + +} diff --git a/src/api/Synapse.Api.Client.Core/Services/IResourceApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/IResourceApiClient.cs new file mode 100644 index 000000000..92190a8a2 --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/IResourceApiClient.cs @@ -0,0 +1,55 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a service used to manage s +/// +/// The type of to manage +public interface IResourceApiClient + where TResource : class, IResource, new() +{ + + /// + /// Creates a new resource + /// + /// The resource to create + /// A + /// The new resource + Task CreateAsync(TResource resource, CancellationToken cancellationToken = default); + + /// + /// Gets the definition of the resources managed by the API + /// + /// A + /// The definition of the resources managed by the API + Task GetDefinitionAsync(CancellationToken cancellationToken = default); + + /// + /// Replaces an existing resource + /// + /// The resource to replace + /// A + /// The replaced resource + Task ReplaceAsync(TResource resource, CancellationToken cancellationToken = default); + + /// + /// Replaces the status of an existing resource + /// + /// The resource to replace the status of + /// A + /// The replaced resource + Task ReplaceStatusAsync(TResource resource, CancellationToken cancellationToken = default); + +} diff --git a/src/api/Synapse.Api.Client.Core/Services/IResourceEventWatchHub.cs b/src/api/Synapse.Api.Client.Core/Services/IResourceEventWatchHub.cs new file mode 100644 index 000000000..e99b1aefe --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/IResourceEventWatchHub.cs @@ -0,0 +1,38 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a service used by clients to watch resource-related events +/// +public interface IResourceEventWatchHub +{ + + /// + /// Subscribes to events produced by resources of the specified type + /// + /// The type of resources to watch + /// The namespace the resources to watch belong to, if any + /// A new awaitable + Task Watch(ResourceDefinitionInfo resourceDefinition, string? @namespace = null); + + /// + /// Unsubscribes from events produced by resources of the specified type + /// + /// The type of resources to stop watching + /// The namespace the resources to stop watching belong to, if any + /// A new awaitable + Task StopWatching(ResourceDefinitionInfo resourceDefinition, string? @namespace = null); + +} diff --git a/src/api/Synapse.Api.Client.Core/Services/IResourceEventWatchHubClient.cs b/src/api/Synapse.Api.Client.Core/Services/IResourceEventWatchHubClient.cs new file mode 100644 index 000000000..c6016d2fb --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/IResourceEventWatchHubClient.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a service used by clients to watch resource-related events +/// +public interface IResourceEventWatchHubClient +{ + + /// + /// Notifies clients about a resource-related event + /// + /// The that has been produced + /// A new awaitable + Task ResourceWatchEvent(object e); + +} diff --git a/src/api/Synapse.Api.Client.Core/Services/ISynapseApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/ISynapseApiClient.cs new file mode 100644 index 000000000..4f7722fad --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/ISynapseApiClient.cs @@ -0,0 +1,74 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Eventing.CloudEvents; + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a Synapse API client +/// +public interface ISynapseApiClient +{ + + /// + /// Gets the Synapse API used to manage s + /// + INamespacedResourceApiClient Correlations { get; } + + /// + /// Gets the Synapse API used to manage s + /// + INamespacedResourceApiClient Correlators { get; } + + /// + /// Gets the Synapse API used to manage s + /// + IDocumentApiClient Documents { get; } + + /// + /// Gets the Synapse API used to produce s + /// + ICloudEventApiClient Events { get; } + + /// + /// Gets the Synapse API used to manage s + /// + IClusterResourceApiClient Namespaces { get; } + + /// + /// Gets the Synapse API used to manage s + /// + INamespacedResourceApiClient Operators { get; } + + /// + /// Gets the Synapse API used to manage s + /// + INamespacedResourceApiClient ServiceAccounts { get; } + + /// + /// Gets the Synapse API used to manage users + /// + IUserApiClient Users { get; } + + /// + /// Gets the Synapse API used to manage s + /// + INamespacedResourceApiClient Workflows { get; } + + /// + /// Gets the Synapse API used to manage s + /// + IWorkflowInstanceApiClient WorkflowInstances { get; } + +} diff --git a/src/api/Synapse.Api.Client.Core/Services/IUserApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/IUserApiClient.cs new file mode 100644 index 000000000..2da0ce72a --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/IUserApiClient.cs @@ -0,0 +1,31 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Security; + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a service used to manage users using the Synapse API +/// +public interface IUserApiClient +{ + + /// + /// Gets the current user's profile + /// + /// A + /// An object that describes the current user + Task GetUserProfileAsync(CancellationToken cancellationToken = default); + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Core/Services/IWorkflowInstanceApiClient.cs b/src/api/Synapse.Api.Client.Core/Services/IWorkflowInstanceApiClient.cs new file mode 100644 index 000000000..ac77c6e18 --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Services/IWorkflowInstanceApiClient.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure; + +namespace Synapse.Api.Client.Services; + +/// +/// Defines the fundamentals of a service used to manage s using the Synapse API +/// +public interface IWorkflowInstanceApiClient + : INamespacedResourceApiClient +{ + + /// + /// Reads the logs of the specified + /// + /// The name of the to read the logs of + /// The namespace the to read the logs of belongs to + /// A + /// The logs of the specified + Task ReadLogsAsync(string name, string @namespace, CancellationToken cancellationToken = default); + + /// + /// Watches the logs of the specified + /// + /// The name of the to watch the logs of + /// The namespace the to watch the logs of belongs to + /// A + /// A new used to watch the logs of the specified + Task> WatchLogsAsync(string name, string @namespace, CancellationToken cancellationToken = default); + +} diff --git a/src/api/Synapse.Api.Client.Core/Synapse.Api.Client.Core.csproj b/src/api/Synapse.Api.Client.Core/Synapse.Api.Client.Core.csproj new file mode 100644 index 000000000..9db51f597 --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Synapse.Api.Client.Core.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse api client core + true + true + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + + + + + + + diff --git a/src/api/Synapse.Api.Client.Core/Usings.cs b/src/api/Synapse.Api.Client.Core/Usings.cs new file mode 100644 index 000000000..532a75f18 --- /dev/null +++ b/src/api/Synapse.Api.Client.Core/Usings.cs @@ -0,0 +1,15 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using Synapse.Resources; +global using Neuroglia.Data.Infrastructure.ResourceOriented; \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Http/Configuration/SynapseHttpApiClientOptions.cs b/src/api/Synapse.Api.Client.Http/Configuration/SynapseHttpApiClientOptions.cs new file mode 100644 index 000000000..8bfee90c9 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Configuration/SynapseHttpApiClientOptions.cs @@ -0,0 +1,42 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Http.Configuration; + +/// +/// Represents the options used to configure a Synapse HTTP API client +/// +public class SynapseHttpApiClientOptions +{ + + /// + /// Initializes a new + /// + public SynapseHttpApiClientOptions() + { + var env = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Uri); + this.BaseAddress = string.IsNullOrWhiteSpace(env) ? null! : new(env, UriKind.RelativeOrAbsolute); + this.TokenFactory = provider => Task.FromResult(Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Api.Token)); + } + + /// + /// Gets/sets the base address of the Synapse API to connect to + /// + public virtual Uri BaseAddress { get; set; } + + /// + /// Gets/sets the function used to create a new token to authenticate on the Synapse API + /// + public virtual Func> TokenFactory { get; set; } + +} diff --git a/src/api/Synapse.Api.Client.Http/Extensions/HttpRequestMessageExtensions.cs b/src/api/Synapse.Api.Client.Http/Extensions/HttpRequestMessageExtensions.cs new file mode 100644 index 000000000..d4da8994e --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Extensions/HttpRequestMessageExtensions.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client; + +/// +/// Defines extensions for s +/// +public static class HttpRequestMessageExtensions +{ + + /// + /// Enables Web Assembly streaming response for the specified + /// + /// The to enable Web Assembly streaming response for + public static void EnableWebAssemblyStreamingResponse(this HttpRequestMessage request) => request.Options.Set(new("WebAssemblyEnableStreamingResponse"), true); + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Http/Extensions/IServiceCollectionExtensions.cs b/src/api/Synapse.Api.Client.Http/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 000000000..3abc7467d --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,51 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using ServerlessWorkflow.Sdk.IO; + +namespace Synapse.Api.Client; + +/// +/// Defines extensions for s +/// +public static class IServiceCollectionExtensions +{ + + /// + /// Adds and configures a client for the Synapse HTTP API + /// + /// The to configure + /// An used to configure the to use + /// The configured + public static IServiceCollection AddSynapseHttpApiClient(this IServiceCollection services, Action setup) + { + services.Configure(setup); + services.AddServerlessWorkflowIO(); + services.AddHttpClient((provider, http) => + { + var apiClientOptions = provider.GetRequiredService>().Value; + http.BaseAddress = apiClientOptions.BaseAddress; + }); + services.TryAddSingleton(provider => + { + var options = provider.GetRequiredService>().Value; + var connection = new HubConnectionBuilder() + .WithUrl(new Uri(options.BaseAddress, "api/ws/resources/watch")) + .WithAutomaticReconnect() + .Build(); + return new ResourceWatchEventHubClient(connection); + }); + return services; + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Http/Extensions/ISynapseApiClientExtensions.cs b/src/api/Synapse.Api.Client.Http/Extensions/ISynapseApiClientExtensions.cs new file mode 100644 index 000000000..3d702584e --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Extensions/ISynapseApiClientExtensions.cs @@ -0,0 +1,50 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia; + +namespace Synapse.Api.Client; + +/// +/// Defines extensions for s +/// +public static class ISynapseApiClientExtensions +{ + + /// + /// Gets the for the specified type + /// + /// The type of to get the for + /// The extended + /// The for the specified type + public static INamespacedResourceApiClient ManageNamespaced(this ISynapseApiClient client) + where TResource : class, IResource, new() + { + var apiProperty = client.GetType().GetProperties().SingleOrDefault(p => p.CanRead && typeof(INamespacedResourceApiClient<>).MakeGenericType(typeof(TResource)).IsAssignableFrom(p.PropertyType)) ?? throw new NullReferenceException($"Failed to find a management API for the specified resource type '{new TResource().Definition}'"); + return (INamespacedResourceApiClient)apiProperty.GetValue(client)!; + } + + /// + /// Gets the for the specified type + /// + /// The type of to get the for + /// The extended + /// The for the specified type + public static IClusterResourceApiClient ManageCluster(this ISynapseApiClient client) + where TResource : class, IResource, new() + { + var apiProperty = client.GetType().GetProperties().SingleOrDefault(p => p.CanRead && typeof(IClusterResourceApiClient<>).MakeGenericType(typeof(TResource)).IsAssignableFrom(p.PropertyType)) ?? throw new NullReferenceException($"Failed to find a management API for the specified resource type '{new TResource().Definition}'"); + return (IClusterResourceApiClient)apiProperty.GetValue(client)!; + } + +} diff --git a/src/api/Synapse.Api.Client.Http/Services/ApiClientBase.cs b/src/api/Synapse.Api.Client.Http/Services/ApiClientBase.cs new file mode 100644 index 000000000..e00663e6b --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/ApiClientBase.cs @@ -0,0 +1,88 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the base class for all API client implementations +/// +/// The current +/// The service used to perform logging +/// The service used to serialize/deserialize objects to/from JSON +/// The service used to access the current +/// The service used to perform HTTP requests +public abstract class ApiClientBase(IServiceProvider serviceProvider, ILogger logger, IJsonSerializer jsonSerializer, IOptions options, HttpClient httpClient) +{ + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider { get; } = serviceProvider; + + /// + /// Gets the service used to perform logging + /// + protected ILogger Logger { get; } = logger; + + /// + /// Gets the service used to serialize/deserialize objects to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the current + /// + protected SynapseHttpApiClientOptions Options { get; } = options.Value; + + /// + /// Gets the service used to perform HTTP requests + /// + protected HttpClient HttpClient { get; } = httpClient; + + /// + /// Processes the specified before sending it + /// + /// the to process + /// A + /// The processed + protected virtual async Task ProcessRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + if (request.Headers.Authorization != null) return request; + var token = await this.Options.TokenFactory(this.ServiceProvider).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(token)) request.Headers.Authorization = new("Bearer", token); + return request; + } + + /// + /// Processes the specified + /// + /// the to process + /// A + /// The processed + protected virtual async Task ProcessResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(response); + if (response.IsSuccessStatusCode) return response; + var content = string.Empty; + if (response.Content != null) content = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + this.Logger.LogError("The remote server responded with a non-success status code '{statusCode}': {errorDetails}", response.StatusCode, content); + if (!response.IsSuccessStatusCode) + { + if (string.IsNullOrWhiteSpace(content)) response.EnsureSuccessStatusCode(); + else throw new ProblemDetailsException(this.JsonSerializer.Deserialize(content)!); + } + return response; + } + +} diff --git a/src/api/Synapse.Api.Client.Http/Services/CloudEventHttpApiClient.cs b/src/api/Synapse.Api.Client.Http/Services/CloudEventHttpApiClient.cs new file mode 100644 index 000000000..5b9ba2cb5 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/CloudEventHttpApiClient.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Http.Services; + +/// +/// Represents the default HTTP implementation of the interface +/// +/// The current +/// The service used to perform logging +/// The service used to serialize/deserialize objects to/from JSON +/// The service used to access the current +/// The service used to perform HTTP requests +public class CloudEventHttpApiClient(IServiceProvider serviceProvider, ILogger logger, IJsonSerializer jsonSerializer, IOptions options, HttpClient httpClient) + : ApiClientBase(serviceProvider, logger, jsonSerializer, options, httpClient), ICloudEventApiClient +{ + + const string PathPrefix = "api/v1/events"; + + /// + public virtual async Task PublishAsync(CloudEvent e, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(e); + var uri = PathPrefix; + var json = this.JsonSerializer.SerializeToText(e); + using var content = new StringContent(json, Encoding.UTF8, CloudEventContentType.Json); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Post, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + } + +} diff --git a/src/api/Synapse.Api.Client.Http/Services/DocumentHttpApiClient.cs b/src/api/Synapse.Api.Client.Http/Services/DocumentHttpApiClient.cs new file mode 100644 index 000000000..6a9f14165 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/DocumentHttpApiClient.cs @@ -0,0 +1,79 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the default HTTP implementation of the interface +/// +/// The current +/// The service used to perform logging +/// The service used to serialize/deserialize objects to/from JSON +/// The service used to access the current +/// The service used to perform HTTP requests +public class DocumentHttpApiClient(IServiceProvider serviceProvider, ILogger logger, IJsonSerializer jsonSerializer, IOptions options, HttpClient httpClient) + : ApiClientBase(serviceProvider, logger, jsonSerializer, options, httpClient), IDocumentApiClient +{ + + const string PathPrefix = "api/v1/documents"; + + /// + public virtual async Task CreateAsync(string documentName, object documentContent, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(documentName); + var document = new Document() + { + Name = documentName, + Content = documentContent + }; + var json = this.JsonSerializer.SerializeToText(document); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + var uri = PathPrefix; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Post, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task GetAsync(string id, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + var uri = $"{PathPrefix}/{id}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task UpdateAsync(string id, object content, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + var json = this.JsonSerializer.SerializeToText(content); + using var requestContent = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + var uri = $"{PathPrefix}/{id}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Put, uri) { Content = requestContent }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + } + + /// + public virtual async Task DeletesAsync(string id, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + var uri = $"{PathPrefix}/{id}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Delete, uri), cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + } + +} diff --git a/src/api/Synapse.Api.Client.Http/Services/ResourceHttpApiClient.cs b/src/api/Synapse.Api.Client.Http/Services/ResourceHttpApiClient.cs new file mode 100644 index 000000000..8ed809e38 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/ResourceHttpApiClient.cs @@ -0,0 +1,277 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the HTTP implementation of the interface +/// +/// The current +/// The service used to perform logging +/// The service used to access the current +/// The service used to serialize/deserialize objects to/from JSON +/// The service used to perform HTTP requests +public class ResourceHttpApiClient(IServiceProvider serviceProvider, ILogger> logger, IOptions options, IJsonSerializer jsonSerializer, HttpClient httpClient) + : ApiClientBase(serviceProvider, logger, jsonSerializer, options, httpClient), IClusterResourceApiClient, INamespacedResourceApiClient + where TResource : class, IResource, new() +{ + + /// + public virtual async Task CreateAsync(TResource resource, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(resource); + var json = this.JsonSerializer.SerializeToText(resource); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Post, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task GetAsync(string name, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{name}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task GetAsync(string name, string @namespace, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task GetDefinitionAsync(CancellationToken cancellationToken = default) + { + var resource = new TResource(); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/definition"), cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task> ListAsync(string? @namespace = null, IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default) + { + var resource = new TResource(); + var uri = string.IsNullOrWhiteSpace(@namespace) ? $"/api/{resource.Definition.Version}/{resource.Definition.Plural}" : $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}"; + var queryStringArguments = new Dictionary(); + if (labelSelectors?.Any() == true) queryStringArguments.Add("labelSelector", labelSelectors.Select(s => s.ToString()).Join(',')); + if (queryStringArguments.Count != 0) uri += $"?{queryStringArguments.Select(kvp => $"{kvp.Key}={kvp.Value}").Join('&')}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + request.EnableWebAssemblyStreamingResponse(); + var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken)!; + } + + /// + public virtual async Task> ListAsync(IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default) + { + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}"; + var queryStringArguments = new Dictionary(); + if (labelSelectors?.Any() == true) queryStringArguments.Add("labelSelector", labelSelectors.Select(s => s.ToString()).Join(',')); + if (queryStringArguments.Count != 0) uri += $"?{queryStringArguments.Select(kvp => $"{kvp.Key}={kvp.Value}").Join('&')}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + request.EnableWebAssemblyStreamingResponse(); + var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.DeserializeAsyncEnumerable(responseStream, cancellationToken: cancellationToken)!; + } + + /// + public virtual async Task>> WatchAsync(string? @namespace = null, IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default) + { + var resource = new TResource(); + var uri = string.IsNullOrWhiteSpace(@namespace) ? $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/watch" : $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/watch"; + var queryStringArguments = new Dictionary(); + if (labelSelectors?.Any() == true) queryStringArguments.Add("labelSelector", labelSelectors.Select(s => s.ToString()).Join(',')); + if (queryStringArguments.Count != 0) uri += $"?{queryStringArguments.Select(kvp => $"{kvp.Key}={kvp.Value}").Join('&')}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + request.EnableWebAssemblyStreamingResponse(); + var response = await this.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.DeserializeAsyncEnumerable>(responseStream, cancellationToken)!; + } + + /// + public virtual async Task>> WatchAsync(IEnumerable? labelSelectors = null, CancellationToken cancellationToken = default) + { + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/watch"; + var queryStringArguments = new Dictionary(); + if (labelSelectors?.Any() == true) queryStringArguments.Add("labelSelector", labelSelectors.Select(s => s.ToString()).Join(',')); + if (queryStringArguments.Count != 0) uri += $"?{queryStringArguments.Select(kvp => $"{kvp.Key}={kvp.Value}").Join('&')}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + request.EnableWebAssemblyStreamingResponse(); + var response = await this.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.DeserializeAsyncEnumerable>(responseStream, cancellationToken)!; + } + + /// + public virtual async Task>> MonitorAsync(string name, string @namespace, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}/monitor"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + request.EnableWebAssemblyStreamingResponse(); + var response = await this.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.DeserializeAsyncEnumerable>(responseStream, cancellationToken)!; + } + + /// + public virtual async Task>> MonitorAsync(string name, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{name}/monitor"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + request.EnableWebAssemblyStreamingResponse(); + var response = await this.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.DeserializeAsyncEnumerable>(responseStream, cancellationToken)!; + } + + /// + public virtual async Task PatchAsync(string name, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentNullException.ThrowIfNull(patch); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{name}"; + if (!string.IsNullOrWhiteSpace(resourceVersion)) uri += $"?resourceVersion={resourceVersion}"; + var json = this.JsonSerializer.SerializeToText(patch); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task PatchAsync(string name, string @namespace, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + ArgumentNullException.ThrowIfNull(patch); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}"; + if (!string.IsNullOrWhiteSpace(resourceVersion)) uri += $"?resourceVersion={resourceVersion}"; + var json = this.JsonSerializer.SerializeToText(patch); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task PatchStatusAsync(string name, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentNullException.ThrowIfNull(patch); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{name}/status"; + if (!string.IsNullOrWhiteSpace(resourceVersion)) uri += $"?resourceVersion={resourceVersion}"; + var json = this.JsonSerializer.SerializeToText(patch); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task PatchStatusAsync(string name, string @namespace, Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + ArgumentNullException.ThrowIfNull(patch); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}/status"; + if (!string.IsNullOrWhiteSpace(resourceVersion)) uri += $"?resourceVersion={resourceVersion}"; + var json = this.JsonSerializer.SerializeToText(patch); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Patch, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task ReplaceAsync(TResource resource, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(resource); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{resource.GetNamespace()!}/{resource.GetName()}"; + var json = this.JsonSerializer.SerializeToText(resource); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Put, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task ReplaceStatusAsync(TResource resource, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(resource); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{resource.GetNamespace()!}/{resource.GetName()}/status"; + var json = this.JsonSerializer.SerializeToText(resource); + using var content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Put, uri) { Content = content }, cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + + /// + public virtual async Task DeleteAsync(string name, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{name}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Delete, uri), cancellationToken).ConfigureAwait(false); + await ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + } + + /// + public virtual async Task DeleteAsync(string name, string @namespace, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + var resource = new TResource(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Delete, uri), cancellationToken).ConfigureAwait(false); + await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + } + +} diff --git a/src/api/Synapse.Api.Client.Http/Services/ResourceWatch.cs b/src/api/Synapse.Api.Client.Http/Services/ResourceWatch.cs new file mode 100644 index 000000000..b3f172c63 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/ResourceWatch.cs @@ -0,0 +1,74 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the service used to handle a resource watch event subscription +/// +/// The type of resources to watch +/// +/// Initializes a new +/// +/// The service used to interact with the +/// The namespace watched resources belong to, if any +/// The used to watch resources of the specified type +public class ResourceWatch(ResourceWatchEventHubClient resourceWatchEventHub, string? resourceNamespace, IObservable> stream) + : IObservable>, IAsyncDisposable + where TResource : class, IResource, new() +{ + + private bool _Disposed; + + /// + /// Gets the service used to interact with the + /// + protected ResourceWatchEventHubClient ResourceWatchEventHub { get; } = resourceWatchEventHub ?? throw new ArgumentNullException(nameof(resourceWatchEventHub)); + + /// + /// Gets the namespace watched resources belong to, if any + /// + protected virtual string? ResourceNamespace { get; } = resourceNamespace; + + /// + /// Gets the used to watch resources of the specified type + /// + protected IObservable> Stream { get; } = stream; + + /// + public IDisposable Subscribe(IObserver> observer) => this.Stream.Subscribe(observer); + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + protected virtual async ValueTask DisposeAsync(bool disposing) + { + if (!this._Disposed) + { + if (disposing) + { + await this.ResourceWatchEventHub.StopWatchingAsync(); + } + this._Disposed = true; + } + } + + /// + public async ValueTask DisposeAsync() + { + await this.DisposeAsync(disposing: true).ConfigureAwait(false); + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Http/Services/ResourceWatchEventHubClient.cs b/src/api/Synapse.Api.Client.Http/Services/ResourceWatchEventHubClient.cs new file mode 100644 index 000000000..432f71187 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/ResourceWatchEventHubClient.cs @@ -0,0 +1,106 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the service used to listen to s in real-time +/// +public class ResourceWatchEventHubClient + : IAsyncDisposable +{ + + bool _disposed; + + /// + /// Initializes a new + /// + /// The underlying + public ResourceWatchEventHubClient(HubConnection connection) + { + this.Connection = connection; + this.Connection.On(nameof(IResourceEventWatchHubClient.ResourceWatchEvent), this.WatchEventStream.OnNext); + } + + /// + /// Gets the underlying + /// + protected HubConnection Connection { get; } + + /// + /// Gets the used to notify subscribers about incoming s + /// + protected Subject WatchEventStream { get; } = new(); + + /// + /// Starts the if it's not already running + /// + /// A new awaitable + public virtual Task StartAsync() => this.Connection.State == HubConnectionState.Disconnected ? this.Connection.StartAsync() : Task.CompletedTask; + + /// + /// Watches resource of the specified type + /// + /// The type of resources to watch + /// The namespace resources to watch belong to + /// A + /// A new used to observe incoming s for resources of the specified type + public virtual async Task> WatchAsync(string? @namespace = null, CancellationToken cancellationToken = default) + where TResource : class, IResource, new() + { + var resource = new TResource(); + await this.Connection.InvokeAsync(nameof(IResourceEventWatchHub.Watch), resource.Definition, @namespace, cancellationToken).ConfigureAwait(false); + var stream = this.WatchEventStream + .Where(e => e.Resource.IsOfType()) + .Select(e => e.OfType()); + if(!string.IsNullOrWhiteSpace(@namespace)) stream = stream.Where(e => e.Resource.GetNamespace() == @namespace); + return new(this, @namespace, stream); + } + + /// + /// Stops watching resources of the specified type + /// + /// The type of resources to stop watching + /// The namespace resources to stop watching belong to + /// A + /// A new awaitable + public virtual async Task StopWatchingAsync(string? @namespace = null, CancellationToken cancellationToken = default) + where TResource : class, IResource, new() + { + var resource = new TResource(); + await this.Connection.InvokeAsync(nameof(IResourceEventWatchHub.StopWatching), resource.Definition, @namespace, cancellationToken).ConfigureAwait(false); + } + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + protected virtual async ValueTask DisposeAsync(bool disposing) + { + if (this._disposed) return; + if (disposing) + { + this.WatchEventStream.Dispose(); + await this.Connection.DisposeAsync(); + } + this._disposed = true; + } + + /// + public async ValueTask DisposeAsync() + { + await this.DisposeAsync(disposing: true); + GC.SuppressFinalize(this); + } + +} diff --git a/src/api/Synapse.Api.Client.Http/Services/SynapseHttpApiClient.cs b/src/api/Synapse.Api.Client.Http/Services/SynapseHttpApiClient.cs new file mode 100644 index 000000000..4380e5ce7 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/SynapseHttpApiClient.cs @@ -0,0 +1,101 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Http.Services; + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the default HTTP implementation of the interface +/// +public class SynapseHttpApiClient + : ISynapseApiClient +{ + + /// + /// Initializes a new + /// + /// The current + /// The service used to create s + /// The service used to serialize/deserialize objects to/from JSON + /// The service used to perform http requests + public SynapseHttpApiClient(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IJsonSerializer serializer, HttpClient httpClient) + { + this.ServiceProvider = serviceProvider; + this.Logger = loggerFactory.CreateLogger(GetType()); + this.Serializer = serializer; + this.HttpClient = httpClient; + this.Documents = ActivatorUtilities.CreateInstance(this.ServiceProvider, this.HttpClient); + this.Events = ActivatorUtilities.CreateInstance(this.ServiceProvider, this.HttpClient); + foreach (var apiProperty in GetType().GetProperties().Where(p => p.CanRead && p.PropertyType.GetGenericType(typeof(IResourceApiClient<>)) != null && p.Name != nameof(WorkflowInstances))) + { + var apiType = apiProperty.PropertyType.GetGenericType(typeof(IResourceApiClient<>))!; + var resourceType = apiType.GetGenericArguments()[0]; + var api = ActivatorUtilities.CreateInstance(this.ServiceProvider, typeof(ResourceHttpApiClient<>).MakeGenericType(resourceType), this.HttpClient); + apiProperty.SetValue(this, api); + } + this.WorkflowInstances = ActivatorUtilities.CreateInstance(this.ServiceProvider, this.HttpClient); + this.Users = ActivatorUtilities.CreateInstance(this.ServiceProvider, this.HttpClient); + } + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider { get; } + + /// + /// Gets the service used to perform logging + /// + protected ILogger Logger { get; } + + /// + /// Gets the service used to serialize/deserialize objects to/from JSON + /// + protected IJsonSerializer Serializer { get; } + + /// + /// Gets the service used to perform http requests + /// + protected HttpClient HttpClient { get; } + + /// + public INamespacedResourceApiClient Correlations { get; private set; } = null!; + + /// + public INamespacedResourceApiClient Correlators { get; private set; } = null!; + + /// + public IDocumentApiClient Documents { get; } + + /// + public ICloudEventApiClient Events { get; } + + /// + public IClusterResourceApiClient Namespaces { get; private set; } = null!; + + /// + public INamespacedResourceApiClient Operators { get; private set; } = null!; + + /// + public INamespacedResourceApiClient ServiceAccounts { get; private set; } = null!; + + /// + public IUserApiClient Users { get; } + + /// + public INamespacedResourceApiClient Workflows { get; private set; } = null!; + + /// + public IWorkflowInstanceApiClient WorkflowInstances { get; private set; } = null!; + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Http/Services/UserHttpApiClient.cs b/src/api/Synapse.Api.Client.Http/Services/UserHttpApiClient.cs new file mode 100644 index 000000000..32786a5c0 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/UserHttpApiClient.cs @@ -0,0 +1,40 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Security; + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the default implementation of the +/// +/// The current +/// The service used to perform logging +/// The service used to serialize/deserialize objects to/from JSON +/// The service used to access the current +/// The service used to perform HTTP requests +public class UserHttpApiClient(IServiceProvider serviceProvider, ILogger logger, IJsonSerializer jsonSerializer, IOptions options, HttpClient httpClient) + : ApiClientBase(serviceProvider, logger, jsonSerializer, options, httpClient), IUserApiClient +{ + + /// + public virtual async Task GetUserProfileAsync(CancellationToken cancellationToken = default) + { + var uri = $"api/v1/users/profile"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + var json = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.Deserialize(json)!; + } + +} diff --git a/src/api/Synapse.Api.Client.Http/Services/WorkflowInstanceHttpApiClient.cs b/src/api/Synapse.Api.Client.Http/Services/WorkflowInstanceHttpApiClient.cs new file mode 100644 index 000000000..7e721a5c1 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Services/WorkflowInstanceHttpApiClient.cs @@ -0,0 +1,52 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure; + +namespace Synapse.Api.Client.Services; + +/// +/// Represents the default HTTP implementation of the interface +/// +/// +public class WorkflowInstanceHttpApiClient(IServiceProvider serviceProvider, ILogger> logger, IOptions options, IJsonSerializer jsonSerializer, HttpClient httpClient) + : ResourceHttpApiClient(serviceProvider, logger, options, jsonSerializer, httpClient), IWorkflowInstanceApiClient +{ + + /// + public virtual async Task ReadLogsAsync(string name, string @namespace, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + var resource = new WorkflowInstance(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}/logs"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + using var response = await this.ProcessResponseAsync(await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + return await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } + + /// + public virtual async Task> WatchLogsAsync(string name, string @namespace, CancellationToken cancellationToken = default) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + var resource = new WorkflowInstance(); + var uri = $"/api/{resource.Definition.Version}/{resource.Definition.Plural}/{@namespace}/{name}/logs/watch"; + using var request = await this.ProcessRequestAsync(new HttpRequestMessage(HttpMethod.Get, uri), cancellationToken).ConfigureAwait(false); + request.EnableWebAssemblyStreamingResponse(); + var response = await this.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + return this.JsonSerializer.DeserializeAsyncEnumerable(responseStream, cancellationToken)!; + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj b/src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj new file mode 100644 index 000000000..6d4d3f91a --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Synapse.Api.Client.Http.csproj @@ -0,0 +1,41 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse api client http + true + true + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + + + + + + + + + + + + + + diff --git a/src/api/Synapse.Api.Client.Http/Usings.cs b/src/api/Synapse.Api.Client.Http/Usings.cs new file mode 100644 index 000000000..3e148d446 --- /dev/null +++ b/src/api/Synapse.Api.Client.Http/Usings.cs @@ -0,0 +1,30 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using Microsoft.AspNetCore.SignalR.Client; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Neuroglia; +global using Neuroglia.Data; +global using Neuroglia.Data.Infrastructure.ResourceOriented; +global using Neuroglia.Eventing.CloudEvents; +global using Neuroglia.Serialization; +global using Synapse.Api.Client.Http.Configuration; +global using Synapse.Api.Client.Services; +global using Synapse.Resources; +global using System.Net.Mime; +global using System.Reactive.Linq; +global using System.Reactive.Subjects; +global using System.Text; diff --git a/src/api/Synapse.Api.Http/ClusterResourceController.cs b/src/api/Synapse.Api.Http/ClusterResourceController.cs new file mode 100644 index 000000000..9b7b356e0 --- /dev/null +++ b/src/api/Synapse.Api.Http/ClusterResourceController.cs @@ -0,0 +1,208 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http; + +/// +/// Represents the base class of a used to manage cluster s +/// +/// The type of to manage +/// The service used to mediate calls +/// The service used to serialize/deserialize data to/from JSON +public abstract class ClusterResourceController(IMediator mediator, IJsonSerializer jsonSerializer) + : ResourceController(mediator) + where TResource : class, IResource, new() +{ + + /// + /// Gets the service used to serialize/deserialize data to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the resource with the specified name + /// + /// The name of the resource to get + /// A + /// A new + [HttpGet("{name}")] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.Created)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public async Task GetResource(string name, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new GetResourceQuery(name, null), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Lists matching resources + /// + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet] + [ProducesResponseType(typeof(Collection), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task GetResources(string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + return this.Process(await this.Mediator.ExecuteAsync(new GetResourcesQuery(null, labelSelectors), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Lists matching resources + /// + /// A comma-separated list of label selectors, if any + /// The maximum amount, if any, of results to list at once + /// A token, defined by a previously retrieved collection, used to continue enumerating through matches + /// A + /// A new + [HttpGet("list")] + [ProducesResponseType(typeof(Collection), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task ListResources(string? labelSelector = null, ulong? maxResults = null, string? continuationToken = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + return this.Process(await this.Mediator.ExecuteAsync(new ListResourcesQuery(null, labelSelectors, maxResults, continuationToken), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Watches matching resources + /// + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet("watch")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task>> WatchResources(string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) throw new Exception($"Invalid label selector '{labelSelector}'"); + var response = await this.Mediator.ExecuteAsync(new WatchResourcesQuery(null, labelSelectors), cancellationToken).ConfigureAwait(false); + return response.Data!; + } + + /// + /// Watches matching resources + /// + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet("watch/sse")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task WatchResourcesUsingSSE(string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + var response = await this.Mediator.ExecuteAsync(new WatchResourcesQuery(null, labelSelectors), cancellationToken).ConfigureAwait(false); + this.Response.Headers.ContentType = "text/event-stream"; + this.Response.Headers.CacheControl = "no-cache"; + this.Response.Headers.Connection = "keep-alive"; + await foreach (var e in response.Data!) + { + var sseMessage = $"data: {this.JsonSerializer.SerializeToText(e)}\\n\\n"; + await this.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(sseMessage), cancellationToken).ConfigureAwait(false); + await this.Response.Body.FlushAsync(cancellationToken).ConfigureAwait(false); + } + return this.Ok(); + } + + /// + /// Monitors a specific resource + /// + /// The name of the resource to monitor + /// A + /// A new + [HttpGet("{name}/monitor")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task>> MonitorResource(string name, CancellationToken cancellationToken = default) + { + var response = await this.Mediator.ExecuteAsync(new MonitorResourceQuery(name, null), cancellationToken).ConfigureAwait(false); + return response.Data!; + } + + /// + /// Monitors a specific resource + /// + /// The name of the cluster resource to monitor + /// A + /// A new + [HttpGet("{name}/monitor/sse")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task MonitorResourceUsingSSE(string name, CancellationToken cancellationToken = default) + { + var response = await this.Mediator.ExecuteAsync(new MonitorResourceQuery(name, null), cancellationToken).ConfigureAwait(false); + this.Response.Headers.ContentType = "text/event-stream"; + this.Response.Headers.CacheControl = "no-cache"; + this.Response.Headers.Connection = "keep-alive"; + await foreach (var e in response.Data!) + { + var sseMessage = $"data: {this.JsonSerializer.SerializeToText(e)}\\n\\n"; + await this.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(sseMessage), cancellationToken).ConfigureAwait(false); + await this.Response.Body.FlushAsync(cancellationToken).ConfigureAwait(false); + } + return this.Ok(); + } + + /// + /// Patches the specified resource + /// + /// The name of the resource to patch + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// A new + [HttpPatch("{namespace}/{name}")] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task PatchResource(string name, [FromBody] Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new PatchResourceCommand(name, null, patch, resourceVersion), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Patches the specified resource's status + /// + /// The name of the resource to patch the status of + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// A new + [HttpPatch("{namespace}/{name}/status")] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task PatchResourceStatus(string name, [FromBody] Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new PatchResourceStatusCommand(name, null, patch, resourceVersion), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Deletes the resource with the specified name + /// + /// The name of the resource to delete + /// A + /// A new + [HttpDelete("{name}")] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.Created)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public async Task DeleteResource(string name, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new DeleteResourceCommand(name, null), cancellationToken).ConfigureAwait(false)); + } + +} diff --git a/src/api/Synapse.Api.Http/Controllers/CorrelationsController.cs b/src/api/Synapse.Api.Http/Controllers/CorrelationsController.cs new file mode 100644 index 000000000..15e00b435 --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/CorrelationsController.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +/// The service used to serialize/deserialize objects to/from JSON +[Route("api/v1/correlations")] +public class CorrelationsController(IMediator mediator, IJsonSerializer jsonSerializer) + : NamespacedResourceController(mediator, jsonSerializer) +{ + + + +} diff --git a/src/api/Synapse.Api.Http/Controllers/CorrelatorsController.cs b/src/api/Synapse.Api.Http/Controllers/CorrelatorsController.cs new file mode 100644 index 000000000..dcc701f50 --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/CorrelatorsController.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +/// The service used to serialize/deserialize objects to/from JSON +[Route("api/v1/correlators")] +public class CorrelatorsController(IMediator mediator, IJsonSerializer jsonSerializer) + : NamespacedResourceController(mediator, jsonSerializer) +{ + + + +} diff --git a/src/api/Synapse.Api.Http/Controllers/DocumentsController.cs b/src/api/Synapse.Api.Http/Controllers/DocumentsController.cs new file mode 100644 index 000000000..383356983 --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/DocumentsController.cs @@ -0,0 +1,70 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Application.Commands.Documents; +using Synapse.Api.Application.Queries.Documents; + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +[Route("api/v1/documents")] +public class DocumentsController(IMediator mediator) + : Controller +{ + + /// + /// Gets the service used to mediate calls + /// + protected IMediator Mediator { get; } = mediator; + + /// + /// Creates a new document + /// + /// The document to create + /// A + /// A new that describes the result of the operation + [HttpPost] + public async Task CreateDocument([FromBody]Document document, CancellationToken cancellationToken = default) + { + return this.Process(await this.Mediator.ExecuteAsync(new CreateDocumentCommand(document), cancellationToken).ConfigureAwait(false), (int)HttpStatusCode.Created); + } + + /// + /// Gets the document with the specified id + /// + /// The id of the document to get + /// A + /// A new that describes the result of the operation + [HttpGet("{id}")] + public async Task GetDocument(string id, CancellationToken cancellationToken = default) + { + return this.Process(await this.Mediator.ExecuteAsync(new GetDocumentQuery(id), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Updates the contents of the document with the specified id + /// + /// The id of the document to update + /// The document's updated content + /// A + /// A new that describes the result of the operation + [HttpPut("{id}")] + public async Task UpdateDocument(string id, [FromBody]object content, CancellationToken cancellationToken = default) + { + return this.Process(await this.Mediator.ExecuteAsync(new UpdateDocumentCommand(id, content), cancellationToken).ConfigureAwait(false)); + } + +} diff --git a/src/api/Synapse.Api.Http/Controllers/EventsController.cs b/src/api/Synapse.Api.Http/Controllers/EventsController.cs new file mode 100644 index 000000000..d6baab7dc --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/EventsController.cs @@ -0,0 +1,45 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Eventing.CloudEvents; +using Synapse.Api.Application.Commands.Events; + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +[Route("api/v1/events")] +public class EventsController(IMediator mediator) + : Controller +{ + + /// + /// Gets the service used to mediate calls + /// + protected IMediator Mediator { get; } = mediator; + + /// + /// Publishes the specified cloud event + /// + /// The cloud event to publish + /// A + /// A new that describes the result of the operation + [HttpPost] + public async Task PublishEvent([FromBody]CloudEvent e, CancellationToken cancellationToken = default) + { + return this.Process(await this.Mediator.ExecuteAsync(new PublishCloudEventCommand(e)).ConfigureAwait(false), (int)HttpStatusCode.Accepted); + } + +} diff --git a/src/api/Synapse.Api.Http/Controllers/NamespacesController.cs b/src/api/Synapse.Api.Http/Controllers/NamespacesController.cs new file mode 100644 index 000000000..c7152a234 --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/NamespacesController.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +/// The service used to serialize/deserialize objects to/from JSON +[Route("api/v1/namespaces")] +public class NamespacesController(IMediator mediator, IJsonSerializer jsonSerializer) + : ClusterResourceController(mediator, jsonSerializer) +{ + + + +} diff --git a/src/api/Synapse.Api.Http/Controllers/OperatorsController.cs b/src/api/Synapse.Api.Http/Controllers/OperatorsController.cs new file mode 100644 index 000000000..47bdcea6a --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/OperatorsController.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +/// The service used to serialize/deserialize objects to/from JSON +[Route("api/v1/operators")] +public class OperatorsController(IMediator mediator, IJsonSerializer jsonSerializer) + : NamespacedResourceController(mediator, jsonSerializer) +{ + + + +} diff --git a/src/api/Synapse.Api.Http/Controllers/ServiceAccountsController.cs b/src/api/Synapse.Api.Http/Controllers/ServiceAccountsController.cs new file mode 100644 index 000000000..8e043009b --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/ServiceAccountsController.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +/// The service used to serialize/deserialize objects to/from JSON +[Route("api/v1/service-accounts")] +public class ServiceAccountsController(IMediator mediator, IJsonSerializer jsonSerializer) + : NamespacedResourceController(mediator, jsonSerializer) +{ + + + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Http/Controllers/UsersController.cs b/src/api/Synapse.Api.Http/Controllers/UsersController.cs new file mode 100644 index 000000000..ada384d9e --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/UsersController.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Application.Queries.Users; + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the controller used to manage users +/// +/// The service used to mediate calls +[Route("api/v1/users")] +public class UsersController(IMediator mediator) + : Controller +{ + + /// + /// Gets the service used to mediate calls + /// + protected IMediator Mediator { get; } = mediator; + + /// + /// Gets the current user's profile + /// + /// A + /// A new + [HttpGet("profile")] + public async Task GetUserProfile(CancellationToken cancellationToken = default) + { + return this.Process(await this.Mediator.ExecuteAsync(new GetUserProfileQuery(), cancellationToken).ConfigureAwait(false)); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Http/Controllers/WorkflowInstancesController.cs b/src/api/Synapse.Api.Http/Controllers/WorkflowInstancesController.cs new file mode 100644 index 000000000..4496fa85b --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/WorkflowInstancesController.cs @@ -0,0 +1,61 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure; +using Synapse.Api.Application.Queries.WorkflowInstances; + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +/// The service used to serialize/deserialize objects to/from JSON +[Route("api/v1/workflow-instances")] +public class WorkflowInstancesController(IMediator mediator, IJsonSerializer jsonSerializer) + : NamespacedResourceController(mediator, jsonSerializer) +{ + + /// + /// Gets the logs produced by workflow instance with the the specified name and namespace + /// + /// The name of the workflow instance to read the logs of + /// The namespace the workflow instance to read the logs of belongs to + /// A + /// A new + [HttpGet("{namespace}/{name}/logs")] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.Created)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public async Task GetResourceLogs(string name, string @namespace, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new ReadWorkflowInstanceLogsQuery(name, @namespace), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Watches the logs of a specific workflow instance + /// + /// The namespace the workflow instance to watch the logs of belongs to + /// The name of the workflow instance to watch the logs of + /// A + /// A new + [HttpGet("{namespace}/{name}/logs/watch")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task> WatchResourceLogs(string name, string @namespace, CancellationToken cancellationToken = default) + { + var response = await this.Mediator.ExecuteAsync(new WatchWorkflowInstanceLogsQuery(name, @namespace), cancellationToken).ConfigureAwait(false); + return response.Data!; + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Http/Controllers/WorkflowsController.cs b/src/api/Synapse.Api.Http/Controllers/WorkflowsController.cs new file mode 100644 index 000000000..d4e55cc1a --- /dev/null +++ b/src/api/Synapse.Api.Http/Controllers/WorkflowsController.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http.Controllers; + +/// +/// Represents the used to manage s +/// +/// The service used to mediate calls +/// The service used to serialize/deserialize objects to/from JSON +[Route("api/v1/workflows")] +public class WorkflowsController(IMediator mediator, IJsonSerializer jsonSerializer) + : NamespacedResourceController(mediator, jsonSerializer) +{ + + + +} diff --git a/src/api/Synapse.Api.Http/Extensions/IServiceCollectionExtensions.cs b/src/api/Synapse.Api.Http/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 000000000..ce2a73546 --- /dev/null +++ b/src/api/Synapse.Api.Http/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,88 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using IdentityServer4.Models; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.OpenApi.Models; +using Neuroglia; +using Neuroglia.Security.Services; +using Synapse.Api.Application; +using Synapse.Api.Application.Services; +using Synapse.Api.Http.Controllers; +using Synapse.Core.Api.Services; +using System.Text.Json; + +namespace Synapse.Api.Http; + +/// +/// Defines extensions for s +/// +public static class IServiceCollectionExtensions +{ + + /// + /// Adds and configures the Synapse HTTP API and its related services + /// + /// The to configure + /// The configured + public static IServiceCollection AddSynapseHttpApi(this IServiceCollection services) + { + ServiceAccountSigningKey.Initialize(); + services.AddHttpContextAccessor(); + services.AddScoped(); + services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + }) + .AddApplicationPart(typeof(WorkflowsController).Assembly); + services.AddIdentityServer() + .AddSigningCredential(ServiceAccountSigningKey.LoadPrivateKey()) + .AddInMemoryApiResources(SynapseApiDefaults.OpenIDConnect.ApiResources.AsEnumerable()) + .AddInMemoryIdentityResources(SynapseApiDefaults.OpenIDConnect.IdentityResources.AsEnumerable()) + .AddInMemoryApiScopes(SynapseApiDefaults.OpenIDConnect.ApiScopes.AsEnumerable()) + .AddClientStore() + .AddProfileService(); + services.AddSignalR(); + services.AddSingleton(); + services.AddSingleton(provider => provider.GetRequiredService()); + services.AddSwaggerGen(builder => + { + builder.CustomOperationIds(o => + { + var action = (ControllerActionDescriptor)o.ActionDescriptor; + return $"{action.ActionName}".ToCamelCase(); + }); + builder.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); + builder.SwaggerDoc("v1", new OpenApiInfo + { + Title = "Synapse REST API", + Version = "v1", + Description = "The Open API documentation for the Synapse REST API", + License = new OpenApiLicense() + { + Name = "Apache-2.0", + Url = new("https://raw.githubusercontent.com/synapse/dsl/main/LICENSE") + }, + Contact = new() + { + Name = "The Synapse Authors", + Url = new Uri("https://github.com/serverlessworkflow/synapse") + } + }); + builder.IncludeXmlComments(typeof(Workflow).Assembly.Location.Replace(".dll", ".xml")); + }); + return services; + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Http/Hubs/ResourceEventWatchHub.cs b/src/api/Synapse.Api.Http/Hubs/ResourceEventWatchHub.cs new file mode 100644 index 000000000..a96846a7f --- /dev/null +++ b/src/api/Synapse.Api.Http/Hubs/ResourceEventWatchHub.cs @@ -0,0 +1,46 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Core.Api.Services; + +namespace Synapse.Api.Http.Hubs; + +/// +/// Represents the used to notify clients about resource-related changes +/// +/// +/// Initializes a new +/// +/// The service used to control s +[Route("api/resource-management/v1/ws/watch")] +public class ResourceEventWatchHub(ResourceWatchEventHubController controller) + : Hub, IResourceEventWatchHub +{ + + /// + /// Gets the service used to control s + /// + protected ResourceWatchEventHubController Controller { get; } = controller; + + /// + public virtual Task Watch(ResourceDefinitionInfo definition, string? @namespace = null) => this.Controller.WatchResourcesAsync(this.Context.ConnectionId, definition, @namespace); + + /// + public virtual Task StopWatching(ResourceDefinitionInfo definition, string? @namespace = null) => this.Controller.StopWatchingResourcesAsync(this.Context.ConnectionId, definition, @namespace); + + /// + public override Task OnDisconnectedAsync(Exception? exception) => this.Controller.ReleaseConnectionResourcesAsync(this.Context.ConnectionId); + + +} + diff --git a/src/api/Synapse.Api.Http/NamespacedResourceController.cs b/src/api/Synapse.Api.Http/NamespacedResourceController.cs new file mode 100644 index 000000000..e6bdae09b --- /dev/null +++ b/src/api/Synapse.Api.Http/NamespacedResourceController.cs @@ -0,0 +1,275 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Http; + +/// +/// Represents the base class of a used to manage namespaced s +/// +/// The type of to manage +/// The service used to mediate calls +/// The service used to serialize/deserialize data to/from JSON +public abstract class NamespacedResourceController(IMediator mediator, IJsonSerializer jsonSerializer) + : ResourceController(mediator) + where TResource : class, IResource, new() +{ + + /// + /// Gets the service used to serialize/deserialize data to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the resource with the specified name and namespace + /// + /// The name of the resource to get + /// The namespace the resource to get belongs to + /// A + /// A new + [HttpGet("{namespace}/{name}")] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.Created)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public async Task GetResource(string name, string @namespace, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new GetResourceQuery(name, @namespace), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Lists matching resources + /// + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet] + [ProducesResponseType(typeof(Collection), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task GetResources(string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + return this.Process(await this.Mediator.ExecuteAsync(new GetResourcesQuery(null, labelSelectors), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Lists matching resources + /// + /// The namespace to resources to list belong to + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet("{namespace}")] + [ProducesResponseType(typeof(Collection), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task GetResources(string @namespace, string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + return this.Process(await this.Mediator.ExecuteAsync(new GetResourcesQuery(@namespace, labelSelectors), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Lists matching resources + /// + /// A comma-separated list of label selectors, if any + /// The maximum amount, if any, of results to list at once + /// A token, defined by a previously retrieved collection, used to continue enumerating through matches + /// A + /// A new + [HttpGet("list")] + [ProducesResponseType(typeof(Collection), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task ListResources(string? labelSelector = null, ulong? maxResults = null, string? continuationToken = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + return this.Process(await this.Mediator.ExecuteAsync(new ListResourcesQuery(null, labelSelectors, maxResults, continuationToken), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Lists matching resources + /// + /// The namespace to resources to list belong to + /// A comma-separated list of label selectors, if any + /// The maximum amount, if any, of results to list at once + /// A token, defined by a previously retrieved collection, used to continue enumerating through matches + /// A + /// A new + [HttpGet("{namespace}/list")] + [ProducesResponseType(typeof(Collection), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task ListResources(string @namespace, string? labelSelector = null, ulong? maxResults = null, string? continuationToken = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + return this.Process(await this.Mediator.ExecuteAsync(new ListResourcesQuery(@namespace, labelSelectors, maxResults, continuationToken), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Watches matching resources + /// + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet("watch")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task WatchResources(string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + var response = await this.Mediator.ExecuteAsync(new WatchResourcesQuery(null, labelSelectors), cancellationToken).ConfigureAwait(false); + this.Response.Headers.ContentType = "text/event-stream"; + this.Response.Headers.CacheControl = "no-cache"; + this.Response.Headers.Connection = "keep-alive"; + await foreach (var e in response.Data!) + { + var sseMessage = $"data: {this.JsonSerializer.SerializeToText(e)}\\n\\n"; + await this.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(sseMessage), cancellationToken).ConfigureAwait(false); + await this.Response.Body.FlushAsync(cancellationToken).ConfigureAwait(false); + } + return this.Ok(); + } + + /// + /// Watches matching resources + /// + /// The namespace the resources to watch belong to + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet("{namespace}/watch")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task>> WatchResources(string @namespace, string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) throw new Exception($"Invalid label selector '{labelSelector}'"); + var response = await this.Mediator.ExecuteAsync(new WatchResourcesQuery(@namespace, labelSelectors), cancellationToken).ConfigureAwait(false); + return response.Data!; + } + + /// + /// Watches matching resources + /// + /// The namespace the resources to watch belong to + /// A comma-separated list of label selectors, if any + /// A + /// A new + [HttpGet("{namespace}/watch/sse")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task WatchResourcesUsingSSE(string @namespace, string? labelSelector = null, CancellationToken cancellationToken = default) + { + if (!this.TryParseLabelSelectors(labelSelector, out var labelSelectors)) return this.InvalidLabelSelector(labelSelector!); + var response = await this.Mediator.ExecuteAsync(new WatchResourcesQuery(@namespace, labelSelectors), cancellationToken).ConfigureAwait(false); + this.Response.Headers.ContentType = "text/event-stream"; + this.Response.Headers.CacheControl = "no-cache"; + this.Response.Headers.Connection = "keep-alive"; + await foreach (var e in response.Data!) + { + var sseMessage = $"data: {this.JsonSerializer.SerializeToText(e)}\\n\\n"; + await this.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(sseMessage), cancellationToken).ConfigureAwait(false); + await this.Response.Body.FlushAsync(cancellationToken).ConfigureAwait(false); + } + return this.Ok(); + } + + /// + /// Monitors a specific resource + /// + /// The namespace the resource to monitor belongs to + /// The name of the resource to monitor + /// A + /// A new + [HttpGet("{namespace}/{name}/monitor")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task>> MonitorResource(string name, string @namespace, CancellationToken cancellationToken = default) + { + var response = await this.Mediator.ExecuteAsync(new MonitorResourceQuery(name, @namespace), cancellationToken).ConfigureAwait(false); + return response.Data!; + } + + /// + /// Monitors a specific resource + /// + /// The namespace the resource to monitor belongs to + /// The name of the resource to monitor + /// A + /// A new + [HttpGet("{namespace}/{name}/monitor/sse")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task MonitorResourceUsingSSE(string name, string @namespace, CancellationToken cancellationToken = default) + { + var response = await this.Mediator.ExecuteAsync(new MonitorResourceQuery(name, @namespace), cancellationToken).ConfigureAwait(false); + this.Response.Headers.ContentType = "text/event-stream"; + this.Response.Headers.CacheControl = "no-cache"; + this.Response.Headers.Connection = "keep-alive"; + await foreach(var e in response.Data!) + { + var sseMessage = $"data: {this.JsonSerializer.SerializeToText(e)}\\n\\n"; + await this.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(sseMessage), cancellationToken).ConfigureAwait(false); + await this.Response.Body.FlushAsync(cancellationToken).ConfigureAwait(false); + } + return this.Ok(); + } + + /// + /// Patches the specified resource + /// + /// The name of the resource to patch + /// The namespace the resource to patch belongs to + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// A new + [HttpPatch("{namespace}/{name}")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task PatchResource(string name, string @namespace, [FromBody] Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new PatchResourceCommand(name, @namespace, patch, resourceVersion), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Patches the specified resource's status + /// + /// The name of the resource to patch the status of + /// The namespace the resource to patch the status of belongs to + /// The patch to apply + /// The expected resource version, if any, used for optimistic concurrency + /// A + /// A new + [HttpPatch("{namespace}/{name}/status")] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task PatchResourceStatus(string name, string @namespace, [FromBody] Patch patch, string? resourceVersion = null, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new PatchResourceStatusCommand(name, @namespace, patch, resourceVersion), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Deletes the resource with the specified name and namespace + /// + /// The name of the resource to delete + /// The namespace the delete to get belongs to + /// A + /// A new + [HttpDelete("{namespace}/{name}")] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.Created)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public async Task DeleteResource(string name, string @namespace, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new DeleteResourceCommand(name, @namespace), cancellationToken).ConfigureAwait(false)); + } + +} diff --git a/src/api/Synapse.Api.Http/Properties/launchSettings.json b/src/api/Synapse.Api.Http/Properties/launchSettings.json new file mode 100644 index 000000000..87abfcb1d --- /dev/null +++ b/src/api/Synapse.Api.Http/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:3131", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5105", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/api/Synapse.Api.Http/ResourceController.cs b/src/api/Synapse.Api.Http/ResourceController.cs new file mode 100644 index 000000000..c779ff233 --- /dev/null +++ b/src/api/Synapse.Api.Http/ResourceController.cs @@ -0,0 +1,123 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Application.Commands.Resources.Generic; +using Synapse.Api.Application.Queries.Resources.Generic; + +namespace Synapse.Api.Http; + +/// +/// Represents the base class of a used to manage s +/// +/// The type of to manage +/// The service used to mediate calls +public abstract class ResourceController(IMediator mediator) + : Controller + where TResource : class, IResource, new() +{ + + /// + /// Gets the service used to mediate calls + /// + protected IMediator Mediator { get; } = mediator; + + /// + /// Creates a new resource of the specified type + /// + /// The resource to create + /// A + /// A new + [HttpPost] + [ProducesResponseType(typeof(Resource), (int)HttpStatusCode.Created)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public async Task CreateResource([FromBody] TResource resource, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new CreateResourceCommand(resource), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Gets the definition of the managed resources + /// + /// A + /// A new + [HttpGet("definition")] + [ProducesResponseType(typeof(ResourceDefinition), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task GetResourceDefinition(CancellationToken cancellationToken = default) + { + return this.Process(await this.Mediator.ExecuteAsync(new GetResourceDefinitionQuery(), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Replaces the specified resource + /// + /// The resource to replace + /// A + /// A new + [HttpPut] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task ReplaceResource(TResource resource, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new ReplaceResourceCommand(resource), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Replaces the status of the specified resource + /// + /// The resource to replace + /// A + /// A new + [HttpPut] + [ProducesResponseType(typeof(IAsyncEnumerable), (int)HttpStatusCode.OK)] + [ProducesErrorResponseType(typeof(Neuroglia.ProblemDetails))] + public virtual async Task ReplaceResourceStatus(TResource resource, CancellationToken cancellationToken = default) + { + if (!this.ModelState.IsValid) return this.ValidationProblem(this.ModelState); + return this.Process(await this.Mediator.ExecuteAsync(new ReplaceResourceStatusCommand(resource), cancellationToken).ConfigureAwait(false)); + } + + /// + /// Parses the specified string into a new of s + /// + /// The string to parse + /// A new containing the parsed s + /// A boolean indicating whether or not the input could be parse + protected virtual bool TryParseLabelSelectors(string? labelSelector, out IEnumerable? labelSelectors) + { + labelSelectors = null; + try + { + if (!string.IsNullOrWhiteSpace(labelSelector)) labelSelectors = LabelSelector.ParseList(labelSelector); + return true; + } + catch + { + return false; + } + } + + /// + /// Creates a new that describes an error while parsing the request's label selector + /// + /// The invalid label selector + /// A new + protected IActionResult InvalidLabelSelector(string labelSelector) + { + this.ModelState.AddModelError(nameof(labelSelector), $"The specified value '{labelSelector}' is not a valid comma-separated label selector list"); + return this.ValidationProblem("Bad Request", statusCode: (int)HttpStatusCode.BadRequest, title: "Bad Request", modelStateDictionary: this.ModelState); + } + +} diff --git a/src/api/Synapse.Api.Http/Services/ResourceWatchEventHubController.cs b/src/api/Synapse.Api.Http/Services/ResourceWatchEventHubController.cs new file mode 100644 index 000000000..81cd552da --- /dev/null +++ b/src/api/Synapse.Api.Http/Services/ResourceWatchEventHubController.cs @@ -0,0 +1,168 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Core.Api.Services; + +/// +/// Represents a service used to dispatch s to all s +/// +/// +/// Initializes a new +/// +/// The current +/// The current 's +public class ResourceWatchEventHubController(IServiceProvider serviceProvider, IHubContext hubContext) + : BackgroundService +{ + + /// + /// Gets the current + /// + protected IServiceScope ServiceScope { get; } = serviceProvider.CreateScope(); + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider => this.ServiceScope.ServiceProvider; + + /// + /// Gets the service used to manage s + /// + protected IResourceRepository Resources => this.ServiceProvider.GetRequiredService(); + + /// + /// Gets the current 's + /// + protected IHubContext HubContext { get; } = hubContext; + + /// + /// Gets a containing the mapping of active ^subscriptions per hub connection id + /// + protected ConcurrentDictionary> Connections { get; } = new(); + + /// + /// Gets the 's + /// + protected CancellationTokenSource CancellationTokenSource { get; private set; } = null!; + + /// + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); + return Task.CompletedTask; + } + + /// + /// Watches resources of the specified type + /// + /// The id of the SignalR connection to create the watch for + /// The type of resource to watch + /// The namespace resources to watch belong to, if any + /// A + /// A new awaitable + public virtual async Task WatchResourcesAsync(string connectionId, ResourceDefinitionInfo definition, string? @namespace = null, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(connectionId)) throw new ArgumentNullException(nameof(connectionId)); + ArgumentNullException.ThrowIfNull(definition); + var subscriptionKey = this.GetSubscriptionKey(definition, @namespace); + if (this.Connections.TryGetValue(connectionId, out var subscriptions) && subscriptions != null && subscriptions.TryGetValue(subscriptionKey, out var watch) && watch != null) return; + if (subscriptions == null) + { + subscriptions = new(); + this.Connections.AddOrUpdate(connectionId, subscriptions, (key, current) => + { + current.Values.ToList().ForEach(d => d.Dispose()); + return subscriptions; + }); + } + watch = await this.Resources.WatchAsync(definition.Group, definition.Version, definition.Plural, @namespace, cancellationToken: cancellationToken).ConfigureAwait(false); + watch.SubscribeAsync(e => this.OnResourceWatchEventAsync(connectionId, e), this.CancellationTokenSource.Token); + subscriptions.AddOrUpdate(subscriptionKey, watch, (key, current) => + { + current.Dispose(); + return watch; + }); + } + + /// + /// Stop watching resources of the specified type + /// + /// The id of the SignalR connection that owns the watch to dispose of + /// The type of resource to stop watching + /// The namespace resources to stop watching belong to, if any + /// A + /// A new awaitable + public virtual Task StopWatchingResourcesAsync(string connectionId, ResourceDefinitionInfo definition, string? @namespace = null, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(connectionId)) throw new ArgumentNullException(nameof(connectionId)); + ArgumentNullException.ThrowIfNull(definition); + if (!this.Connections.TryGetValue(connectionId, out var subscriptions) || subscriptions == null || subscriptions.IsEmpty) return Task.CompletedTask; + var subscriptionKey = this.GetSubscriptionKey(definition, @namespace); + if (subscriptions.Remove(subscriptionKey, out var subscription) && subscription != null) subscription.Dispose(); + return Task.CompletedTask; + } + + /// + /// Releases all resources owned by the specified connection + /// + /// The id of the connection to release the resources of + /// A + /// A new awaitable + public virtual Task ReleaseConnectionResourcesAsync(string connectionId, CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(connectionId)) throw new ArgumentNullException(nameof(connectionId)); + if (!this.Connections.Remove(connectionId, out var subscriptions) || subscriptions == null || subscriptions.IsEmpty) return Task.CompletedTask; + subscriptions.Keys.ToList().ForEach(subscriptionId => + { + subscriptions.Remove(subscriptionId, out var subscription); + subscription?.Dispose(); + }); + return Task.CompletedTask; + } + + /// + /// Creates a new subscription key for the specified resource type and namespace + /// + /// The type of resources to create a new subscription key for + /// The namespace the resources to create a new subscription key for belong to + /// A new subscription key for the specified resource type and namespace + protected virtual string GetSubscriptionKey(ResourceDefinitionInfo definition, string? @namespace = null) => string.IsNullOrWhiteSpace(@namespace) ? definition.ToString() : $"{definition}/{@namespace}"; + + /// + /// Handles the specified + /// + /// The id of the connection the event has been produced for + /// The to handle + /// A new awaitable + protected virtual Task OnResourceWatchEventAsync(string connectionId, IResourceWatchEvent e) => this.HubContext.Clients.Client(connectionId).ResourceWatchEvent(e); + + /// + public override void Dispose() + { + this.CancellationTokenSource?.Dispose(); + this.Connections.Keys.ToList().ForEach(connectionId => + { + this.Connections.Remove(connectionId, out var subscriptions); + if (subscriptions == null) return; + subscriptions.Keys.ToList().ForEach(subscriptionId => + { + subscriptions.Remove(subscriptionId, out var subscription); + subscription?.Dispose(); + }); + }); + this.ServiceScope.Dispose(); + base.Dispose(); + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj b/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj new file mode 100644 index 000000000..990c292c6 --- /dev/null +++ b/src/api/Synapse.Api.Http/Synapse.Api.Http.csproj @@ -0,0 +1,41 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse api http + true + true + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + + + + + + + + + + + + + + diff --git a/src/api/Synapse.Api.Http/Usings.cs b/src/api/Synapse.Api.Http/Usings.cs new file mode 100644 index 000000000..103d65951 --- /dev/null +++ b/src/api/Synapse.Api.Http/Usings.cs @@ -0,0 +1,30 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.SignalR; +global using Neuroglia.Data; +global using Neuroglia.Data.Infrastructure.ResourceOriented; +global using Neuroglia.Data.Infrastructure.ResourceOriented.Services; +global using Neuroglia.Mediation; +global using Neuroglia.Mediation.AspNetCore; +global using Neuroglia.Reactive; +global using Neuroglia.Serialization; +global using Synapse.Api.Application.Commands.Resources.Generic; +global using Synapse.Api.Application.Queries.Resources.Generic; +global using Synapse.Api.Client.Services; +global using Synapse.Api.Http.Hubs; +global using Synapse.Resources; +global using System.Collections.Concurrent; +global using System.Net; +global using System.Text; diff --git a/src/api/Synapse.Api.Server/Dockerfile b/src/api/Synapse.Api.Server/Dockerfile new file mode 100644 index 000000000..596ebefcd --- /dev/null +++ b/src/api/Synapse.Api.Server/Dockerfile @@ -0,0 +1,22 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER app +WORKDIR /app +EXPOSE 8080 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["src/api/Synapse.Api.Server/Synapse.Api.Server.csproj", "src/api/Synapse.Api.Server/"] +RUN dotnet restore "./src/api/Synapse.Api.Server/Synapse.Api.Server.csproj" +COPY . . +WORKDIR "/src/src/api/Synapse.Api.Server" +RUN dotnet build "./Synapse.Api.Server.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Synapse.Api.Server.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Synapse.Api.Server.dll"] \ No newline at end of file diff --git a/src/api/Synapse.Api.Server/FallbackPolicySchemeDefaults.cs b/src/api/Synapse.Api.Server/FallbackPolicySchemeDefaults.cs new file mode 100644 index 000000000..93dba7e28 --- /dev/null +++ b/src/api/Synapse.Api.Server/FallbackPolicySchemeDefaults.cs @@ -0,0 +1,27 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Server; + +/// +/// Exposes the defaults of Synapse's Fallback policy scheme based authentication +/// +public static class FallbackPolicySchemeDefaults +{ + + /// + /// Gets the name of Synapse's Fallback policy scheme based authentication + /// + public const string AuthenticationScheme = "Fallback"; + +} diff --git a/src/api/Synapse.Api.Server/Program.cs b/src/api/Synapse.Api.Server/Program.cs new file mode 100644 index 000000000..d3c854c6f --- /dev/null +++ b/src/api/Synapse.Api.Server/Program.cs @@ -0,0 +1,156 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.AspNetCore.Authentication.JwtBearer; + +var builder = WebApplication.CreateBuilder(args); +var applicationOptions = new ApiServerOptions(); +builder.Configuration.Bind(applicationOptions); +if (applicationOptions.Authentication.Tokens.Count < 1) throw new Exception("The Synapse API server requires that at least one static user token be configured"); + +builder.Services.Configure(builder.Configuration); +builder.Services.AddResponseCompression(); +builder.Services.AddSynapse(builder.Configuration); +builder.Services.AddSynapseApi(); +builder.Services.AddSynapseHttpApi(); + +var authentication = builder.Services.AddAuthentication(FallbackPolicySchemeDefaults.AuthenticationScheme); +authentication.AddScheme(StaticBearerDefaults.AuthenticationScheme, options => +{ + foreach(var token in applicationOptions.Authentication.Tokens) options.AddToken(token.Key, new(token.Value.Select(kvp => new Claim(kvp.Key, kvp.Value)), StaticBearerDefaults.AuthenticationScheme, JwtClaimTypes.Name, JwtClaimTypes.Role)); +}); +authentication.AddJwtBearer(ServiceAccountAuthenticationDefaults.AuthenticationScheme, options => +{ + options.Authority = builder.Environment.RunsInDocker() || builder.Environment.RunsInKubernetes() + ? "http://localhost:8080" + : "http://localhost:5257"; + options.RequireHttpsMetadata = false; + options.TokenValidationParameters = new() + { + NameClaimType = JwtClaimTypes.Name, + RoleClaimType = JwtClaimTypes.Role, + ValidAudience = "api", + ValidateAudience = false, + ValidIssuer = options.Authority, + ValidateIssuer = true, + IssuerSigningKey = ServiceAccountSigningKey.LoadPublicKey() + }; +}); +authentication.AddPolicyScheme(FallbackPolicySchemeDefaults.AuthenticationScheme, FallbackPolicySchemeDefaults.AuthenticationScheme, options => +{ + options.ForwardDefaultSelector = context => + { + var authorizationValue = context.Request.Headers.Authorization.ToString(); + var bearerPrefix = $"{JwtBearerDefaults.AuthenticationScheme} "; + if (authorizationValue?.StartsWith(bearerPrefix) == true) + { + var token = authorizationValue[bearerPrefix.Length..].Trim(); + var staticBearerOptions = context.RequestServices.GetRequiredService>().Get(StaticBearerDefaults.AuthenticationScheme); + if (staticBearerOptions.Tokens.ContainsKey(token)) return StaticBearerDefaults.AuthenticationScheme; + } + return ServiceAccountAuthenticationDefaults.AuthenticationScheme; + }; +}); +if (applicationOptions.Authentication.Jwt != null) +{ + authentication.AddJwtBearer(options => + { + options.Authority = applicationOptions.Authentication.Jwt.Authority; + options.Audience = applicationOptions.Authentication.Jwt.Audience; + options.TokenValidationParameters = new() + { + NameClaimType = JwtClaimTypes.Name, + RoleClaimType = JwtClaimTypes.Role, + ValidAudience = applicationOptions.Authentication.Jwt.Audience, + ValidateAudience = !string.IsNullOrWhiteSpace(applicationOptions.Authentication.Jwt.Audience), + ValidIssuer = applicationOptions.Authentication.Jwt.Issuer, + ValidateIssuer = !string.IsNullOrWhiteSpace(applicationOptions.Authentication.Jwt.Issuer), + IssuerSigningKey = applicationOptions.Authentication.Jwt.GetSigningKey() + }; + }); +} +if (applicationOptions.Authentication.Oidc != null) +{ + authentication.AddOpenIdConnect(options => + { + options.Authority = applicationOptions.Authentication.Oidc.Authority; + options.ClientId = applicationOptions.Authentication.Oidc.ClientId; + options.ClientSecret = applicationOptions.Authentication.Oidc.ClientSecret; + options.Resource = applicationOptions.Authentication.Oidc.Resource; + options.ResponseMode = applicationOptions.Authentication.Oidc.ResponseMode; + options.ResponseType = applicationOptions.Authentication.Oidc.ResponseType; + options.UsePkce = applicationOptions.Authentication.Oidc.UsePkce; + applicationOptions.Authentication.Oidc.Scope?.ForEach(options.Scope.Add); + options.TokenValidationParameters = new() + { + ValidAudience = applicationOptions.Authentication.Oidc.Audience, + ValidateAudience = !string.IsNullOrWhiteSpace(applicationOptions.Authentication.Oidc.Audience), + ValidIssuer = applicationOptions.Authentication.Oidc.Issuer, + ValidateIssuer = !string.IsNullOrWhiteSpace(applicationOptions.Authentication.Oidc.Issuer), + IssuerSigningKey = applicationOptions.Authentication.Oidc.GetSigningKey() + }; + }); +} +using var app = builder.Build(); +var options = app.Services.GetRequiredService>().Value; + +if (app.Environment.IsDevelopment() && options.ServeDashboard) app.UseWebAssemblyDebugging(); +app.UseResponseCompression(); +if (options.ServeDashboard) app.UseBlazorFrameworkFiles(); +app.UseStaticFiles(); +app.UseRouting(); +app.UseIdentityServer(); +app.UseAuthentication(); +app.UseAuthorization(); +app.UseExceptionHandler(handler => +{ + handler.Run(async context => + { + var exceptionHandlerPathFeature = context.Features.Get(); + var problemDetails = exceptionHandlerPathFeature?.Error switch + { + ProblemDetailsException problemDetailsException => problemDetailsException.Problem, + _ => new ProblemDetails(ErrorType.Runtime, ErrorTitle.Runtime, ErrorStatus.Runtime, exceptionHandlerPathFeature?.Error.Message) + }; + var json = context.RequestServices.GetRequiredService().SerializeToText(problemDetails); + context.Response.ContentType = MediaTypeNames.Application.Json; + context.Response.StatusCode = problemDetails.Status; + await context.Response.WriteAsync(json).ConfigureAwait(false); + }); +}); +app.UseSwagger(builder => +{ + builder.RouteTemplate = "api/{documentName}/doc/oas.{json|yaml}"; +}); +app.UseSwaggerUI(builder => +{ + builder.DocExpansion(DocExpansion.None); + builder.SwaggerEndpoint("/api/v1/doc/oas.json", "Synapse API v1"); + builder.RoutePrefix = "api/doc"; + builder.DisplayOperationId(); +}); +app.MapControllers().RequireAuthorization(); +app.MapHub("api/ws/resources/watch"); +if (options.ServeDashboard) +{ + app.MapFallbackToFile("index.html"); + app.MapFallbackToFile("/workflows/details/{namespace}/{name}/{version?}/{instanceName?}", "index.html"); + app.MapFallbackToFile("/workflow-instances/{instanceName?}", "index.html"); +} + +await app.RunAsync(); + +/// +/// The API server's program +/// +public partial class Program { } \ No newline at end of file diff --git a/src/api/Synapse.Api.Server/Properties/launchSettings.json b/src/api/Synapse.Api.Server/Properties/launchSettings.json new file mode 100644 index 000000000..3bfe6f96d --- /dev/null +++ b/src/api/Synapse.Api.Server/Properties/launchSettings.json @@ -0,0 +1,39 @@ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5257", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", + "environmentVariables": { + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:62918", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Server/ServiceAccountAuthenticationDefaults.cs b/src/api/Synapse.Api.Server/ServiceAccountAuthenticationDefaults.cs new file mode 100644 index 000000000..4eb47fc7b --- /dev/null +++ b/src/api/Synapse.Api.Server/ServiceAccountAuthenticationDefaults.cs @@ -0,0 +1,27 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Server; + +/// +/// Exposes the defaults of Synapse's Service Account authentication +/// +public static class ServiceAccountAuthenticationDefaults +{ + + /// + /// Gets the name of the Service Account authentication + /// + public const string AuthenticationScheme = "ServiceAccount"; + +} \ No newline at end of file diff --git a/src/api/Synapse.Api.Server/StaticBearerDefaults.cs b/src/api/Synapse.Api.Server/StaticBearerDefaults.cs new file mode 100644 index 000000000..d399ffc6e --- /dev/null +++ b/src/api/Synapse.Api.Server/StaticBearerDefaults.cs @@ -0,0 +1,27 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Api.Server; + +/// +/// Exposes the defaults of Synapse's Static Bearer Token authentication +/// +public static class StaticBearerDefaults +{ + + /// + /// Gets the name of the Static Bearer Token authentication + /// + public const string AuthenticationScheme = "StaticBearer"; + +} diff --git a/src/api/Synapse.Api.Server/Synapse.Api.Server.csproj b/src/api/Synapse.Api.Server/Synapse.Api.Server.csproj new file mode 100644 index 000000000..5746bc82d --- /dev/null +++ b/src/api/Synapse.Api.Server/Synapse.Api.Server.csproj @@ -0,0 +1,45 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse api server + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + ghcr.io/serverlessworkflow/synapse/api + Linux + ..\..\.. + false + + + + + + + + + + + + + + + + diff --git a/src/api/Synapse.Api.Server/Usings.cs b/src/api/Synapse.Api.Server/Usings.cs new file mode 100644 index 000000000..247ef4bf9 --- /dev/null +++ b/src/api/Synapse.Api.Server/Usings.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using IdentityModel; +global using Microsoft.AspNetCore.Diagnostics; +global using Microsoft.Extensions.Options; +global using Neuroglia; +global using Neuroglia.Serialization; +global using ServerlessWorkflow.Sdk; +global using Swashbuckle.AspNetCore.SwaggerUI; +global using Synapse; +global using Synapse.Api.Application; +global using Synapse.Api.Application.Configuration; +global using Synapse.Api.Application.Services; +global using Synapse.Api.Http; +global using Synapse.Api.Http.Hubs; +global using Synapse.Api.Server; +global using System.Net.Mime; +global using System.Security.Claims; diff --git a/src/api/Synapse.Api.Server/appsettings.Development.json b/src/api/Synapse.Api.Server/appsettings.Development.json new file mode 100644 index 000000000..7fb0fc120 --- /dev/null +++ b/src/api/Synapse.Api.Server/appsettings.Development.json @@ -0,0 +1,24 @@ +{ + "ConnectionStrings": { + "redis": "localhost:6379" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "ServeDashboard": true, + "Authentication": { + "Tokens": { + "debug": { + "sub": "fc4c86cd-e4ff-463c-b0e4-36f34f795d7e", + "name": "root", + "role": "admin" + } + } + }, + "Events": { + "Endpoint": "https://webhook.site/e2e48dd2-4ff5-477d-9788-f8fa9abdae7d" + } +} diff --git a/src/api/Synapse.Api.Server/appsettings.json b/src/api/Synapse.Api.Server/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/src/api/Synapse.Api.Server/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/apis/management/Synapse.Apis.Management.Core/Services/ISynapseManagementApi.cs b/src/apis/management/Synapse.Apis.Management.Core/Services/ISynapseManagementApi.cs deleted file mode 100644 index 58e88b9c0..000000000 --- a/src/apis/management/Synapse.Apis.Management.Core/Services/ISynapseManagementApi.cs +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Commands.AuthenticationDefinitionCollections; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Commands.EventDefinitionCollections; -using Synapse.Integration.Commands.FunctionDefinitionCollections; -using Synapse.Integration.Commands.Schedules; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Commands.Workflows; -using Synapse.Integration.Models; -using System.ServiceModel; - -namespace Synapse.Apis.Management -{ - - ///

- /// Defines the fundamentals of the Synapse Management API - /// - [ServiceContract] - public interface ISynapseManagementApi - { - - #region Application - - /// - /// Gets informations about the running Synapse application - /// - /// A - /// A new awaitable - [OperationContract] - Task GetApplicationInfoAsync(CancellationToken cancellationToken = default); - - #endregion - - #region Workflows - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// A - /// The newly created - [OperationContract] - Task CreateWorkflowAsync(V1CreateWorkflowCommand command, CancellationToken cancellationToken = default); - - /// - /// Uploads a new - /// - /// The object that describes the command to execute - /// A - /// The newly created - [OperationContract] - Task UploadWorkflowAsync(V1UploadWorkflowCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// A - /// The with the specified id - - [OperationContract] - Task GetWorkflowByIdAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// A - /// A new containing all existing s - [OperationContract] - Task> GetWorkflowsAsync(string? query = null, CancellationToken cancellationToken = default); - - /// - /// Gets the id of the to archive - /// - /// The id of the to archive - /// The version of the to achieve - /// A - /// A new containing the archived - [OperationContract] - Task ArchiveWorkflowAsync(string id, string? version = null, CancellationToken cancellationToken = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteWorkflowAsync(string id, CancellationToken cancellationToken = default); - - #endregion - - #region WorkflowInstances - - /// - /// Creates a new workflow instance - /// - /// The object that describes the command to execute - /// A - /// The newly created workflow instance - [OperationContract] - Task CreateWorkflowInstanceAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default); - - /// - /// Starts a workflow instance - /// - /// The id of the workflow instance to start - /// A - /// The started workflow instance - [OperationContract] - Task StartWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Gets the workflow instance with the specified id - /// - /// The id of the workflow instance to get - /// A - /// The workflow instance with the specified id - [OperationContract] - Task GetWorkflowInstanceByIdAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Lists existing workflow instances - /// - /// The ODATA query - /// A - /// A new containing all existing workflow instances - [OperationContract] - Task> GetWorkflowInstancesAsync(string? query = null, CancellationToken cancellationToken = default); - - /// - /// Suspends the execution of the with the specified id - /// - /// The id of the to suspend the exection of - /// A - /// A new awaitable - [OperationContract] - Task SuspendWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Resumes the execution of the with the specified id - /// - /// The id of the to resume the execution of - /// A - /// A new awaitable - [OperationContract] - Task ResumeWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Cancels the execution of the with the specified id - /// - /// The id of the to cancel the execution of - /// A - /// A new awaitable - [OperationContract] - Task CancelWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Gets the logs of the with the specified id - /// - /// The id of the to get the logs of - /// A - /// The logs of the specified - [OperationContract] - Task GetWorkflowInstanceLogsAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Gets the id of the to archive - /// - /// The id of the to archive - /// A - /// A new containing the archived - [OperationContract] - Task ArchiveWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default); - - #endregion - - #region Correlations - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// A - /// The newly created - [OperationContract] - Task CreateCorrelationAsync(V1CreateCorrelationCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// A - /// The with the specified id - - [OperationContract] - Task GetCorrelationByIdAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// A - /// A new containing all existing s - [OperationContract] - Task> GetCorrelationsAsync(string? query = null, CancellationToken cancellationToken = default); - - /// - /// Deletes the specified - /// - /// The id of the the to delete belongs to - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteCorrelationContextAsync(string correlationId, string contextId, CancellationToken cancellationToken = default); - - /// - /// Deletes the specified correlated - /// - /// The id of the the correlated to delete belongs to - /// The id of the the correlated to delete belongs to - /// The id of the correlated to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteCorrelationContextEventAsync(string correlationId, string contextId, string eventId, CancellationToken cancellationToken = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteCorrelationAsync(string id, CancellationToken cancellationToken = default); - - #endregion - - #region AuthenticationDefinitionCollections - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// A - /// The newly created - [OperationContract] - Task CreateAuthenticationDefinitionCollectionAsync(V1CreateAuthenticationDefinitionCollectionCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// A - /// The with the specified id - - [OperationContract] - Task GetAuthenticationDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// A - /// A new containing all existing s - [OperationContract] - Task> GetAuthenticationDefinitionCollectionsAsync(string? query = null, CancellationToken cancellationToken = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteAuthenticationDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default); - - #endregion - - #region EventDefinitionCollections - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// A - /// The newly created - [OperationContract] - Task CreateEventDefinitionCollectionAsync(V1CreateEventDefinitionCollectionCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// A - /// The with the specified id - - [OperationContract] - Task GetEventDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// A - /// A new containing all existing s - [OperationContract] - Task> GetEventDefinitionCollectionsAsync(string? query = null, CancellationToken cancellationToken = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteEventDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default); - - #endregion - - #region FunctionDefinitionCollections - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// A - /// The newly created - [OperationContract] - Task CreateFunctionDefinitionCollectionAsync(V1CreateFunctionDefinitionCollectionCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// A - /// The with the specified id - - [OperationContract] - Task GetFunctionDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// A - /// A new containing all existing s - [OperationContract] - Task> GetFunctionDefinitionCollectionsAsync(string? query = null, CancellationToken cancellationToken = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteFunctionDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default); - - #endregion - - #region Metrics - - /// - /// Gets the - /// - /// The date to get the for - /// A - /// The - [OperationContract] - Task GetOperationalReportAsync(DateTime? date = null, CancellationToken cancellationToken = default); - - #endregion - - #region Schedules - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// A - /// The newly created - [OperationContract] - Task CreateScheduleAsync(V1CreateScheduleCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// A - /// The with the specified id - - [OperationContract] - Task GetScheduleByIdAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// A - /// A new containing all existing s - [OperationContract] - Task> GetSchedulesAsync(string query, CancellationToken cancellationToken = default); - - /// - /// Triggers the specified - /// - /// The id of the to trigger - /// A - /// A new awaitable - [OperationContract] - Task TriggerScheduleAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Suspends the specified - /// - /// The id of the to suspend - /// A - /// A new awaitable - [OperationContract] - Task SuspendScheduleAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Resumes the specified - /// - /// The id of the to resume - /// A - /// A new awaitable - [OperationContract] - Task ResumeScheduleAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Retires the specified - /// - /// The id of the to retire - /// A - /// A new awaitable - [OperationContract] - Task RetireScheduleAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Makes the specified obsolete - /// - /// The id of the to make obsolete - /// A - /// A new awaitable - [OperationContract] - Task MakeScheduleObsoleteAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// A - /// A new awaitable - [OperationContract] - Task DeleteScheduleAsync(string id, CancellationToken cancellationToken = default); - - #endregion - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Core/Synapse.Apis.Management.Core.csproj b/src/apis/management/Synapse.Apis.Management.Core/Synapse.Apis.Management.Core.csproj deleted file mode 100644 index a1edcda8a..000000000 --- a/src/apis/management/Synapse.Apis.Management.Core/Synapse.Apis.Management.Core.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Core", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse management api core - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains abstractions for the management API of Synapse WFMS - - - - \ - True - - - - - - \ No newline at end of file diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/ISynapseGrpcManagementApiClientOptionsBuilder.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/ISynapseGrpcManagementApiClientOptionsBuilder.cs deleted file mode 100644 index 7185c841f..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/ISynapseGrpcManagementApiClientOptionsBuilder.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Grpc.Net.Client; - -namespace Synapse.Apis.Management.Grpc.Configuration -{ - - ///

- /// Defines the fundamentals of a service used to build - /// - public interface ISynapseGrpcManagementApiClientOptionsBuilder - { - - /// - /// Configures the address of the GRPC-based API to connect to - /// - /// The address of the GRPC-based API to connect to - /// The configured - ISynapseGrpcManagementApiClientOptionsBuilder ForAddress(Uri address); - - /// - /// Configures the to use the specified - /// - /// The to use - /// The configured - ISynapseGrpcManagementApiClientOptionsBuilder WithChannelOptions(GrpcChannelOptions options); - - /// - /// Configures the to use the specified - /// - /// The used to configure the to use - /// The configured - ISynapseGrpcManagementApiClientOptionsBuilder WithChannelOptions(Action configurationAction); - - /// - /// Builds the to use - /// - /// New - SynapseGrpcManagementApiClientOptions Build(); - - } - - -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/SynapseGrpcManagementApiClientOptions.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/SynapseGrpcManagementApiClientOptions.cs deleted file mode 100644 index 0258e35e1..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/SynapseGrpcManagementApiClientOptions.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Grpc.Net.Client; - -namespace Synapse.Apis.Management.Grpc.Configuration -{ - - ///

- /// Represents the options used to configure a client for a GRPC-based - /// - public class SynapseGrpcManagementApiClientOptions - { - - /// - /// Gets the default address of the GRPC-based - /// - public static Uri DefaultAddress - { - get - { - var scheme = EnvironmentVariables.Api.Grpc.Scheme.Value; - if (string.IsNullOrWhiteSpace(scheme)) - scheme = "http"; - var host = EnvironmentVariables.Api.HostName.Value; - if (string.IsNullOrWhiteSpace(host)) - host = "synapse"; - var port = EnvironmentVariables.Api.Grpc.Port.Value; - if (string.IsNullOrWhiteSpace(port)) - port = "41387"; - return new($"{scheme}://{host}:{port}"); - } - } - - /// - /// Gets/sets the address of the GRPC-based to connect to - /// - public virtual Uri Address { get; set; } = DefaultAddress; - - /// - /// gets/sets the options used to configure the GRPC-based 's options - /// - public virtual GrpcChannelOptions ChannelOptions { get; set; } = new(); - - } -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/SynapseGrpcManagementApiClientOptionsBuilder.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/SynapseGrpcManagementApiClientOptionsBuilder.cs deleted file mode 100644 index 160008f45..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Configuration/SynapseGrpcManagementApiClientOptionsBuilder.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Grpc.Net.Client; - -namespace Synapse.Apis.Management.Grpc.Configuration -{ - - ///

- /// Represents the default implementation of the interface - /// - public class SynapseGrpcManagementApiClientOptionsBuilder - : ISynapseGrpcManagementApiClientOptionsBuilder - { - - /// - /// Gets the to configure - /// - protected SynapseGrpcManagementApiClientOptions Options { get; } = new(); - - /// - public virtual ISynapseGrpcManagementApiClientOptionsBuilder ForAddress(Uri address) - { - if(address == null) - throw new ArgumentNullException(nameof(address)); - this.Options.Address = address; - return this; - } - - /// - public virtual ISynapseGrpcManagementApiClientOptionsBuilder WithChannelOptions(GrpcChannelOptions options) - { - if (options == null) - throw new ArgumentNullException(nameof(options)); - this.Options.ChannelOptions = options; - return this; - } - - /// - public virtual ISynapseGrpcManagementApiClientOptionsBuilder WithChannelOptions(Action configurationAction) - { - if (configurationAction == null) - throw new ArgumentNullException(nameof(configurationAction)); - var options = new GrpcChannelOptions(); - configurationAction(options); - return this.WithChannelOptions(options); - } - - /// - public virtual SynapseGrpcManagementApiClientOptions Build() - { - return this.Options; - } - - } -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Extensions/IServiceCollectionExtensions.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index e297eafa6..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Grpc.Net.Client; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; -using ProtoBuf.Grpc.Client; -using Synapse.Apis.Management.Grpc.Configuration; - -namespace Synapse.Apis.Management.Grpc -{ - - ///

- /// Defines extensions for s - /// - public static class IServiceCollectionExtensions - { - - /// - /// Adds and configures a GRPC-based client for the Synapse Management API - /// - /// The to configure - /// An used to configure the to use - /// The configured - public static IServiceCollection AddSynapseGrpcManagementApiClient(this IServiceCollection services, Action configurationAction) - { - if (configurationAction == null) - throw new ArgumentNullException(nameof(configurationAction)); - var builder = new SynapseGrpcManagementApiClientOptionsBuilder(); - configurationAction(builder); - var options = builder.Build(); - services.TryAddSingleton(Options.Create(options)); - services.TryAddSingleton(provider => - { - var channel = GrpcChannel.ForAddress(options.Address, options.ChannelOptions); - return channel.CreateGrpcService(); - }); - services.TryAddSingleton(); - return services; - } - - /// - /// Adds and configures a GRPC-based client for the Synapse Management API - /// - /// The to configure - /// The configured - public static IServiceCollection AddSynapseGrpcManagementApiClient(this IServiceCollection services) - { - return services.AddSynapseGrpcManagementApiClient(builder => { }); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Models/GrpcApiRequest.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Models/GrpcApiRequest.cs deleted file mode 100644 index 16099d736..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Models/GrpcApiRequest.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Management.Grpc.Models -{ - ///

- /// Represents a Grpc wrapper for an API request - /// - /// The type of data wrapped by the request - [ProtoContract] - public class GrpcApiRequest - { - - /// - /// Initializes a new - /// - /// The data wrapped by the - public GrpcApiRequest(T data) - { - this.Data = data; - } - - /// - /// Gets the data wrapped by the - /// - [ProtoMember(1)] - public virtual T Data { get; protected set; } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Models/GrpcApiResult.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Models/GrpcApiResult.cs deleted file mode 100644 index 907136844..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Models/GrpcApiResult.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Management.Grpc.Models -{ - - ///

- /// Represents a Protobuf-compliant object used to describe the result of an operation on the Synapse Management API - /// - [ProtoContract] - public class GrpcApiResult - { - - /// - /// Initializes a new - /// - protected GrpcApiResult() - { - Code = null!; - } - - /// - /// Initializes a new - /// - /// The 's code - /// An array of errors that have occured during the operation's execution - public GrpcApiResult(string code, params Error[] errors) - { - Code = code; - Errors = errors == null ? new List() : errors.ToList(); - } - - /// - /// Initializes a new - /// - /// The 's code - public GrpcApiResult(string code) - : this(code, Array.Empty()) - { - - } - - /// - /// Gets the described result code - /// - [ProtoMember(1)] - public string Code { get; internal set; } - - /// - /// Gets an containing the errors that have occured during the application's execution - /// - [ProtoMember(2)] - public IEnumerable? Errors { get; internal set; } - - /// - /// Gets a boolean indicating whether or not the operation was successfull - /// - [ProtoIgnore] - [IgnoreDataMember] - public virtual bool Succeeded => this.Errors == null || !this.Errors.Any(); - - /// - /// Creates a new for the specified - /// - /// The to create a new for - /// A new - public static GrpcApiResult CreateFor(IOperationResult result) - { - return new GrpcApiResult() - { - Code = result.Code, - Errors = result.Errors?.Select(e => new Error() { Code = e.Code, Message = e.Message })! - }; - } - - /// - /// Creates a new for the specified - /// - /// The type of data wrapped by the - /// The to create a new for - /// A new - public static GrpcApiResult CreateFor(IOperationResult result) - { - return new GrpcApiResult() - { - Code = result.Code, - Errors = result.Errors?.Select(e => new Error() { Code = e.Code, Message = e.Message })! - }; - } - - /// - /// Creates a new for the specified - /// - /// The type of data wrapped by the - /// The to create a new for - /// A new - public static GrpcApiResult CreateFor(IOperationResult result) - { - return new GrpcApiResult() - { - Code = result.Code, - Errors = result.Errors?.Select(e => new Error() { Code = e.Code, Message = e.Message })!, - Data = result.Data - }; - } - - } - - /// - /// Represents a Protobuf-compliant object used to describe the result of an operation on the Synapse Management API - /// - /// The type of wrapped result - [ProtoContract] - public class GrpcApiResult - : GrpcApiResult - { - - /// - /// Initializes a new - /// - protected internal GrpcApiResult() - { - Code = null!; - } - - /// - /// Initializes a new - /// - /// The 's code - /// An array of errors that have occured during the operation's execution - public GrpcApiResult(string code, params Error[] errors) - : base(code, errors) - { - Code = code; - } - - /// - /// Initializes a new - /// - /// The 's code - /// The data returned by the operation - public GrpcApiResult(string code, T? data) - : base(code) - { - Code = code; - Data = data; - } - - /// - /// Initializes a new - /// - /// The 's code - public GrpcApiResult(string code) - : this(code, default(T)) - { - - } - - /// - [ProtoMember(1)] - public new string Code { get; internal set; } - - /// - [ProtoMember(2)] - public new IEnumerable? Errors { get; internal set; } - - /// - /// Gets the data returned by the operation - /// - [ProtoMember(3)] - public T? Data { get; internal set; } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Properties/GlobalUsings.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Properties/GlobalUsings.cs deleted file mode 100644 index 8c2b0c0d6..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Properties/GlobalUsings.cs +++ /dev/null @@ -1,7 +0,0 @@ -global using Neuroglia; -global using ProtoBuf; -global using Synapse.Apis.Management.Grpc.Models; -global using Synapse.Integration.Models; -global using System.Runtime.Serialization; -global using System.ServiceModel; -global using Error = Synapse.Integration.Models.Error; \ No newline at end of file diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Services/ISynapseGrpcManagementApi.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Services/ISynapseGrpcManagementApi.cs deleted file mode 100644 index bf041a38e..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Services/ISynapseGrpcManagementApi.cs +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ProtoBuf.Grpc; -using Synapse.Integration.Commands.AuthenticationDefinitionCollections; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Commands.EventDefinitionCollections; -using Synapse.Integration.Commands.FunctionDefinitionCollections; -using Synapse.Integration.Commands.Schedules; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Commands.Workflows; - -namespace Synapse.Apis.Management.Grpc; - -///

-/// Defines the GRPC port of the Synapse API client -/// -[ServiceContract] -public interface ISynapseGrpcManagementApi -{ - - #region Application - - /// - /// Gets information about the running Synapse application - /// - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> GetApplicationInfoAsync(CallContext context = default); - - #endregion - - #region Workflows - - /// - /// Creates a new workflow - /// - /// The object that describes the command to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> CreateWorkflowAsync(V1CreateWorkflowCommand command, CallContext context = default); - - /// - /// Uploads a new workflow - /// - /// The object that describes the command to execute - /// A - /// A new object that describes the result of the operation - //[OperationContract] //todo: uncomment - Task> UploadWorkflowAsync(V1UploadWorkflowCommand command, CallContext context = default); //todo: implement - - /// - /// Gets the workflow with the specified id - /// - /// The id of the to get - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> GetWorkflowByIdAsync(string id, CallContext context = default); - - /// - /// Queries workflows - /// - /// The ODATA query to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task>> GetWorkflowsAsync(string? query = null, CallContext context = default); - - /// - /// Gets the id of the to archive - /// - /// The id of the to archive - /// The version of the to archive - /// The current - /// A new containing the archived - [OperationContract] - Task> ArchiveWorkflowAsync(string id, string? version = null, CallContext context = default); - - /// - /// Deletes the workflow with the specified id - /// - /// The id of the workflow to delete - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task DeleteWorkflowAsync(string id, CallContext context = default); - - #endregion - - #region WorkflowInstances - - /// - /// Creates a new workflow instance - /// - /// The object that describes the command to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> CreateWorkflowInstanceAsync(V1CreateWorkflowInstanceCommand command, CallContext context = default); - - - /// - /// Starts a workflow instance - /// - /// The id of the workflow instance to start - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> StartWorkflowInstanceAsync(string id, CallContext context = default); - - /// - /// Gets the workflow instance with the specified id - /// - /// The id of the workflow instance to get - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> GetWorkflowInstanceByIdAsync(string id, CallContext context = default); - - /// - /// Queries workflow instances - /// - /// The ODATA query to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task>> GetWorkflowInstancesAsync(string? query = null, CallContext context = default); - - /// - /// Suspends the execution of the with the specified id - /// - /// The id of the to suspend the exection of - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task SuspendWorkflowInstanceAsync(string id, CallContext context = default); - - /// - /// Resumes the execution of the with the specified id - /// - /// The id of the to resume the execution of - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task ResumeWorkflowInstanceAsync(string id, CallContext context = default); - - /// - /// Cancels the execution of the with the specified id - /// - /// The id of the to cancel the execution of - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task CancelWorkflowInstanceAsync(string id, CallContext context = default); - - /// - /// Gets the logs of the with the specified id - /// - /// The id of the to get the logs of - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> GetWorkflowInstanceLogsAsync(string id, CallContext context = default); - - /// - /// Gets the id of the to archive - /// - /// The id of the to archive - /// The current - /// A new containing the archived - [OperationContract] - Task> ArchiveWorkflowInstanceAsync(string id, CallContext context = default); - - /// - /// Deletes the workflow instance with the specified id - /// - /// The id of the workflow instance to delete - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task DeleteWorkflowInstanceAsync(string id, CallContext context = default); - - #endregion - - #region Correlations - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> CreateCorrelationAsync(V1CreateCorrelationCommand command, CallContext context = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// The current - /// A new object that describes the result of the operation - - [OperationContract] - Task> GetCorrelationByIdAsync(string id, CallContext context = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task>> GetCorrelationsAsync(string? query = null, CallContext context = default); - - /// - /// Deletes the specified - /// - /// An object that describes the command to execute - /// The current - /// A new awaitable - [OperationContract] - Task DeleteCorrelationContextAsync(V1DeleteCorrelationContextCommand command, CallContext context = default); - - /// - /// Deletes the specified correlated - /// - /// An object that describes the command to execute - /// The current - /// A new awaitable - [OperationContract] - Task DeleteCorrelationContextEventAsync(V1DeleteCorrelatedEventCommand command, CallContext context = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task DeleteCorrelationAsync(string id, CallContext context = default); - - #endregion - - #region AuthenticationDefinitionCollections - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// The current - /// The newly created - [OperationContract] - Task> CreateAuthenticationDefinitionCollectionAsync(V1CreateAuthenticationDefinitionCollectionCommand command, CallContext context = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// The current - /// The with the specified id - - [OperationContract] - Task> GetAuthenticationDefinitionCollectionByIdAsync(string id, CallContext context = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// The current - /// A new containing all existing s - [OperationContract] - Task>> GetAuthenticationDefinitionCollectionsAsync(string? query = null, CallContext context = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// The current - /// A new awaitable - [OperationContract] - Task DeleteAuthenticationDefinitionCollectionAsync(string id, CallContext context = default); - - #endregion - - #region EventDefinitionCollections - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// The current - /// The newly created - [OperationContract] - Task> CreateEventDefinitionCollectionAsync(V1CreateEventDefinitionCollectionCommand command, CallContext context = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// The current - /// The with the specified id - - [OperationContract] - Task> GetEventDefinitionCollectionByIdAsync(string id, CallContext context = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// The current - /// A new containing all existing s - [OperationContract] - Task>> GetEventDefinitionCollectionsAsync(string? query = null, CallContext context = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// The current - /// A new awaitable - [OperationContract] - Task DeleteEventDefinitionCollectionAsync(string id, CallContext context = default); - - #endregion - - #region FunctionDefinitionCollections - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// The current - /// The newly created - [OperationContract] - Task> CreateFunctionDefinitionCollectionAsync(V1CreateFunctionDefinitionCollectionCommand command, CallContext context = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// The current - /// The with the specified id - - [OperationContract] - Task> GetFunctionDefinitionCollectionByIdAsync(string id, CallContext context = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// The current - /// A new containing all existing s - [OperationContract] - Task>> GetFunctionDefinitionCollectionsAsync(string? query = null, CallContext context = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// The current - /// A new awaitable - [OperationContract] - Task DeleteFunctionDefinitionCollectionAsync(string id, CallContext context = default); - - #endregion - - #region OperationReports - - /// - /// Gets the - /// - /// The date to get the report for. Defaults to today - /// The current - /// The - [OperationContract] - Task> GetOperationalReportAsync(GrpcApiRequest request, CallContext context = default); - - #endregion - - #region Schedules - - /// - /// Creates a new - /// - /// The object that describes the command to execute - /// The current - /// The newly created - [OperationContract] - Task> CreateScheduleAsync(V1CreateScheduleCommand command, CallContext context = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// The current - /// The with the specified id - - [OperationContract] - Task> GetScheduleByIdAsync(string id, CallContext context = default); - - /// - /// Lists existing s - /// - /// The OData query string - /// The current - /// A new containing all existing s - [OperationContract] - Task>> GetSchedulesAsync(string? query = null, CallContext context = default); - - /// - /// Triggers the specified - /// - /// The id of the to trigger - /// The current - /// A new awaitable - [OperationContract] - Task TriggerScheduleAsync(string id, CallContext context = default); - - /// - /// Suspends the specified - /// - /// The id of the to suspend - /// The current - /// A new awaitable - [OperationContract] - Task SuspendScheduleAsync(string id, CallContext context = default); - - /// - /// Resumes the specified - /// - /// The id of the to resume - /// The current - /// A new awaitable - [OperationContract] - Task ResumeScheduleAsync(string id, CallContext context = default); - - /// - /// Retires the specified - /// - /// The id of the to retire - /// The current - /// A new awaitable - [OperationContract] - Task RetireScheduleAsync(string id, CallContext context = default); - - /// - /// Makes the specified obsolete - /// - /// The id of the to make obsolete - /// The current - /// A new awaitable - [OperationContract] - Task MakeScheduleObsoleteAsync(string id, CallContext context = default); - - /// - /// Deletes the with the specified id - /// - /// The id of the to delete - /// The current - /// A new awaitable - [OperationContract] - Task DeleteScheduleAsync(string id, CallContext context = default); - - #endregion - -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Services/SynapseGrpcManagementApiClient.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Services/SynapseGrpcManagementApiClient.cs deleted file mode 100644 index 32843cf5b..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Services/SynapseGrpcManagementApiClient.cs +++ /dev/null @@ -1,480 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Microsoft.Extensions.Logging; -using Synapse.Integration.Commands.AuthenticationDefinitionCollections; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Commands.EventDefinitionCollections; -using Synapse.Integration.Commands.FunctionDefinitionCollections; -using Synapse.Integration.Commands.Schedules; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Commands.Workflows; - -namespace Synapse.Apis.Management.Grpc -{ - - ///

- /// Represents a GRPC-based client for the - /// - public class SynapseGrpcManagementApiClient - : ISynapseManagementApi - { - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to interact with the GRPC port of the Synapse API - public SynapseGrpcManagementApiClient(ILogger logger, ISynapseGrpcManagementApi adapter) - { - this.Logger = logger; - this.Adapter = adapter; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to interact with the GRPC port of the Synapse API - /// - protected ISynapseGrpcManagementApi Adapter { get; } - - #region Application - - /// - public virtual async Task GetApplicationInfoAsync(CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetApplicationInfoAsync(cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - #endregion - - #region Workflows - - /// - public virtual async Task CreateWorkflowAsync(V1CreateWorkflowCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CreateWorkflowAsync(command, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task UploadWorkflowAsync(V1UploadWorkflowCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.UploadWorkflowAsync(command, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task> GetWorkflowsAsync(string? query = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetWorkflowsAsync(query, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task GetWorkflowByIdAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetWorkflowByIdAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task ArchiveWorkflowAsync(string id, string? version = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.ArchiveWorkflowAsync(id, version, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return new MemoryStream(result.Data!); - } - - /// - public virtual async Task DeleteWorkflowAsync(string id, CancellationToken cancellationToken = default) - { - await this.Adapter.DeleteWorkflowAsync(id, cancellationToken); - } - - #endregion region - - #region WorkflowInstances - - /// - public virtual async Task CreateWorkflowInstanceAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CreateWorkflowInstanceAsync(command, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task StartWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.StartWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task GetWorkflowInstanceByIdAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetWorkflowInstanceByIdAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task> GetWorkflowInstancesAsync(string? query = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetWorkflowInstancesAsync(query, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task SuspendWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.SuspendWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - /// - public virtual async Task ResumeWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.ResumeWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - /// - public virtual async Task CancelWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CancelWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - /// - public virtual async Task ArchiveWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.ArchiveWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return new MemoryStream(result.Data!); - } - - /// - public virtual async Task GetWorkflowInstanceLogsAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetWorkflowInstanceLogsAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task DeleteWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.DeleteWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - #endregion - - #region Correlations - - /// - public virtual async Task CreateCorrelationAsync(V1CreateCorrelationCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CreateCorrelationAsync(command, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task> GetCorrelationsAsync(string? query = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetCorrelationsAsync(query, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task GetCorrelationByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - var result = await this.Adapter.GetCorrelationByIdAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task DeleteCorrelationContextAsync(string correlationId, string contextId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(correlationId)) throw new ArgumentNullException(nameof(correlationId)); - if (string.IsNullOrWhiteSpace(contextId)) throw new ArgumentNullException(nameof(contextId)); - var result = await this.Adapter.DeleteCorrelationContextAsync(new() { CorrelationId = correlationId, ContextId = contextId }, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - /// - public virtual async Task DeleteCorrelationContextEventAsync(string correlationId, string contextId, string eventId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(correlationId)) throw new ArgumentNullException(nameof(correlationId)); - if (string.IsNullOrWhiteSpace(contextId)) throw new ArgumentNullException(nameof(contextId)); - if (string.IsNullOrWhiteSpace(eventId)) throw new ArgumentNullException(nameof(eventId)); - var result = await this.Adapter.DeleteCorrelationContextEventAsync(new() { CorrelationId = correlationId, ContextId = contextId, EventId = eventId }, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - /// - public virtual async Task DeleteCorrelationAsync(string id, CancellationToken cancellationToken = default) - { - await this.Adapter.DeleteCorrelationAsync(id, cancellationToken); - } - - - #endregion region - - #region AuthenticationDefinitionCollections - - /// - public virtual async Task CreateAuthenticationDefinitionCollectionAsync(V1CreateAuthenticationDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CreateAuthenticationDefinitionCollectionAsync(command, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task GetAuthenticationDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetAuthenticationDefinitionCollectionByIdAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - - /// - public virtual async Task> GetAuthenticationDefinitionCollectionsAsync(string? query = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetAuthenticationDefinitionCollectionsAsync(query, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task DeleteAuthenticationDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.DeleteWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - #endregion - - #region EventDefinitionCollections - - /// - public virtual async Task CreateEventDefinitionCollectionAsync(V1CreateEventDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CreateEventDefinitionCollectionAsync(command, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task GetEventDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetEventDefinitionCollectionByIdAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task> GetEventDefinitionCollectionsAsync(string? query = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetEventDefinitionCollectionsAsync(query, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task DeleteEventDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.DeleteWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - #endregion - - #region FunctionDefinitionCollections - - /// - public virtual async Task CreateFunctionDefinitionCollectionAsync(V1CreateFunctionDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CreateFunctionDefinitionCollectionAsync(command, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task GetFunctionDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetFunctionDefinitionCollectionByIdAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task> GetFunctionDefinitionCollectionsAsync(string? query = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetFunctionDefinitionCollectionsAsync(query, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task DeleteFunctionDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.DeleteWorkflowInstanceAsync(id, cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - } - - #endregion - - #region OperationalReports - - /// - public virtual async Task GetOperationalReportAsync(DateTime? date = null, CancellationToken cancellationToken = default) - { - if (!date.HasValue) - date = DateTime.Now; - var result = await this.Adapter.GetOperationalReportAsync(new(date.Value), cancellationToken); - if (!result.Succeeded) - throw new SynapseApiException(result); - return result.Data!; - } - - #endregion - - #region Schedules - - /// - public virtual async Task CreateScheduleAsync(V1CreateScheduleCommand command, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.CreateScheduleAsync(command, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task GetScheduleByIdAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetScheduleByIdAsync(id, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task> GetSchedulesAsync(string? query = null, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.GetSchedulesAsync(query, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - return result.Data!; - } - - /// - public virtual async Task TriggerScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.TriggerScheduleAsync(id, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - } - - /// - public virtual async Task SuspendScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.SuspendScheduleAsync(id, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - } - - /// - public virtual async Task ResumeScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.ResumeScheduleAsync(id, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - } - - /// - public virtual async Task RetireScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.RetireScheduleAsync(id, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - } - - /// - public virtual async Task MakeScheduleObsoleteAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.MakeScheduleObsoleteAsync(id, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - } - - /// - public virtual async Task DeleteScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var result = await this.Adapter.DeleteScheduleAsync(id, cancellationToken); - if (!result.Succeeded) throw new SynapseApiException(result); - } - - #endregion - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Synapse.Apis.Management.Grpc.Client.csproj b/src/apis/management/Synapse.Apis.Management.Grpc.Client/Synapse.Apis.Management.Grpc.Client.csproj deleted file mode 100644 index 0f286ec73..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/Synapse.Apis.Management.Grpc.Client.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Client", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse management api grpc client - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains the GRPC client for the Synapse WFMS - - - - True - \ - - - - - - - - - - \ No newline at end of file diff --git a/src/apis/management/Synapse.Apis.Management.Grpc.Client/SynapseApiException.cs b/src/apis/management/Synapse.Apis.Management.Grpc.Client/SynapseApiException.cs deleted file mode 100644 index 7c2dd97c5..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc.Client/SynapseApiException.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Management -{ - - ///

- /// Represents an exception originating from or concerning the Synapse API - /// - public class SynapseApiException - : Exception - { - - /// - /// Initializes a new - /// - /// The which is the cause of the - public SynapseApiException(GrpcApiResult result) - : base($"The Synapse API responded with a non-success result code '{result.Code}'.{Environment.NewLine}Errors: {(result.Errors == null ? "" : string.Join(Environment.NewLine, result.Errors.Select(e => $"{e.Code}: {e.Message}")))}") - { - - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Grpc/Services/SynapseGrpcManagementApi.cs b/src/apis/management/Synapse.Apis.Management.Grpc/Services/SynapseGrpcManagementApi.cs deleted file mode 100644 index 0f516a580..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc/Services/SynapseGrpcManagementApi.cs +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.Logging; -using Neuroglia; -using Neuroglia.Mapping; -using Neuroglia.Mediation; -using ProtoBuf.Grpc; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Apis.Management.Grpc.Models; -using Synapse.Application.Commands.Generic; -using Synapse.Application.Queries.Generic; -using Synapse.Application.Services; -using Synapse.Integration.Commands.AuthenticationDefinitionCollections; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Commands.EventDefinitionCollections; -using Synapse.Integration.Commands.FunctionDefinitionCollections; -using Synapse.Integration.Commands.Schedules; -using Synapse.Integration.Commands.Workflows; -using Synapse.Integration.Models; - -namespace Synapse.Apis.Management.Grpc; - -///

-/// Represents the default implementation of the , which acts as a GRPC adapter of the Synapse API -/// -public class SynapseGrpcManagementApi - : ISynapseGrpcManagementApi -{ - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to mediate calls - /// The service used to map objects - /// The service used to parse - public SynapseGrpcManagementApi(ILogger logger, IMediator mediator, IMapper mapper, IODataQueryOptionsParser queryOptionsParser) - { - this.Logger = logger; - this.Mediator = mediator; - this.Mapper = mapper; - this.QueryOptionsParser = queryOptionsParser; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - /// - /// Gets the service used to parse - /// - protected IODataQueryOptionsParser QueryOptionsParser { get; } - - #region Application - - /// - public virtual async Task> GetApplicationInfoAsync(CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Queries.Application.V1GetApplicationInfoQuery(), context.CancellationToken)); - } - - #endregion - - #region Workflows - - /// - public virtual async Task> CreateWorkflowAsync(V1CreateWorkflowCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual Task> UploadWorkflowAsync(V1UploadWorkflowCommand command, CallContext context = default) - { - throw new NotSupportedException(); - } - - /// - public virtual async Task> GetWorkflowByIdAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - /// - public virtual async Task>> GetWorkflowsAsync(string? query = null, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FilterQuery(this.QueryOptionsParser.Parse(query)), context.CancellationToken)); - } - - /// - public virtual async Task> ArchiveWorkflowAsync(string id, string? version = null, CallContext context = default) - { - var result = await this.Mediator.ExecuteAsync(new Application.Commands.Workflows.V1ArchiveWorkflowCommand(id, version), context.CancellationToken); - OperationResult toReturn; - if (result.Succeeded) - toReturn = new(((MemoryStream)result.Data!).ToArray()); - else - toReturn = new(result.Code, result.Errors?.ToArray()); - return GrpcApiResult.CreateFor(toReturn); - } - - /// - public virtual async Task DeleteWorkflowAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.Workflows.V1DeleteWorkflowCommand(id), context.CancellationToken)); - } - - #endregion - - #region WorkflowInstances - - /// - public virtual async Task> CreateWorkflowInstanceAsync(Integration.Commands.WorkflowInstances.V1CreateWorkflowInstanceCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> StartWorkflowInstanceAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1StartWorkflowInstanceCommand(id), context.CancellationToken)); - } - - /// - public virtual async Task> GetWorkflowInstanceByIdAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - /// - public virtual async Task>> GetWorkflowInstancesAsync(string? query = null, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FilterQuery(this.QueryOptionsParser.Parse(query)), context.CancellationToken)); - } - - /// - public virtual async Task SuspendWorkflowInstanceAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1SuspendWorkflowInstanceCommand(id), context.CancellationToken)); - } - - /// - public virtual async Task ResumeWorkflowInstanceAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1ResumeWorkflowInstanceCommand(id), context.CancellationToken)); - } - - /// - public virtual async Task CancelWorkflowInstanceAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1CancelWorkflowInstanceCommand(id), context.CancellationToken)); - } - - /// - public virtual async Task> ArchiveWorkflowInstanceAsync(string id, CallContext context = default) - { - var result = await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1ArchiveWorkflowInstanceCommand(id), context.CancellationToken); - OperationResult toReturn; - if (result.Succeeded) - toReturn = new(((MemoryStream)result.Data!).ToArray()); - else - toReturn = new(result.Code, result.Errors?.ToArray()); - return GrpcApiResult.CreateFor(toReturn); - } - - /// - public virtual async Task DeleteWorkflowInstanceAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1DeleteWorkflowInstanceCommand(id), context.CancellationToken)); - } - - /// - public virtual async Task> GetWorkflowInstanceLogsAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Queries.WorkflowInstances.V1GetWorkflowInstanceLogsQuery(id), context.CancellationToken)); - } - - #endregion - - #region Correlations - - /// - public virtual async Task> CreateCorrelationAsync(V1CreateCorrelationCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> GetCorrelationByIdAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - /// - public virtual async Task>> GetCorrelationsAsync(string? query = null, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FilterQuery(this.QueryOptionsParser.Parse(query)), context.CancellationToken)); - } - - /// - public virtual async Task DeleteCorrelationContextAsync(V1DeleteCorrelationContextCommand command, CallContext context = default) - { - if (command == null) throw new ArgumentNullException(nameof(command)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task DeleteCorrelationContextEventAsync(V1DeleteCorrelatedEventCommand command, CallContext context = default) - { - if (command == null) throw new ArgumentNullException(nameof(command)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task DeleteCorrelationAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1DeleteCommand(id), context.CancellationToken)); - } - - #endregion - - #region AuthenticationDefinitionCollections - - /// - public virtual async Task> CreateAuthenticationDefinitionCollectionAsync(V1CreateAuthenticationDefinitionCollectionCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> GetAuthenticationDefinitionCollectionByIdAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - /// - public virtual async Task>> GetAuthenticationDefinitionCollectionsAsync(string? query = null, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FilterQuery(this.QueryOptionsParser.Parse(query)), context.CancellationToken)); - } - - /// - public virtual async Task DeleteAuthenticationDefinitionCollectionAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - #endregion - - #region EventDefinitionCollections - - /// - public virtual async Task> CreateEventDefinitionCollectionAsync(V1CreateEventDefinitionCollectionCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> GetEventDefinitionCollectionByIdAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - /// - public virtual async Task>> GetEventDefinitionCollectionsAsync(string? query = null, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FilterQuery(this.QueryOptionsParser.Parse(query)), context.CancellationToken)); - } - - /// - public virtual async Task DeleteEventDefinitionCollectionAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - #endregion - - #region FunctionDefinitionCollections - - /// - public virtual async Task> CreateFunctionDefinitionCollectionAsync(V1CreateFunctionDefinitionCollectionCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> GetFunctionDefinitionCollectionByIdAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - /// - public virtual async Task>> GetFunctionDefinitionCollectionsAsync(string? query = null, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FilterQuery(this.QueryOptionsParser.Parse(query)), context.CancellationToken)); - } - - /// - public virtual async Task DeleteFunctionDefinitionCollectionAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - #endregion - - #region OperationalReports - - /// - public virtual async Task> GetOperationalReportAsync(GrpcApiRequest request, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(V1OperationalReport.GetIdFor(request.Data)), context.CancellationToken)); - } - - #endregion - - #region Schedules - - /// - public virtual async Task> CreateScheduleAsync(V1CreateScheduleCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> GetScheduleByIdAsync(string id, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FindByIdQuery(id), context.CancellationToken)); - } - - /// - public virtual async Task>> GetSchedulesAsync(string? query = null, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1FilterQuery(this.QueryOptionsParser.Parse(query)), context.CancellationToken)); - } - - /// - public virtual async Task TriggerScheduleAsync(string id, CallContext context = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(id), context.CancellationToken)); - } - - /// - public virtual async Task SuspendScheduleAsync(string id, CallContext context = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(id), context.CancellationToken)); - } - - /// - public virtual async Task ResumeScheduleAsync(string id, CallContext context = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(id), context.CancellationToken)); - } - - /// - public virtual async Task RetireScheduleAsync(string id, CallContext context = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(id), context.CancellationToken)); - } - - /// - public virtual async Task MakeScheduleObsoleteAsync(string id, CallContext context = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(id), context.CancellationToken)); - } - - /// - public virtual async Task DeleteScheduleAsync(string id, CallContext context = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new V1DeleteCommand(id), context.CancellationToken)); - } - - #endregion - -} - diff --git a/src/apis/management/Synapse.Apis.Management.Grpc/Synapse.Apis.Management.Grpc.csproj b/src/apis/management/Synapse.Apis.Management.Grpc/Synapse.Apis.Management.Grpc.csproj deleted file mode 100644 index 4a85b525e..000000000 --- a/src/apis/management/Synapse.Apis.Management.Grpc/Synapse.Apis.Management.Grpc.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse management api grpc - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - - - - - - \ No newline at end of file diff --git a/src/apis/management/Synapse.Apis.Management.Http.Client/Extensions/IServiceCollectionExtensions.cs b/src/apis/management/Synapse.Apis.Management.Http.Client/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 22460de38..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http.Client/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Synapse.Apis.Management; -using Synapse.Apis.Management.Http; - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class IServiceCollectionExtensions - { - - /// - /// Adds and configures an HTTP client for the Synapse Management API - /// - /// The to configure - /// An used to configure the to use - /// The configured - public static IServiceCollection AddSynapseRestApiClient(this IServiceCollection services, Action httpClientSetup) - { - services.AddHttpClient(typeof(SynapseHttpManagementApiClient).Name, http => httpClientSetup(http)); - services.TryAddSingleton(); - return services; - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http.Client/Extensions/OperationTypeExtensions.cs b/src/apis/management/Synapse.Apis.Management.Http.Client/Extensions/OperationTypeExtensions.cs deleted file mode 100644 index f299d6546..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http.Client/Extensions/OperationTypeExtensions.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.OpenApi.Models; - -namespace Synapse -{ - - ///

- /// Defines extensions for s - /// - public static class OperationTypeExtensions - { - - /// - /// Converts the into a new - /// - /// The to convert - /// The reuslting - public static HttpMethod ToHttpMethod(this OperationType operationType) - { - switch (operationType) - { - case OperationType.Delete: - return HttpMethod.Delete; - case OperationType.Get: - return HttpMethod.Get; - case OperationType.Head: - return HttpMethod.Head; - case OperationType.Options: - return HttpMethod.Options; - case OperationType.Patch: - return HttpMethod.Patch; - case OperationType.Post: - return HttpMethod.Post; - case OperationType.Put: - return HttpMethod.Put; - case OperationType.Trace: - return HttpMethod.Trace; - default: - throw new NotSupportedException($"The specified {nameof(OperationType)} '{operationType}' is not supported"); - } - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http.Client/Services/SynapseHttpManagementApiClient.cs b/src/apis/management/Synapse.Apis.Management.Http.Client/Services/SynapseHttpManagementApiClient.cs deleted file mode 100644 index e066a3b0b..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http.Client/Services/SynapseHttpManagementApiClient.cs +++ /dev/null @@ -1,730 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.Logging; -using Neuroglia.Serialization; -using Synapse.Integration.Commands.AuthenticationDefinitionCollections; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Commands.EventDefinitionCollections; -using Synapse.Integration.Commands.FunctionDefinitionCollections; -using Synapse.Integration.Commands.Schedules; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Commands.Workflows; -using Synapse.Integration.Models; -using System.Net.Mime; -using System.Text; - -namespace Synapse.Apis.Management.Http; - -///

-/// Represents the service used to interact with the Synapse HTTP REST API -/// -public class SynapseHttpManagementApiClient - : ISynapseManagementApi -{ - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to create s - /// The service used to serialize/deserialize to/from JSON - public SynapseHttpManagementApiClient(ILogger logger, IHttpClientFactory httpClientFactory, IJsonSerializer serializer) - { - this.Logger = logger; - this.HttpClient = httpClientFactory.CreateClient(this.GetType().Name); - this.Serializer = serializer; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the used to request the Synapse HTTP REST API - /// - protected HttpClient HttpClient { get; } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer Serializer { get; } - - /// - /// Creates a new - /// - /// The of the to create - /// The request of the to create - /// The of the to create - /// A new - protected virtual HttpRequestMessage CreateRequest(HttpMethod method, string requestUri, HttpContent? content = null) - { - var request = new HttpRequestMessage(method, requestUri) { Content = content }; - return request; - } - - #region Application - - /// - public virtual async Task GetApplicationInfoAsync(CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/application/info"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while retrieving information about the running Synapse application: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - #endregion - - #region Workflows - - /// - public virtual async Task CreateWorkflowAsync(V1CreateWorkflowCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/workflows"; - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - var json = await this.Serializer.SerializeAsync(command, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new workflow: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task UploadWorkflowAsync(V1UploadWorkflowCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/workflows/upload"; - using var content = new MultipartFormDataContent(); - content.Add(new StreamContent(command.DefinitionFile.OpenReadStream()), nameof(V1UploadWorkflowCommand.DefinitionFile), command.DefinitionFile.FileName); - foreach (var resourceFile in command.ResourceFiles) - { - content.Add(new StreamContent(resourceFile.OpenReadStream()), nameof(V1UploadWorkflowCommand.ResourceFiles), resourceFile.FileName); - } - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - request.Content = content; - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new workflow: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task> GetWorkflowsAsync(string? query, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/workflows"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?{query}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying workflows: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task> GetWorkflowsAsync(CancellationToken cancellationToken = default) - { - return await this.GetWorkflowsAsync(null!, cancellationToken); - } - - /// - public virtual async Task> SearchWorkflowsAsync(string term, string? query = null, CancellationToken cancellationToken = default) - { - var requestUri = "/api/odata/v1workflows"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?$search={term}{(string.IsNullOrWhiteSpace(query) ? string.Empty : $"&{query}")}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying workflows: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task GetWorkflowByIdAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/workflows/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying workflows: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task ArchiveWorkflowAsync(string id, string? version = null, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/workflows/{id}/archive"; - return await this.HttpClient.GetStreamAsync(requestUri, cancellationToken); - } - - /// - public virtual async Task DeleteWorkflowAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/workflows/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the workflow with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - #endregion region - - #region WorkflowInstances - - /// - public virtual async Task CreateWorkflowInstanceAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/workflow-instances"; - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - var json = await this.Serializer.SerializeAsync(command, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new workflow instance: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task StartWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/workflow-instances/byid/{id}/start"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while starting the workflow instance with id '{workflowInstanceId}': {details}", id, json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task GetWorkflowInstanceByIdAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/workflow-instances/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying workflow instances: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task> GetWorkflowInstancesAsync(string? query, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/workflow-instances"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?{query}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying workflows: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task> GetWorkflowInstancesAsync(CancellationToken cancellationToken = default) - { - return await this.GetWorkflowInstancesAsync(null, cancellationToken); - } - - /// - public virtual async Task SuspendWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/workflow-instances/{id}/suspend"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while suspending the execution of the workflow with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task ResumeWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/workflow-instances/{id}/resume"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while resuming the execution of the workflow with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task CancelWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/workflow-instances/{id}/cancel"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while cancelling the execution of the workflow instance with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task GetWorkflowInstanceLogsAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/workflow-instances/{id}/logs"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying the log of the workflow instance with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - return json; - } - - /// - public virtual async Task ArchiveWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/workflow-instances/{id}/logs"; - return await this.HttpClient.GetStreamAsync(requestUri, cancellationToken); - } - - /// - public virtual async Task DeleteWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/workflow-instances/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying deleting the workflow instance with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - #endregion - - #region Correlations - - /// - public virtual async Task CreateCorrelationAsync(V1CreateCorrelationCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/correlations"; - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - var json = await this.Serializer.SerializeAsync(command, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new correlation: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task> GetCorrelationsAsync(string? query, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/correlations"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?{query}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying correlations: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task> GetCorrelationsAsync(CancellationToken cancellationToken = default) - { - return await this.GetCorrelationsAsync(null!, cancellationToken); - } - - /// - public virtual async Task GetCorrelationByIdAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/correlations/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying correlations: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task DeleteCorrelationContextAsync(string correlationId, string contextId, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/correlations/byid/{correlationId}/contexts/{contextId}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the context with id '{contextId}', owned by the correlation with id '{correlationId}': {details}", contextId, correlationId, json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task DeleteCorrelationContextEventAsync(string correlationId, string contextId, string eventId, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/correlations/byid/{correlationId}/contexts/{contextId}/events/{eventId}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the event with id '{eventId}', owned by the correlation context with id '{contextId}': {details}", eventId, contextId, json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task DeleteCorrelationAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/correlations/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the correlation with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - #endregion region - - #region AuthenticationDefinitionCollections - - /// - public virtual async Task CreateAuthenticationDefinitionCollectionAsync(V1CreateAuthenticationDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/resources/collections/authentications"; - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - var json = await this.Serializer.SerializeAsync(command, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new authentication definition collection: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task GetAuthenticationDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/resources/collections/authentications/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying authentication definition collections: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task> GetAuthenticationDefinitionCollectionsAsync(string? query, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/resources/collections/authentications"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?{query}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying authentication definition collections: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task DeleteAuthenticationDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/resources/collections/authentications/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the authentication definition collection with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - #endregion - - #region EventDefinitionCollections - - /// - public virtual async Task CreateEventDefinitionCollectionAsync(V1CreateEventDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/resources/collections/events"; - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - var json = await this.Serializer.SerializeAsync(command, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new e definition collection: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task GetEventDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/resources/collections/events/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying e definition collections: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task> GetEventDefinitionCollectionsAsync(string? query, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/resources/collections/events"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?{query}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying e definition collections: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task DeleteEventDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/resources/collections/events/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the e definition collection with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - #endregion - - #region FunctionDefinitionCollections - - /// - public virtual async Task CreateFunctionDefinitionCollectionAsync(V1CreateFunctionDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/resources/collections/functions"; - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - var json = await this.Serializer.SerializeAsync(command, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new function definition collection: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task GetFunctionDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/resources/collections/functions/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying function definition collections: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task> GetFunctionDefinitionCollectionsAsync(string? query, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/resources/collections/functions"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?{query}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying function definition collections: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task DeleteFunctionDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/resources/collections/functions/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the function definition collection with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - #endregion - - #region OperationalReports - - /// - public virtual async Task GetOperationalReportAsync(DateTime? date = null, CancellationToken cancellationToken = default) - { - if (date == null) - date = DateTime.Now; - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/reports/operations?date={date}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying operation reports: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - #endregion - - #region Schedules - - /// - public virtual async Task CreateScheduleAsync(V1CreateScheduleCommand command, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/schedules"; - using var request = this.CreateRequest(HttpMethod.Post, requestUri); - var json = await this.Serializer.SerializeAsync(command, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while creating a new schedule: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task GetScheduleByIdAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Get, $"/api/v1/schedules/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying schedules: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync(json, cancellationToken); - } - - /// - public virtual async Task> GetSchedulesAsync(string query, CancellationToken cancellationToken = default) - { - var requestUri = "/api/v1/schedules"; - if (!string.IsNullOrWhiteSpace(query)) - requestUri += $"?{query}"; - using var request = this.CreateRequest(HttpMethod.Get, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while querying schedules: {details}", json); - response.EnsureSuccessStatusCode(); - return await this.Serializer.DeserializeAsync>(json, cancellationToken); - } - - /// - public virtual async Task> GetSchedulesAsync(CancellationToken cancellationToken = default) => await this.GetSchedulesAsync(null!, cancellationToken); - - /// - public virtual async Task TriggerScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/schedules/{id}/trigger"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while triggering a new schedule occurence: {details}", json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task SuspendScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/schedules/{id}/suspend"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while suspending a schedule: {details}", json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task ResumeScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/schedules/{id}/resume"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while resuming a schedule: {details}", json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task RetireScheduleAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/schedules/{id}/retire"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while retiring a schedule: {details}", json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task MakeScheduleObsoleteAsync(string id, CancellationToken cancellationToken = default) - { - var requestUri = $"/api/v1/schedules/{id}/obsolete"; - using var request = this.CreateRequest(HttpMethod.Put, requestUri); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while making a schedule obsolete: {details}", json); - response.EnsureSuccessStatusCode(); - } - - /// - public virtual async Task DeleteScheduleAsync(string id, CancellationToken cancellationToken = default) - { - using var request = this.CreateRequest(HttpMethod.Delete, $"/api/v1/schedules/{id}"); - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - this.Logger.LogError("An error occured while deleting the schedule with the specified id '{id}': {details}", id, json); - response.EnsureSuccessStatusCode(); - } - - #endregion - -} - diff --git a/src/apis/management/Synapse.Apis.Management.Http.Client/Synapse.Apis.Management.Http.Client.csproj b/src/apis/management/Synapse.Apis.Management.Http.Client/Synapse.Apis.Management.Http.Client.csproj deleted file mode 100644 index baea580ae..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http.Client/Synapse.Apis.Management.Http.Client.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Client", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse management api http client - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains the HTTP client for the management API of Synapse WFMS - - - - \ - True - - - - - - - - - \ No newline at end of file diff --git a/src/apis/management/Synapse.Apis.Management.Http/ApiController.cs b/src/apis/management/Synapse.Apis.Management.Http/ApiController.cs deleted file mode 100644 index 2e0c867ca..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/ApiController.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Management.Http -{ - - ///

- /// Represents the base class for all the application's implementations - /// - public abstract class ApiController - : ControllerBase - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - protected ApiController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Mediator = mediator; - this.Mapper = mapper; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1ApplicationController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1ApplicationController.cs deleted file mode 100644 index 994525b2b..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1ApplicationController.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage the Synapse application - /// - [Tags("Application")] - [Route("api/v1/application")] - public class V1ApplicationController - : ApiController - { - - /// - public V1ApplicationController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) : base(loggerFactory, mediator, mapper) { } - - /// - /// Gets information about the running Synapse instance - /// - /// A - /// A new - [HttpGet("info")] - [ProducesResponseType(typeof(V1ApplicationInfo), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetApplicationInfo(CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Application.V1GetApplicationInfoQuery(), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1AuthenticationDefinitionCollectionsController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1AuthenticationDefinitionCollectionsController.cs deleted file mode 100644 index 06b11d906..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1AuthenticationDefinitionCollectionsController.cs +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage authentication definition collections - /// - [Tags("AuthenticationDefinitionCollections")] - [Route("api/v1/resources/collections/authentications")] - public class V1AuthenticationDefinitionCollectionsController - : ApiController - { - - /// - public V1AuthenticationDefinitionCollectionsController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Creates a new authentication definition collection - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost] - [ProducesResponseType(typeof(Integration.Models.V1AuthenticationDefinitionCollection), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CreateWorkflow([FromBody] Integration.Commands.AuthenticationDefinitionCollections.V1CreateAuthenticationDefinitionCollectionCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Gets the authentication definition collection with the specified id - /// - /// The id of the authentication definition collection to get - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1AuthenticationDefinitionCollection), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public virtual async Task GetAuthenticationCollectionById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(Application.Queries.AuthenticationDefinitionCollections.V1GetAuthenticationDefinitionCollectionByIdQuery.Parse(id), cancellationToken)); - } - - /// - /// Gets the raw authentication definition collection with the specified id - /// - /// The id of the authentication definition collection to get - /// A - /// A new - [HttpGet("{id}/raw")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public virtual async Task GetRawAuthenticationCollectionById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(Application.Queries.AuthenticationDefinitionCollections.V1GetRawAuthenticationDefinitionCollectionByIdQuery.Parse(id), cancellationToken)); - } - - /// - /// Queries authentication definition collections - /// This endpoint supports ODATA. - /// - /// The options of the ODATA query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetAuthenticationCollections(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - /// - /// Deletes an existing authentication definition collection - /// - /// The id of the authentication definition collection to delete - /// A - /// A new - [HttpDelete("{id}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteAuthenticationCollection(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1CorrelationsController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1CorrelationsController.cs deleted file mode 100644 index 3c8c8388d..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1CorrelationsController.cs +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage correlation definitions - /// - [Tags("Correlations")] - [Route("api/v1/correlations")] - public class V1CorrelationsController - : ApiController - { - - /// - public V1CorrelationsController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Creates a new correlation - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost] - [ProducesResponseType(typeof(Integration.Models.V1Correlation), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CreateCorrelation([FromBody] Integration.Commands.Correlations.V1CreateCorrelationCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Gets the correlation with the specified id. - /// - /// The id of the correlation to find - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1Correlation), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetCorrelationById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken)); - } - - /// - /// Queries correlations - /// This endpoint supports ODATA. - /// - /// The options used to configure the query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetCorrelations(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - /// - /// Patches an existing correlation - /// - /// An object used to describe the command to execute - /// A - /// A new - [HttpPatch] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task PatchCorrelation([FromBody] Integration.Commands.Generic.V1PatchCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map>(command), cancellationToken)); - } - - /// - /// Deletes an existing correlation context - /// - /// The id of the correlation the context to delete belongs to - /// The id of the correlation context to delete - /// A - /// A new - [HttpDelete("byid/{correlationId}/contexts/{contextId}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteCorrelationContext(string correlationId, string contextId, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Correlations.V1DeleteCorrelationContextCommand(correlationId, contextId), cancellationToken), (int)HttpStatusCode.NoContent); - } - - /// - /// Deletes a correlated event - /// - /// The id of the correlation the correlated event to delete belongs to - /// The id of the context the correlated event to delete belongs to - /// The id of the correlated event to delete - /// A - /// A new - [HttpDelete("byid/{correlationId}/contexts/{contextId}/events/{eventId}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteCorrelatedEvent(string correlationId, string contextId, string eventId, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Correlations.V1DeleteCorrelatedEventCommand(correlationId, contextId, eventId), cancellationToken), (int)HttpStatusCode.NoContent); - } - - /// - /// Deletes an existing correlation - /// - /// The id of the correlation to delete - /// A - /// A new - [HttpDelete("byid/{id}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteCorrelation(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken), (int)HttpStatusCode.NoContent); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1EventDefinitionCollectionsController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1EventDefinitionCollectionsController.cs deleted file mode 100644 index a755f93ae..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1EventDefinitionCollectionsController.cs +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage event definition collections - /// - [Tags("EventDefinitionCollections")] - [Route("api/v1/resources/collections/events")] - public class V1EventDefinitionCollectionsController - : ApiController - { - - /// - public V1EventDefinitionCollectionsController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Creates a new event definition collection - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost] - [ProducesResponseType(typeof(Integration.Models.V1EventDefinitionCollection), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CreateWorkflow([FromBody] Integration.Commands.EventDefinitionCollections.V1CreateEventDefinitionCollectionCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Gets the event definition collection with the specified id - /// - /// The id of the event definition collection to get - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1EventDefinitionCollection), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public virtual async Task GetEventCollectionById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(Application.Queries.EventDefinitionCollections.V1GetEventDefinitionCollectionByIdQuery.Parse(id), cancellationToken)); - } - - /// - /// Gets the raw event definition collection with the specified id - /// - /// The id of the event definition collection to get - /// A - /// A new - [HttpGet("{id}/raw")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public virtual async Task GetRawEventCollectionById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(Application.Queries.EventDefinitionCollections.V1GetRawEventDefinitionCollectionByIdQuery.Parse(id), cancellationToken)); - } - - /// - /// Queries event definition collections - /// This endpoint supports ODATA. - /// - /// The options of the ODATA query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetEventCollections(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - /// - /// Deletes an existing event definition collection - /// - /// The id of the event definition collection to delete - /// A - /// A new - [HttpDelete("{id}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteEventCollection(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1FunctionDefinitionCollectionsController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1FunctionDefinitionCollectionsController.cs deleted file mode 100644 index cf3ad8134..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1FunctionDefinitionCollectionsController.cs +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage function definition collections - /// - [Tags("FunctionDefinitionCollections")] - [Route("api/v1/resources/collections/functions")] - public class V1FunctionDefinitionCollectionsController - : ApiController - { - - /// - public V1FunctionDefinitionCollectionsController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Creates a new function definition collection - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost] - [ProducesResponseType(typeof(Integration.Models.V1FunctionDefinitionCollection), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CreateWorkflow([FromBody] Integration.Commands.FunctionDefinitionCollections.V1CreateFunctionDefinitionCollectionCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Gets the function definition collection with the specified id - /// - /// The id of the function definition collection to get - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1FunctionDefinitionCollection), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public virtual async Task GetFunctionCollectionById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(Application.Queries.FunctionDefinitionCollections.V1GetFunctionDefinitionCollectionByIdQuery.Parse(id), cancellationToken)); - } - - /// - /// Gets the raw function definition collection with the specified id - /// - /// The id of the function definition collection to get - /// A - /// A new - [HttpGet("{id}/raw")] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public virtual async Task GetRawFunctionCollectionById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(Application.Queries.FunctionDefinitionCollections.V1GetRawFunctionDefinitionCollectionByIdQuery.Parse(id), cancellationToken)); - } - - /// - /// Queries function definition collections - /// This endpoint supports ODATA. - /// - /// The options of the ODATA query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetFunctionCollections(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - /// - /// Deletes an existing function definition collection - /// - /// The id of the function definition collection to delete - /// A - /// A new - [HttpDelete("{id}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteFunctionCollection(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1OperationalReportsController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1OperationalReportsController.cs deleted file mode 100644 index 68995c3f1..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1OperationalReportsController.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage application metrics - /// - [Tags("OperationalReports")] - [Route("api/v1/reports/operations")] - public class V1OperationalReportsController - : ApiController - { - - /// - public V1OperationalReportsController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Gets the for the specified date - /// - /// The date for which to get the . Defaults to today - /// A - /// A new - [HttpGet] - [ProducesResponseType(typeof(V1OperationalReport), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetOperationalReport(DateTime? date = null, CancellationToken cancellationToken = default) - { - if (!date.HasValue) - date = DateTime.Now; - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FindByIdQuery(V1OperationalReport.GetIdFor(date.Value)), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1SchedulesController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1SchedulesController.cs deleted file mode 100644 index 99e600f30..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1SchedulesController.cs +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage schedules - /// - [Tags("Schedules")] - [Route("api/v1/schedules")] - public class V1SchedulesController - : ApiController - { - - /// - public V1SchedulesController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) : base(loggerFactory, mediator, mapper) { } - - /// - /// Creates a new schedule - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost] - [ProducesResponseType(typeof(Integration.Models.V1Schedule), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CreateSchedule([FromBody] Integration.Commands.Schedules.V1CreateScheduleCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Gets the schedule with the specified id. - /// - /// The id of the schedule to find - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1Schedule), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetScheduleById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken)); - } - - /// - /// Queries schedules - /// This endpoint supports ODATA. - /// - /// The options of the ODATA query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetSchedules(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - /// - /// Patches the specified schedule - /// - /// An object that describes the command to execute - /// A - /// A new - [HttpPatch] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task PatchSchedule(Integration.Commands.Generic.V1PatchCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map>(command), cancellationToken)); - } - - /// - /// Triggers a new occurence the specified schedule - /// - /// The id of the schedule to trigger - /// A - /// A new - [HttpPut("{id}/trigger")] - [ProducesResponseType(typeof(Integration.Models.V1Schedule), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task TriggerSchedule(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Schedules.V1TriggerScheduleCommand(id), cancellationToken)); - } - - /// - /// Suspends the specified schedule - /// - /// The id of the schedule to suspend - /// A - /// A new - [HttpPut("{id}/suspend")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task SuspendSchedule(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Schedules.V1SuspendScheduleCommand(id), cancellationToken)); - } - - /// - /// Resumes the specified schedule - /// - /// The id of the schedule to resume - /// A - /// A new - [HttpPut("{id}/resume")] - [ProducesResponseType(typeof(Integration.Models.V1Schedule), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task ResumeSchedule(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Schedules.V1ResumeScheduleCommand(id), cancellationToken)); - } - - /// - /// Retires the specified schedule - /// - /// The id of the schedule to retire - /// A - /// A new - [HttpPut("{id}/retire")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task RetireSchedule(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Schedules.V1RetireScheduleCommand(id), cancellationToken)); - } - - /// - /// Makes the specified schedule obsolete - /// - /// The id of the schedule to make obsolete - /// A - /// A new - [HttpPut("{id}/obsolete")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task MakeScheduleObsolete(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Schedules.V1MakeScheduleObsoleteCommand(id), cancellationToken)); - } - - /// - /// Deletes an existing schedule - /// - /// The id of the schedule to delete - /// A - /// A new - [HttpDelete("{id}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteSchedule(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowActivitiesController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowActivitiesController.cs deleted file mode 100644 index 91584da06..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowActivitiesController.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage workflow activities - /// - [Tags("WorkflowActivities")] - [Route("api/v1/workflow-activities")] - public class V1WorkflowActivitiesController - : ApiController - { - - /// - public V1WorkflowActivitiesController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Gets the workflow activity with the specified id. - /// - /// The id of the workflow activity to find - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1WorkflowActivity), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflowActivityById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken)); - } - - /// - /// Queries workflow activities - /// This endpoint supports ODATA. - /// - /// The options used to configure the query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflowActivities(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowInstancesController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowInstancesController.cs deleted file mode 100644 index 7c88f4a20..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowInstancesController.cs +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; -using System.Net.Mime; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage workflow instances - /// - [Tags("WorkflowInstances")] - [Route("api/v1/workflow-instances")] - public class V1WorkflowInstancesController - : ApiController - { - - /// - public V1WorkflowInstancesController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Creates a new workflow instance - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost] - [ProducesResponseType(typeof(Integration.Models.V1WorkflowInstance), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CreateWorkflowInstance([FromBody] Integration.Commands.WorkflowInstances.V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Gets the workflow instance with the specified id. - /// - /// The id of the workflow instance to find - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1WorkflowInstance), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflowInstanceById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken)); - } - - /// - /// Queries workflow instances - /// This endpoint supports ODATA. - /// - /// The options used to configure the query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflowInstance(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - /// - /// Starts the workflow instance with the specified id - /// - /// The id of the workflow instance to start - /// A - /// A new - [HttpPut("byid/{id}/start")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task StartWorkflowInstance(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1StartWorkflowInstanceCommand(id), cancellationToken)); - } - - /// - /// Patches an existing workflow instance - /// - /// An object used to describe the command to execute - /// A - /// A new - [HttpPatch] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task PatchWorkflowInstance([FromBody] Integration.Commands.Generic.V1PatchCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map>(command), cancellationToken)); - } - - /// - /// Suspends the execution of an existing workflow instance - /// - /// The id of the workflow definition to suspend the execution of - /// A - /// A new - [HttpPut("{id}/suspend")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task SuspendWorkflowInstance(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1SuspendWorkflowInstanceCommand(id), cancellationToken)); - } - - /// - /// Resumes the execution of an existing workflow instance - /// - /// The id of the workflow definition to resume the execution of - /// A - /// A new - [HttpPut("{id}/resume")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task ResumeWorkflowInstance(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1ResumeWorkflowInstanceCommand(id), cancellationToken)); - } - - /// - /// Cancels the execution of an existing workflow instance - /// - /// The id of the workflow instance to cancel the execution of - /// A - /// A new - [HttpPut("{id}/cancel")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CancelWorkflowInstance(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1CancelWorkflowInstanceCommand(id), cancellationToken)); - } - - /// - /// Donwloads the archive of an existing workflow instance - /// - /// The id of the workflow instance to archive - /// A - /// A new - [HttpGet("{id}/archive")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task ArchiveWorkflowInstance(string id, CancellationToken cancellationToken) - { - var result = await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1ArchiveWorkflowInstanceCommand(id), cancellationToken); - if (!result.Succeeded) - return this.Process(result); - return this.File(result.Data, MediaTypeNames.Application.Zip, $"{id}.zip"); - } - - /// - /// Gets the aggregated logs of the specified workflow instance - /// - /// The id of the workflow instance to get the logs of - /// A - /// A new - [HttpGet("{id}/logs")] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflowInstanceLogs(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.WorkflowInstances.V1GetWorkflowInstanceLogsQuery(id), cancellationToken)); - } - - /// - /// Deletes an existing workflow instance - /// - /// The id of the workflow definition to delete - /// A - /// A new - [HttpDelete("{id}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteWorkflowInstance(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1DeleteWorkflowInstanceCommand(id), cancellationToken), (int)HttpStatusCode.Created); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowsController.cs b/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowsController.cs deleted file mode 100644 index 43d2010ab..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Controllers/V1WorkflowsController.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; -using System.Net.Mime; - -namespace Synapse.Apis.Management.Http.Controllers -{ - - ///

- /// Represents the used to manage workflow definitions - /// - [Tags("Workflows")] - [Route("api/v1/workflows")] - public class V1WorkflowsController - : ApiController - { - - /// - public V1WorkflowsController(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - : base(loggerFactory, mediator, mapper) - { - - } - - /// - /// Creates a new workflow - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost] - [ProducesResponseType(typeof(Integration.Models.V1Workflow), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task CreateWorkflow([FromBody] Integration.Commands.Workflows.V1CreateWorkflowCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Uploads a new workflow - /// - /// An object that represents the command to execute - /// A - /// A new - [HttpPost("upload")] - [ProducesResponseType(typeof(Integration.Models.V1Workflow), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task UploadWorkflow([FromForm] Integration.Commands.Workflows.V1UploadWorkflowCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), cancellationToken), (int)HttpStatusCode.Created); - } - - /// - /// Gets the workflow with the specified id. - /// - /// The id of the workflow to find - /// A - /// A new - [HttpGet("{id}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1Workflow), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflowById(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(Application.Queries.Workflows.V1GetWorkflowByIdQuery.Parse(id), cancellationToken)); - } - - /// - /// Gets the workflow instance with the specified id. - /// - /// The id of the workflow of the instance to get - /// The key of the specified workflow's instance to find - /// A - /// A new - [HttpGet("{id}/{instanceKey}"), EnableQuery] - [ProducesResponseType(typeof(Integration.Models.V1WorkflowInstance), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflowInstanceByKey(string id, string instanceKey, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FindByIdQuery($"{id}-{instanceKey}"), cancellationToken)); - } - - /// - /// Queries workflows - /// This endpoint supports ODATA. - /// - /// The options of the ODATA query to perform - /// A - /// A new - [HttpGet, EnableQuery] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task GetWorkflows(ODataQueryOptions queryOptions, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Queries.Generic.V1FilterQuery(queryOptions), cancellationToken)); - } - - /// - /// Patches an existing workflow - /// - /// An object used to describe the command to execute - /// A - /// A new - [HttpPatch] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task PatchWorkflow([FromBody] Integration.Commands.Generic.V1PatchCommand command, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(this.Mapper.Map>(command), cancellationToken)); - } - - /// - /// Donwloads the archive of an existing workflow - /// - /// The id of the workflow to archive - /// The version of the workflow to archive - /// A - /// A new - [HttpGet("{id}/archive")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task ArchiveWorkflow(string id, string? version, CancellationToken cancellationToken) - { - var result = await this.Mediator.ExecuteAsync(new Application.Commands.Workflows.V1ArchiveWorkflowCommand(id, version), cancellationToken); - if (!result.Succeeded) - return this.Process(result); - return this.File(result.Data, MediaTypeNames.Application.Zip, $"{id}.zip"); - } - - /// - /// Deletes an existing workflow - /// - /// The id of the workflow to delete - /// A - /// A new - [HttpDelete("{id}")] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public async Task DeleteWorkflow(string id, CancellationToken cancellationToken) - { - return this.Process(await this.Mediator.ExecuteAsync(new Application.Commands.Workflows.V1DeleteWorkflowCommand(id), cancellationToken)); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Extensions/ISynapseApplicationBuilderExtensions.cs b/src/apis/management/Synapse.Apis.Management.Http/Extensions/ISynapseApplicationBuilderExtensions.cs deleted file mode 100644 index a3d5ec275..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Extensions/ISynapseApplicationBuilderExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.AspNetCore.OData; -using Microsoft.AspNetCore.OData.NewtonsoftJson; -using Microsoft.AspNetCore.OData.Query.Expressions; -using Microsoft.AspNetCore.OData.Routing.Controllers; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.OpenApi.Models; -using Neuroglia; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Synapse.Apis.Management.Http.Services; -using Synapse.Application.Configuration; -using Synapse.Application.Services; -using Synapse.Integration.Serialization.Converters; - -namespace Synapse.Apis.Management.Http -{ - - ///

- /// Defines extensions for s - /// - public static class ISynapseApplicationBuilderExtensions - { - - /// - /// Configures Synapse to use the Http REST API port - /// - /// The to configure - /// The configured - public static ISynapseApplicationBuilder UseHttpManagementApi(this ISynapseApplicationBuilder synapse) - { - var searchBinder = new ODataSearchBinder(); - synapse.Services.AddSingleton(searchBinder); - synapse.Services - .AddControllers() - .AddOData((options, provider) => - { - IEdmModelBuilder builder = provider.GetRequiredService(); - options.AddRouteComponents("api/odata", builder.Build(), services => services.AddSingleton(searchBinder)) - .EnableQueryFeatures(50); - options.RouteOptions.EnableControllerNameCaseInsensitive = true; - options.RouteOptions.EnableActionNameCaseInsensitive = true; - options.RouteOptions.EnablePropertyNameCaseInsensitive = true; - }) - .AddODataNewtonsoftJson() - .AddNewtonsoftJson(options => - { - options.SerializerSettings.ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }; - options.SerializerSettings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor; - options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; - options.SerializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore; - options.SerializerSettings.DateFormatHandling = DateFormatHandling.IsoDateFormat; - options.SerializerSettings.Converters.Add(new AbstractClassConverterFactory()); - options.SerializerSettings.Converters.Add(new FilteredExpandoObjectConverter()); - }) - .AddApplicationPart(typeof(ISynapseApplicationBuilderExtensions).Assembly) - .AddApplicationPart(typeof(MetadataController).Assembly); - synapse.Services.AddSwaggerGen(builder => - { - builder.OperationFilter(); - builder.SchemaFilter(); - builder.DocumentFilter(); - builder.CustomOperationIds(o => - { - if (!string.IsNullOrWhiteSpace(o.RelativePath) && o.RelativePath.StartsWith("odata")) return o.RelativePath; - var action = (ControllerActionDescriptor)o.ActionDescriptor; - return $"{action.ActionName}".ToCamelCase(); - }); - builder.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); - builder.SwaggerDoc("v1", new OpenApiInfo - { - Title = "Synapse API", - Version = "v1", - Description = "The Open API documentation for the Synapse API", - Contact = new() - { - Name = "The Synapse Authors", - Url = new Uri("https://github.com/serverlessworkflow/synapse/") - } - }); - builder.IncludeXmlComments(typeof(ISynapseApplicationBuilderExtensions).Assembly.Location.Replace(".dll", ".xml")); - builder.IncludeXmlComments(typeof(Integration.Models.V1Workflow).Assembly.Location.Replace(".dll", ".xml")); - }); - return synapse; - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Properties/GlobalUsings.cs b/src/apis/management/Synapse.Apis.Management.Http/Properties/GlobalUsings.cs deleted file mode 100644 index 45d0692ed..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Properties/GlobalUsings.cs +++ /dev/null @@ -1,7 +0,0 @@ -global using Microsoft.AspNetCore.Mvc; -global using Microsoft.AspNetCore.OData.Query; -global using Microsoft.Extensions.Logging; -global using Neuroglia.Mapping; -global using Neuroglia.Mediation; -global using Synapse.Integration.Models; -global using System.Net; diff --git a/src/apis/management/Synapse.Apis.Management.Http/Properties/launchSettings.json b/src/apis/management/Synapse.Apis.Management.Http/Properties/launchSettings.json deleted file mode 100644 index eb1ac5836..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:51792/", - "sslPort": 44303 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "Synapse.Ports.HttpRest": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/src/apis/management/Synapse.Apis.Management.Http/Services/IgnorExternalSchemaFilter.cs b/src/apis/management/Synapse.Apis.Management.Http/Services/IgnorExternalSchemaFilter.cs deleted file mode 100644 index 8f367d88e..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Services/IgnorExternalSchemaFilter.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Synapse.Apis.Management.Http.Services -{ - ///

- /// Represents the object used to filter schemas described by the generated OpenAPI documentation - /// - public class IgnorExternalSchemaFilter - : ISchemaFilter, IDocumentFilter - { - - /// - /// Gets a containing all the schemas to include in the generated OpenAPI documentation - /// - internal protected static readonly List IncludedSchemas = new(); - - /// - public void Apply(OpenApiSchema schema, SchemaFilterContext context) - { - if (context.Type.Namespace!.StartsWith("Neuroglia") - || context.Type.Namespace!.StartsWith("Synapse") - || context.Type.Namespace.StartsWith("ServerlessWorkflow") - || context.Type.Namespace == "System") - IncludedSchemas.Add(context.Type.Name); - } - - /// - public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) - { - foreach(var schema in swaggerDoc.Components.Schemas.ToList()) - { - if (IncludedSchemas.Contains(schema.Key)) continue; - swaggerDoc.Components.Schemas.Remove(schema.Key); - } - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Services/IgnoreODataQueryOptionOperationFilter.cs b/src/apis/management/Synapse.Apis.Management.Http/Services/IgnoreODataQueryOptionOperationFilter.cs deleted file mode 100644 index 9e28ced70..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Services/IgnoreODataQueryOptionOperationFilter.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Synapse.Apis.Management.Http.Services -{ - - ///

- /// Represents the used to ignore parameters - /// - public class IgnoreODataQueryOptionOperationFilter - : IOperationFilter - { - - /// - public virtual void Apply(OpenApiOperation operation, OperationFilterContext context) - { - context.ApiDescription.ParameterDescriptions - .Where(param => param.ParameterDescriptor.ParameterType.IsGenericType && param.ParameterDescriptor.ParameterType.GetGenericTypeDefinition() == typeof(ODataQueryOptions<>)) - .ToList() - .ForEach(param => - { - var parameter = operation.Parameters.SingleOrDefault(p => p.Name == param.Name); - if (parameter != null) operation.Parameters.Remove(parameter); - }); - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Http/Synapse.Apis.Management.Http.csproj b/src/apis/management/Synapse.Apis.Management.Http/Synapse.Apis.Management.Http.csproj deleted file mode 100644 index e1611b298..000000000 --- a/src/apis/management/Synapse.Apis.Management.Http/Synapse.Apis.Management.Http.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse management api http - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apis/management/Synapse.Apis.Management.Ipc.Client/Extensions/IServiceCollectionExtensions.cs b/src/apis/management/Synapse.Apis.Management.Ipc.Client/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 5e1ffa0f5..000000000 --- a/src/apis/management/Synapse.Apis.Management.Ipc.Client/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Synapse.Apis.Management; -using Synapse.Apis.Management.Ipc; - -namespace Synapse -{ - - ///

- /// Defines extensions for s - /// - public static class IServiceCollectionExtensions - { - - /// - /// Adds and configures an Intra-Process Communication (IPC) client for the Synapse Management API - /// - /// The to configure - /// The configured - public static IServiceCollection AddSynapseRestApiClient(this IServiceCollection services) - { - services.TryAddSingleton(); - return services; - } - - } - -} diff --git a/src/apis/management/Synapse.Apis.Management.Ipc.Client/Services/SynapseIpcManagementApiClient.cs b/src/apis/management/Synapse.Apis.Management.Ipc.Client/Services/SynapseIpcManagementApiClient.cs deleted file mode 100644 index 3244f1177..000000000 --- a/src/apis/management/Synapse.Apis.Management.Ipc.Client/Services/SynapseIpcManagementApiClient.cs +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.OData.Query; -using Microsoft.Extensions.Logging; -using Neuroglia.Mapping; -using Neuroglia.Mediation; -using Synapse.Application.Services; -using Synapse.Integration.Commands.AuthenticationDefinitionCollections; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Commands.EventDefinitionCollections; -using Synapse.Integration.Commands.FunctionDefinitionCollections; -using Synapse.Integration.Commands.Schedules; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Commands.Workflows; -using Synapse.Integration.Models; - -namespace Synapse.Apis.Management.Ipc; - -///

-/// Represents the Intra-Process Communication (IPC) implementation of the -/// -public class SynapseIpcManagementApiClient - : ISynapseManagementApi -{ - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The service used to parse - protected SynapseIpcManagementApiClient(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IODataQueryOptionsParser queryOptionsParser) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Mediator = mediator; - this.Mapper = mapper; - this.QueryOptionsParser = queryOptionsParser; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - /// - /// Gets the service used to parse - /// - protected IODataQueryOptionsParser QueryOptionsParser { get; } - - #region Application - - /// - public virtual async Task GetApplicationInfoAsync(CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Application.V1GetApplicationInfoQuery(), cancellationToken); - } - - #endregion - - #region Workflows - - /// - public virtual async Task CreateWorkflowAsync(V1CreateWorkflowCommand command, CancellationToken cancellationToken = default) - { - if(command == null) throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task UploadWorkflowAsync(V1UploadWorkflowCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task> GetWorkflowsAsync(string? query, CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FilterQuery(this.QueryOptionsParser.Parse(query)), cancellationToken); - } - - /// - public virtual async Task> GetWorkflowsAsync(CancellationToken cancellationToken = default) - { - return await this.GetWorkflowsAsync(null!, cancellationToken); - } - - /// - public virtual async Task GetWorkflowByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken); - } - - /// - public virtual async Task ArchiveWorkflowAsync(string id, string? version = null, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Workflows.V1ArchiveWorkflowCommand(id, version), cancellationToken); - } - - /// - public virtual async Task DeleteWorkflowAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken); - } - - #endregion region - - #region WorkflowInstances - - /// - public virtual async Task CreateWorkflowInstanceAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task StartWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1StartWorkflowInstanceCommand(id), cancellationToken); - } - - /// - public virtual async Task GetWorkflowInstanceByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken); - } - - /// - public virtual async Task> GetWorkflowInstancesAsync(string? query, CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FilterQuery(this.QueryOptionsParser.Parse(query)), cancellationToken); - } - - /// - public virtual async Task> GetWorkflowInstancesAsync(CancellationToken cancellationToken = default) - { - return await this.GetWorkflowInstancesAsync(null, cancellationToken); - } - - /// - public virtual async Task SuspendWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1SuspendWorkflowInstanceCommand(id), cancellationToken); - } - - /// - public virtual async Task ResumeWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1ResumeWorkflowInstanceCommand(id), cancellationToken); - } - - /// - public virtual async Task CancelWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1CancelWorkflowInstanceCommand(id), cancellationToken); - } - - /// - public virtual async Task GetWorkflowInstanceLogsAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.WorkflowInstances.V1GetWorkflowInstanceLogsQuery(id), cancellationToken); - } - - - /// - public virtual async Task ArchiveWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1ArchiveWorkflowInstanceCommand(id), cancellationToken); - } - - /// - public virtual async Task DeleteWorkflowInstanceAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1DeleteWorkflowInstanceCommand(id), cancellationToken); - } - - #endregion - - #region Correlations - - /// - public virtual async Task CreateCorrelationAsync(V1CreateCorrelationCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task> GetCorrelationsAsync(string? query, CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FilterQuery(this.QueryOptionsParser.Parse(query)), cancellationToken); - } - - /// - public virtual async Task> GetCorrelationsAsync(CancellationToken cancellationToken = default) - { - return await this.GetCorrelationsAsync(null!, cancellationToken); - } - - /// - public virtual async Task GetCorrelationByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken); - } - - /// - public virtual async Task DeleteCorrelationContextAsync(string correlationId, string contextId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(correlationId)) throw new ArgumentNullException(nameof(correlationId)); - if (string.IsNullOrWhiteSpace(contextId)) throw new ArgumentNullException(nameof(contextId)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Correlations.V1DeleteCorrelationContextCommand(correlationId, contextId), cancellationToken); - } - - /// - public virtual async Task DeleteCorrelationContextEventAsync(string correlationId, string contextId, string eventId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(correlationId)) throw new ArgumentNullException(nameof(correlationId)); - if (string.IsNullOrWhiteSpace(contextId)) throw new ArgumentNullException(nameof(contextId)); - if (string.IsNullOrWhiteSpace(eventId)) throw new ArgumentNullException(nameof(eventId)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Correlations.V1DeleteCorrelatedEventCommand(correlationId, contextId, eventId), cancellationToken); - } - - /// - public virtual async Task DeleteCorrelationAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken); - } - - #endregion region - - #region AuthenticationDefinitionCollections - - /// - public virtual async Task CreateAuthenticationDefinitionCollectionAsync(V1CreateAuthenticationDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task GetAuthenticationDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken); - } - - /// - public virtual async Task> GetAuthenticationDefinitionCollectionsAsync(string? query, CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FilterQuery(this.QueryOptionsParser.Parse(query)), cancellationToken); - } - - /// - public virtual async Task DeleteAuthenticationDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken); - } - - #endregion - - #region EventDefinitionCollections - - /// - public virtual async Task CreateEventDefinitionCollectionAsync(V1CreateEventDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task GetEventDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken); - } - - /// - public virtual async Task> GetEventDefinitionCollectionsAsync(string? query, CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FilterQuery(this.QueryOptionsParser.Parse(query)), cancellationToken); - } - - /// - public virtual async Task DeleteEventDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken); - } - - #endregion - - #region FunctionDefinitionCollections - - /// - public virtual async Task CreateFunctionDefinitionCollectionAsync(V1CreateFunctionDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task GetFunctionDefinitionCollectionByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken); - } - - /// - public virtual async Task> GetFunctionDefinitionCollectionsAsync(string? query, CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FilterQuery(this.QueryOptionsParser.Parse(query)), cancellationToken); - } - - /// - public virtual async Task DeleteFunctionDefinitionCollectionAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken); - } - - #endregion - - #region OperationalReports - - /// - public virtual async Task GetOperationalReportAsync(DateTime? date = null, CancellationToken cancellationToken = default) - { - if (!date.HasValue) - date = DateTime.Now; - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(V1OperationalReport.GetIdFor(date.Value)), cancellationToken); - } - - #endregion - - #region Schedules - - /// - public virtual async Task CreateScheduleAsync(V1CreateScheduleCommand command, CancellationToken cancellationToken = default) - { - if (command == null) throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task GetScheduleByIdAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FindByIdQuery(id), cancellationToken); - } - - /// - public virtual async Task> GetSchedulesAsync(CancellationToken cancellationToken = default) => await this.GetSchedulesAsync(null!, cancellationToken); - - /// - public virtual async Task> GetSchedulesAsync(string query, CancellationToken cancellationToken = default) - { - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.Generic.V1FilterQuery(this.QueryOptionsParser.Parse(query)), cancellationToken); - } - - /// - public virtual async Task TriggerScheduleAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Schedules.V1TriggerScheduleCommand(id), cancellationToken); - } - - /// - public virtual async Task SuspendScheduleAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Schedules.V1SuspendScheduleCommand(id), cancellationToken); - } - - /// - public virtual async Task ResumeScheduleAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Schedules.V1ResumeScheduleCommand(id), cancellationToken); - } - - /// - public virtual async Task RetireScheduleAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Schedules.V1RetireScheduleCommand(id), cancellationToken); - } - - /// - public virtual async Task MakeScheduleObsoleteAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Schedules.V1MakeScheduleObsoleteCommand(id), cancellationToken); - } - - /// - public virtual async Task DeleteScheduleAsync(string id, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id)); - await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.Generic.V1DeleteCommand(id), cancellationToken); - } - - #endregion - -} - diff --git a/src/apis/management/Synapse.Apis.Management.Ipc.Client/Synapse.Apis.Management.Ipc.Client.csproj b/src/apis/management/Synapse.Apis.Management.Ipc.Client/Synapse.Apis.Management.Ipc.Client.csproj deleted file mode 100644 index cba59dd85..000000000 --- a/src/apis/management/Synapse.Apis.Management.Ipc.Client/Synapse.Apis.Management.Ipc.Client.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Client", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse management api ipc intra process client - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains the IPC client for the management API of Synapse WFMS - - - - \ - True - - - - - - - \ No newline at end of file diff --git a/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Services/ISynapseMonitoringApi.cs b/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Services/ISynapseMonitoringApi.cs deleted file mode 100644 index c3b512284..000000000 --- a/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Services/ISynapseMonitoringApi.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.ServiceModel; - -namespace Synapse.Apis.Monitoring -{ - ///

- /// Defines the fundamentals of a Synapse Monitoring API - /// - [ServiceContract] - public interface ISynapseMonitoringApi - { - - - - } - -} diff --git a/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Services/ISynapseMonitoringApiClient.cs b/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Services/ISynapseMonitoringApiClient.cs deleted file mode 100644 index 4208926f1..000000000 --- a/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Services/ISynapseMonitoringApiClient.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Models; -using System.ServiceModel; - -namespace Synapse.Apis.Monitoring -{ - - ///

- /// Defines the fundamentals of a Synapse Monitoring API client - /// - [ServiceContract] - public interface ISynapseMonitoringApiClient - { - - /// - /// Handles the specified - /// - /// The to handle - /// A new awaitable - [OperationContract] - Task PublishIntegrationEvent(V1Event e); - - } - -} diff --git a/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Synapse.Apis.Monitoring.Core.csproj b/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Synapse.Apis.Monitoring.Core.csproj deleted file mode 100644 index 4911bdf4c..000000000 --- a/src/apis/monitoring/Synapse.Apis.Monitoring.Core/Synapse.Apis.Monitoring.Core.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Core", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse monitoring api core - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains abstractions for the monitoring API of Synapse WFMS - - - - \ - True - - - - - - \ No newline at end of file diff --git a/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Extensions/IApplicationBuilderExtensions.cs b/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Extensions/IApplicationBuilderExtensions.cs deleted file mode 100644 index 1aecc60ae..000000000 --- a/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Extensions/IApplicationBuilderExtensions.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.DependencyInjection; -using Neuroglia.Mapping; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using Synapse.Application.Configuration; -using Synapse.Integration.Models; - -namespace Synapse.Apis.Monitoring.WebSocket -{ - - ///

- /// Defines extensions for s - /// - public static class ISynapseApplicationBuilderExtensions - { - - /// - /// Configures Synapse to use the Http REST API port - /// - /// The to configure - /// The configured - public static ISynapseApplicationBuilder UseWebSocketMonitoringApi(this ISynapseApplicationBuilder synapse) - { - synapse.Services.AddSignalR() - .AddNewtonsoftJsonProtocol(options => - { - options.PayloadSerializerSettings = new() - { - ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }, - NullValueHandling = NullValueHandling.Ignore - }; - }); - synapse.Services.AddIntegrationEventBus(async (provider, e) => - { - var mapper = provider.GetRequiredService(); - var hub = provider.GetRequiredService>(); - await hub.Clients.All.PublishIntegrationEvent(mapper.Map(e)); - }); - return synapse; - } - - } - -} diff --git a/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Services/SynapseWebSocketMonitoringApi.cs b/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Services/SynapseWebSocketMonitoringApi.cs deleted file mode 100644 index 490520ee6..000000000 --- a/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Services/SynapseWebSocketMonitoringApi.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore.SignalR; - -namespace Synapse.Apis.Monitoring.WebSocket -{ - - /// - /// Represents the WebSocket implementation of the interface - /// - public class SynapseWebSocketMonitoringApi - : Hub, ISynapseMonitoringApi - { - - - - } - -} diff --git a/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Synapse.Apis.Monitoring.WebSocket.csproj b/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Synapse.Apis.Monitoring.WebSocket.csproj deleted file mode 100644 index d342adf7e..000000000 --- a/src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Synapse.Apis.Monitoring.WebSocket.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse monitoring api websocket ws wss - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - - - - - - - - - \ No newline at end of file diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Core/Models/V1RuntimeSignal.cs b/src/apis/runtime/Synapse.Apis.Runtime.Core/Models/V1RuntimeSignal.cs deleted file mode 100644 index 35beea0d9..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Core/Models/V1RuntimeSignal.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Runtime -{ - - ///

- /// Represents a runtime signal - /// - [DataContract] - public class V1RuntimeSignal - { - - /// - /// Initializes a new - /// - protected V1RuntimeSignal() - { - - } - - /// - /// Initializes a new - /// - /// The type - /// The 's data - public V1RuntimeSignal(V1RuntimeSignalType type, Dynamic? data = null) - { - this.Type = type; - this.Data = data; - } - - /// - /// Gets the type - /// - [DataMember(Order = 1)] - public V1RuntimeSignalType Type { get; protected set; } - - /// - /// Gets the 's data - /// - [DataMember(Order = 2)] - public virtual Dynamic? Data { get; protected set; } - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Core/Properties/GlobalUsings.cs b/src/apis/runtime/Synapse.Apis.Runtime.Core/Properties/GlobalUsings.cs deleted file mode 100644 index aa451a551..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Core/Properties/GlobalUsings.cs +++ /dev/null @@ -1,4 +0,0 @@ -global using Neuroglia.Serialization; -global using ServerlessWorkflow.Sdk.Models; -global using System.Runtime.Serialization; -global using System.ServiceModel; \ No newline at end of file diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Core/Services/ISynapseRuntimeApi.cs b/src/apis/runtime/Synapse.Apis.Runtime.Core/Services/ISynapseRuntimeApi.cs deleted file mode 100644 index df60a9c6e..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Core/Services/ISynapseRuntimeApi.cs +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Commands.Events; -using Synapse.Integration.Commands.WorkflowActivities; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Apis.Runtime -{ - - ///

- /// Defines the fundamentals of the Synapse Runtime API - /// - [ServiceContract] - public interface ISynapseRuntimeApi - { - - /// - /// Connects to the Synapse Runtime API - /// - /// The id of the runtime to connect - /// A new - [OperationContract] - IAsyncEnumerable Connect(string runtimeId); - - /// - /// Starts the specified workflow instance - /// - /// The id of the workflow instance to start - /// A - /// The started workflow instance - [OperationContract] - Task StartAsync(string workflowInstanceId, CancellationToken cancellationToken = default); - - /// - /// Attempts to consume a pending event - /// - /// The object that describes the command to execute - /// A - /// The consumed , if any - [OperationContract] - Task ConsumeOrBeginCorrelateEventAsync(V1ConsumeWorkflowInstancePendingEventCommand command, CancellationToken cancellationToken = default); - - /// - /// Attempts to correlate an event to a workflow instance - /// - /// The object that describes the command to execute - /// A - /// A boolean indicating whether or not the specified event could be correlated to the workflow instance - [OperationContract] - Task TryCorrelateAsync(V1TryCorrelateWorkflowInstanceCommand command, CancellationToken cancellationToken = default); - - /// - /// Sets a correlation mapping for the specified workflow instance - /// - /// The object that describes the command to execute - /// A - /// A new awaitable - [OperationContract] - Task SetCorrelationMappingAsync(V1SetWorkflowInstanceCorrelationMappingCommand command, CancellationToken cancellationToken = default); - - /// - /// Publishes the specified - /// - /// The object that describes the command to execute - /// A - /// A new awaitable - [OperationContract] - Task PublishEventAsync(V1PublishEventCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets all the activities (including non-operative ones) of the specified workflow instance - /// - /// The id of the workflow instance to get the activities of - /// A - /// A new containing all the activities (including non-operative ones) of the specified workflow instance - [OperationContract] - Task> GetActivitiesAsync(string workflowInstanceId, CancellationToken cancellationToken = default); - - /// - /// Gets the operative activities of the specified workflow instance - /// - /// The id of the workflow instance to get the activities of - /// A - /// The operative activities of the specified workflow instance - [OperationContract] - Task> GetOperativeActivitiesAsync(string workflowInstanceId, CancellationToken cancellationToken = default); - - /// - /// Gets the all the child activities (including non-operative ones) of the specified activity - /// - /// The id of the workflow instance to get the activities of - /// The id of the activity to get the child activities of - /// A - /// The child activities (including non-operative ones) of the specified activity - [OperationContract] - Task> GetActivitiesAsync(string workflowInstanceId, string activityId, CancellationToken cancellationToken = default); - - /// - /// Gets the operative child activities of the specified activity - /// - /// The id of the workflow instance to get the activities of - /// The id of the activity to get the child activities of - /// A - /// The operative child activities of the specified activity - [OperationContract] - Task> GetOperativeActivitiesAsync(string workflowInstanceId, string activityId, CancellationToken cancellationToken = default); - - /// - /// Creates a new workflow activity - /// - /// The object that describes the command to execute - /// A - /// The newly created activity - [OperationContract] - Task CreateActivityAsync(V1CreateWorkflowActivityCommand command, CancellationToken cancellationToken = default); - - /// - /// Starts the specified activity - /// - /// The workflow activity to start - /// A - /// The started activity - [OperationContract] - Task StartActivityAsync(string activityId, CancellationToken cancellationToken = default); - - /// - /// Suspends the specified activity - /// - /// The workflow activity to suspend - /// A - /// The suspended activity - [OperationContract] - Task SuspendActivityAsync(string activityId, CancellationToken cancellationToken = default); - - /// - /// Skips the specified activity - /// - /// The workflow activity to skip - /// A - /// The skipped activity - [OperationContract] - Task SkipActivityAsync(string activityId, CancellationToken cancellationToken = default); - - /// - /// Sets the specified 's metadata - /// - /// The object that describes the command to execute - /// A - /// A new awaitable - [OperationContract] - Task SetActivityMetadataAsync(V1SetWorkflowActivityMetadataCommand command, CancellationToken cancellationToken = default); - - /// - /// Faults the specified activity - /// - /// The object that describes the command to execute - /// A - /// The faulted activity - [OperationContract] - Task FaultActivityAsync(V1FaultWorkflowActivityCommand command, CancellationToken cancellationToken = default); - - /// - /// Compensates the specified activity - /// - /// The object that describes the command to execute - /// A - /// The compensated activity - [OperationContract] - Task CompensateActivityAsync(V1CompensateActivityCommand command, CancellationToken cancellationToken = default); - - /// - /// Martks the specified activity as compensated - /// - /// The object that describes the command to execute - /// A - /// The compensated activity - [OperationContract] - Task MarkActivityAsCompensatedAsync(V1MarkActivityAsCompensatedCommand command, CancellationToken cancellationToken = default); - - /// - /// Cancels the specified activity - /// - /// The id of the activity to cancel - /// A - /// The faulted activity - [OperationContract] - Task CancelActivityAsync(string activityId, CancellationToken cancellationToken = default); - - /// - /// Sets the output of the specified workflow activity - /// - /// The object that describes the command to execute - /// A - /// The completed workflow activity - [OperationContract] - Task SetActivityOutputAsync(V1SetWorkflowActivityOutputCommand command, CancellationToken cancellationToken = default); - - /// - /// Gets the data of the the specified belongs to - /// - /// The id of the activity to get the parent state's data for - /// A - /// The data of the the specified belongs to - [OperationContract] - Task GetActivityStateDataAsync(string activityId, CancellationToken cancellationToken = default); - - /// - /// Starts a subflow - /// - /// The object that describes the command to execute - /// A - /// The id of the subflow's newly created workflow instance - Task StartSubflowAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default); - - /// - /// Marks the as suspended - /// - /// The id of the to mark as suspended - /// A - /// The updated - [OperationContract] - Task SuspendAsync(string workflowInstanceId, CancellationToken cancellationToken = default); - - /// - /// Marks the as cancelled - /// - /// The id of the to mark as cancelled - /// A - /// The updated - [OperationContract] - Task CancelAsync(string workflowInstanceId, CancellationToken cancellationToken = default); - - /// - /// Faults the specified workflow instance - /// - /// The object that describes the command to execute - /// A - /// The faulted activity - [OperationContract] - Task FaultAsync(V1FaultWorkflowInstanceCommand command, CancellationToken cancellationToken = default); - - /// - /// Sets the output of the specified workflow instance - /// - /// The object that describes the command to execute - /// A - /// The completed workflow instance - [OperationContract] - Task SetOutputAsync(V1SetWorkflowInstanceOutputCommand command, CancellationToken cancellationToken = default); - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Core/Synapse.Apis.Runtime.Core.csproj b/src/apis/runtime/Synapse.Apis.Runtime.Core/Synapse.Apis.Runtime.Core.csproj deleted file mode 100644 index 4682995f5..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Core/Synapse.Apis.Runtime.Core.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Core", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse runtime api core - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains abstractions for the runtime API of Synapse WFMS - - - - \ - True - - - - - - \ No newline at end of file diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Core/V1RuntimeSignalType.cs b/src/apis/runtime/Synapse.Apis.Runtime.Core/V1RuntimeSignalType.cs deleted file mode 100644 index 23cf22fe6..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Core/V1RuntimeSignalType.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Runtime -{ - - ///

- /// Enumerates all supported signal types - /// - public enum V1RuntimeSignalType - { - /// - /// Indicates that the server requested the runtime to correlate the workflow instance to inbound events - /// - Correlate, - /// - /// Indicates that the server requested the runtime to suspend its execution - /// - Suspend, - /// - /// Indicates that the server requested the runtime to cancel its execution - /// - Cancel, - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/ISynapseGrpcRuntimeApiClientOptionsBuilder.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/ISynapseGrpcRuntimeApiClientOptionsBuilder.cs deleted file mode 100644 index 644726dde..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/ISynapseGrpcRuntimeApiClientOptionsBuilder.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Runtime.Grpc.Configuration -{ - - ///

- /// Defines the fundamentals of a service used to build - /// - public interface ISynapseGrpcRuntimeApiClientOptionsBuilder - { - - /// - /// Configures the address of the GRPC-based API to connect to - /// - /// The address of the GRPC-based API to connect to - /// The configured - ISynapseGrpcRuntimeApiClientOptionsBuilder ForAddress(Uri address); - - /// - /// Configures the to use the specified - /// - /// The to use - /// The configured - ISynapseGrpcRuntimeApiClientOptionsBuilder WithChannelOptions(GrpcChannelOptions options); - - /// - /// Configures the to use the specified - /// - /// The used to configure the to use - /// The configured - ISynapseGrpcRuntimeApiClientOptionsBuilder WithChannelOptions(Action configurationAction); - - /// - /// Builds the to use - /// - /// New - SynapseGrpcRuntimeApiClientOptions Build(); - - } - - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/SynapseGrpcRuntimeApiClientOptions.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/SynapseGrpcRuntimeApiClientOptions.cs deleted file mode 100644 index abdfa5e38..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/SynapseGrpcRuntimeApiClientOptions.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Runtime.Grpc -{ - - ///

- /// Represents the options used to configure a client for a - /// - public class SynapseGrpcRuntimeApiClientOptions - { - - /// - /// Gets the default address of the GRPC-based - /// - public static Uri DefaultAddress - { - get - { - var scheme = EnvironmentVariables.Api.Grpc.Scheme.Value; - if (string.IsNullOrWhiteSpace(scheme)) - scheme = "http"; - var host = EnvironmentVariables.Api.HostName.Value; - if (string.IsNullOrWhiteSpace(host)) - host = "synapse"; - var port = EnvironmentVariables.Api.Grpc.Port.Value; - if (string.IsNullOrWhiteSpace(port)) - port = "41387"; - return new($"{scheme}://{host}:{port}"); - } - } - - /// - /// Gets/sets the address of the GRPC-based to connect to - /// - public virtual Uri Address { get; set; } = DefaultAddress; - - /// - /// gets/sets the options used to configure the 's options - /// - public virtual GrpcChannelOptions ChannelOptions { get; set; } = new(); - - } -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/SynapseGrpcRuntimeApiClientOptionsBuilder.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/SynapseGrpcRuntimeApiClientOptionsBuilder.cs deleted file mode 100644 index f8182ed4d..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Configuration/SynapseGrpcRuntimeApiClientOptionsBuilder.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Apis.Runtime.Grpc.Configuration; - -namespace Synapse.Apis.Runtime.Grpc -{ - - ///

- /// Represents the default implementation of the interface - /// - public class SynapseGrpcRuntimeApiClientOptionsBuilder - : ISynapseGrpcRuntimeApiClientOptionsBuilder - { - - /// - /// Gets the to configure - /// - protected SynapseGrpcRuntimeApiClientOptions Options { get; } = new(); - - /// - public virtual ISynapseGrpcRuntimeApiClientOptionsBuilder ForAddress(Uri address) - { - if (address == null) - throw new ArgumentNullException(nameof(address)); - this.Options.Address = address; - return this; - } - - /// - public virtual ISynapseGrpcRuntimeApiClientOptionsBuilder WithChannelOptions(GrpcChannelOptions options) - { - if (options == null) - throw new ArgumentNullException(nameof(options)); - this.Options.ChannelOptions = options; - return this; - } - - /// - public virtual ISynapseGrpcRuntimeApiClientOptionsBuilder WithChannelOptions(Action configurationAction) - { - if (configurationAction == null) - throw new ArgumentNullException(nameof(configurationAction)); - var options = new GrpcChannelOptions(); - configurationAction(options); - return this.WithChannelOptions(options); - } - - /// - public virtual SynapseGrpcRuntimeApiClientOptions Build() - { - return this.Options; - } - - } -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Extensions/IServiceCollectionExtensions.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 7c5f929f5..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Apis.Runtime.Grpc.Configuration; - -namespace Synapse.Apis.Runtime.Grpc -{ - - ///

- /// Defines extensions for s - /// - public static class IServiceCollectionExtensions - { - - /// - /// Adds and configures a GRPC-based client for the Synapse Runtime API - /// - /// The to configure - /// An used to configure the to use - /// The configured - public static IServiceCollection AddSynapseGrpcRuntimeApiClient(this IServiceCollection services, Action configurationAction) - { - if (configurationAction == null) - throw new ArgumentNullException(nameof(configurationAction)); - var builder = new SynapseGrpcRuntimeApiClientOptionsBuilder(); - configurationAction(builder); - var options = builder.Build(); - services.TryAddSingleton(Options.Create(options)); - services.TryAddSingleton(provider => - { - var channel = GrpcChannel.ForAddress(options.Address, options.ChannelOptions); - return channel.CreateGrpcService(); - }); - services.TryAddSingleton(); - return services; - } - - /// - /// Adds and configures a GRPC-based client for the Synapse Runtime API - /// - /// The to configure - /// The configured - public static IServiceCollection AddSynapseGrpcRuntimeApiClient(this IServiceCollection services) - { - return services.AddSynapseGrpcRuntimeApiClient(builder => { }); - } - - } -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Models/GrpcApiResult.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Models/GrpcApiResult.cs deleted file mode 100644 index 292b7de84..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Models/GrpcApiResult.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Apis.Runtime.Grpc.Models -{ - - ///

- /// Represents a Protobuf-compliant object used to describe the result of an operation on the Synapse Management API - /// - [ProtoContract] - public class GrpcApiResult - { - - /// - /// Initializes a new - /// - protected GrpcApiResult() - { - Code = null!; - } - - /// - /// Initializes a new - /// - /// The 's code - /// An array of errors that have occured during the operation's execution - public GrpcApiResult(string code, params Error[] errors) - { - Code = code; - Errors = errors == null ? new List() : errors.ToList(); - } - - /// - /// Initializes a new - /// - /// The 's code - public GrpcApiResult(string code) - : this(code, Array.Empty()) - { - - } - - /// - /// Gets the described result code - /// - [ProtoMember(1)] - public string Code { get; internal set; } - - /// - /// Gets an containing the errors that have occured during the application's execution - /// - [ProtoMember(2)] - public IEnumerable? Errors { get; internal set; } - - /// - /// Gets a boolean indicating whether or not the operation was successfull - /// - [ProtoIgnore] - [IgnoreDataMember] - public virtual bool Succeeded => this.Errors == null || !this.Errors.Any(); - - /// - /// Creates a new for the specified - /// - /// The to create a new for - /// A new - public static GrpcApiResult CreateFor(IOperationResult result) - { - return new GrpcApiResult() - { - Code = result.Code, - Errors = result.Errors?.Select(e => new Error() { Code = e.Code, Message = e.Message })! - }; - } - - /// - /// Creates a new for the specified - /// - /// The type of data wrapped by the - /// The to create a new for - /// A new - public static GrpcApiResult CreateFor(IOperationResult result) - { - return new GrpcApiResult() - { - Code = result.Code, - Errors = result.Errors?.Select(e => new Error() { Code = e.Code, Message = e.Message })! - }; - } - - /// - /// Creates a new for the specified - /// - /// The type of data wrapped by the - /// The to create a new for - /// A new - public static GrpcApiResult CreateFor(IOperationResult result) - { - return new GrpcApiResult() - { - Code = result.Code, - Errors = result.Errors?.Select(e => new Error() { Code = e.Code, Message = e.Message })!, - Data = result.Data - }; - } - - } - - /// - /// Represents a Protobuf-compliant object used to describe the result of an operation on the Synapse Management API - /// - /// The type of wrapped result - [ProtoContract] - public class GrpcApiResult - : GrpcApiResult - { - - /// - /// Initializes a new - /// - protected internal GrpcApiResult() - { - Code = null!; - } - - /// - /// Initializes a new - /// - /// The 's code - /// An array of errors that have occured during the operation's execution - public GrpcApiResult(string code, params Error[] errors) - : base(code, errors) - { - Code = code; - } - - /// - /// Initializes a new - /// - /// The 's code - /// The data returned by the operation - public GrpcApiResult(string code, T? data) - : base(code) - { - Code = code; - Data = data; - } - - /// - /// Initializes a new - /// - /// The 's code - public GrpcApiResult(string code) - : this(code, default(T)) - { - - } - - /// - [ProtoMember(1)] - public new string Code { get; internal set; } - - /// - [ProtoMember(2)] - public new IEnumerable? Errors { get; internal set; } - - /// - /// Gets the data returned by the operation - /// - [ProtoMember(3)] - public T? Data { get; internal set; } - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Properties/GlobalUsings.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Properties/GlobalUsings.cs deleted file mode 100644 index e2b49fc07..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Properties/GlobalUsings.cs +++ /dev/null @@ -1,14 +0,0 @@ -global using Grpc.Net.Client; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.DependencyInjection.Extensions; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Options; -global using Neuroglia; -global using Neuroglia.Serialization; -global using ProtoBuf; -global using ProtoBuf.Grpc; -global using ProtoBuf.Grpc.Client; -global using ServerlessWorkflow.Sdk.Models; -global using System.Runtime.Serialization; -global using System.ServiceModel; -global using Error = Synapse.Integration.Models.Error; diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Services/ISynapseGrpcRuntimeApi.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Services/ISynapseGrpcRuntimeApi.cs deleted file mode 100644 index 6afc61c99..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Services/ISynapseGrpcRuntimeApi.cs +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Apis.Runtime.Grpc.Models; -using Synapse.Integration.Commands.Events; -using Synapse.Integration.Commands.WorkflowActivities; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Models; -using Synapse.Integration.Queries.WorkflowActivities; - -namespace Synapse.Apis.Runtime.Grpc -{ - - ///

- /// Defines the fundamentals of the GRPC-based - /// - [ServiceContract] - public interface ISynapseGrpcRuntimeApi - { - - /// - /// Connects to the Synapse Runtime API - /// - /// The id of the workflow instance id the client runtime executes - /// The current - /// A new - [OperationContract] - IAsyncEnumerable Connect(string workflowInstanceId, CallContext context = default); - - /// - /// Starts the specified workflow instance - /// - /// The id of the workflow instance to start - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> StartAsync(string workflowInstanceId, CallContext context = default); - - /// - /// Attempts to consume a pending event - /// - /// The object that describes the command to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> ConsumeOrBeginCorrelateEventAsync(V1ConsumeWorkflowInstancePendingEventCommand command, CallContext context = default); - - /// - /// Attempts to correlate an event to a workflow instance - /// - /// The object that describes the command to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> TryCorrelateAsync(V1TryCorrelateWorkflowInstanceCommand command, CallContext context = default); - - /// - /// Sets a correlation mapping for the specified workflow instance - /// - /// The object that describes the command to execute - /// The current - /// A new object that describes the result of the operation - [OperationContract] - Task> SetCorrelationMappingAsync(V1SetWorkflowInstanceCorrelationMappingCommand command, CallContext context = default); - - /// - /// Publishes the specified - /// - /// The object that describes the command to execute - /// The current - /// A new awaitable - [OperationContract] - Task PublishEventAsync(V1PublishEventCommand command, CallContext context = default); - - /// - /// Gets the activities (including non-operative ones) of the specified workflow instance - /// - /// The id of the workflow instance to get the activities of - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task>> GetActivitiesAsync(V1GetWorkflowActivitiesQuery query, CallContext context = default); - - /// - /// Creates a new workflow activity - /// - /// An object that describes the command to execute - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> CreateActivityAsync(V1CreateWorkflowActivityCommand command, CallContext context = default); - - /// - /// Starts the specified workflow activity - /// - /// The id of the workflow activity to start - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> StartActivityAsync(string activityId, CallContext context = default); - - /// - /// Suspends the execution of the specified workflow activity - /// - /// The id of the workflow activity to suspend the execution of - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> SuspendActivityAsync(string activityId, CallContext context = default); - - /// - /// Skips the specified activity - /// - /// The workflow activity to skip - /// The current server call context - /// The skipped activity - [OperationContract] - Task> SkipActivityAsync(string activityId, CallContext context = default); - - /// - /// Sets the specified 's metadata - /// - /// The object that describes the command to execute - /// The current server call context - /// The updated - [OperationContract] - Task> SetActivityMetadataAsync(V1SetWorkflowActivityMetadataCommand command, CallContext context = default); - - /// - /// Faults the specified workflow activity - /// - /// An object that describes the command to execute - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> FaultActivityAsync(V1FaultWorkflowActivityCommand command, CallContext context = default); - - /// - /// Compensates the specified workflow activity - /// - /// An object that describes the command to execute - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> CompensateActivityAsync(V1CompensateActivityCommand command, CallContext context = default); - - /// - /// Marks the specified workflow activity as compensated - /// - /// An object that describes the command to execute - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> MarkActivityAsCompensatedAsync(V1MarkActivityAsCompensatedCommand command, CallContext context = default); - - /// - /// Cancels the specified workflow activity - /// - /// The id of the workflow activity to cancel - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> CancelActivityAsync(string activityId, CallContext context = default); - - /// - /// Completes and sets the output of the specified workflow activity - /// - /// The id of the workflow activity to complete and set the output of - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> SetActivityOutputAsync(V1SetWorkflowActivityOutputCommand command, CallContext context = default); - - /// - /// Gets the data of the the specified belongs to - /// - /// The id of the activity to get the parent state's data for - /// The current server call context - /// The data of the the specified belongs to - [OperationContract] - Task> GetActivityStateDataAsync(string activityId, CallContext context = default); - - /// - /// Starts a subflow - /// - /// The object that describes the command to execute - /// The current server call context - /// The newly created workflow instance - [OperationContract] - Task> StartSubflowAsync(V1CreateWorkflowInstanceCommand command, CallContext context = default); - - /// - /// Faults the specified workflow instance - /// - /// An object that describes the command to execute - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> FaultAsync(V1FaultWorkflowInstanceCommand command, CallContext context = default); - - /// - /// Suspends the execution of the specified workflow instance - /// - /// The id of the workflow instance to suspend the execution of - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> SuspendAsync(string workflowInstanceId, CallContext context = default); - - /// - /// Cancels the execution of the specified workflow instance - /// - /// The id of the workflow instance to cancel the execution of - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> CancelAsync(string workflowInstanceId, CallContext context = default); - - /// - /// Completes and sets the output of the specified workflow instance - /// - /// The id of the workflow instance to complete and set the output of - /// The current server call context - /// A new object that describes the result of the operation - [OperationContract] - Task> SetOutputAsync(V1SetWorkflowInstanceOutputCommand command, CallContext context = default); - - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Services/SynapseGrpcRuntimeApiClient.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Services/SynapseGrpcRuntimeApiClient.cs deleted file mode 100644 index f43aa8525..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Services/SynapseGrpcRuntimeApiClient.cs +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Commands.Events; -using Synapse.Integration.Commands.WorkflowActivities; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Apis.Runtime.Grpc -{ - - ///

- /// Represents the default GRPC-based implementation of the interface - /// - public class SynapseGrpcRuntimeApiClient - : ISynapseRuntimeApi - { - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to interact with the GRPC port of the Synapse Runtime API - public SynapseGrpcRuntimeApiClient(ILogger logger, ISynapseGrpcRuntimeApi runtimeApi) - { - this.Logger = logger; - this.RuntimeApi = runtimeApi; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to interact with the GRPC port of the Synapse Runtime API - /// - protected ISynapseGrpcRuntimeApi RuntimeApi { get; } - - /// - public virtual async IAsyncEnumerable Connect(string workflowInstanceId) - { - var messages = this.RuntimeApi.Connect(workflowInstanceId); - await using var messageEnumerator = messages.GetAsyncEnumerator(); - for (var canRead = true; canRead;) - { - try - { - canRead = await messageEnumerator.MoveNextAsync(); - } - catch (Exception ex) - when (ex is TaskCanceledException || ex is OperationCanceledException) - { - break; - } - yield return messageEnumerator.Current; - } - } - - /// - public virtual async Task StartAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.StartAsync(workflowInstanceId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task ConsumeOrBeginCorrelateEventAsync(V1ConsumeWorkflowInstancePendingEventCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.ConsumeOrBeginCorrelateEventAsync(command, cancellationToken); - if (!result.Succeeded) throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task PublishEventAsync(V1PublishEventCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.PublishEventAsync(command, cancellationToken); - if (!result.Succeeded) throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - } - - /// - public virtual async Task> GetActivitiesAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.GetActivitiesAsync(new(workflowInstanceId, true), cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data ?? (new()); - } - - /// - public virtual async Task> GetOperativeActivitiesAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.GetActivitiesAsync(new(workflowInstanceId), cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data ?? (new()); - } - - /// - public virtual async Task> GetActivitiesAsync(string workflowInstanceId, string activityId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.GetActivitiesAsync(new(workflowInstanceId, true, activityId), cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data ?? (new()); - } - - /// - public virtual async Task> GetOperativeActivitiesAsync(string workflowInstanceId, string activityId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.GetActivitiesAsync(new(workflowInstanceId, false, activityId), cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data ?? (new()); - } - - /// - public virtual async Task CreateActivityAsync(V1CreateWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.CreateActivityAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task StartActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.StartActivityAsync(activityId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task SuspendActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.SuspendActivityAsync(activityId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task SkipActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.SkipActivityAsync(activityId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task SetActivityMetadataAsync(V1SetWorkflowActivityMetadataCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.SetActivityMetadataAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task FaultActivityAsync(V1FaultWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.FaultActivityAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task CancelActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.CancelActivityAsync(activityId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task SetActivityOutputAsync(V1SetWorkflowActivityOutputCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.SetActivityOutputAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task GetActivityStateDataAsync(string activityId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.GetActivityStateDataAsync(activityId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task TryCorrelateAsync(V1TryCorrelateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.TryCorrelateAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task SetCorrelationMappingAsync(V1SetWorkflowInstanceCorrelationMappingCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.SetCorrelationMappingAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - } - - /// - public virtual async Task StartSubflowAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.StartSubflowAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task FaultAsync(V1FaultWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.FaultAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task CompensateActivityAsync(V1CompensateActivityCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.CompensateActivityAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task MarkActivityAsCompensatedAsync(V1MarkActivityAsCompensatedCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.MarkActivityAsCompensatedAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task SuspendAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.SuspendAsync(workflowInstanceId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task CancelAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.CancelAsync(workflowInstanceId, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - /// - public virtual async Task SetOutputAsync(V1SetWorkflowInstanceOutputCommand command, CancellationToken cancellationToken = default) - { - var result = await this.RuntimeApi.SetOutputAsync(command, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(new OperationResult(result.Code, result.Errors?.Select(e => new Neuroglia.Error(e.Code, e.Message))?.ToArray())); - return result.Data!; - } - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Synapse.Apis.Runtime.Grpc.Client.csproj b/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Synapse.Apis.Runtime.Grpc.Client.csproj deleted file mode 100644 index c8c9ec53a..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Synapse.Apis.Runtime.Grpc.Client.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Client", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse runtime api grpc client - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains the GRPC client for the management API of Synapse WFMS - - - - \ - True - - - - - - - - - - \ No newline at end of file diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc/Services/SynapseGrpcRuntimeApi.cs b/src/apis/runtime/Synapse.Apis.Runtime.Grpc/Services/SynapseGrpcRuntimeApi.cs deleted file mode 100644 index 45143d836..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc/Services/SynapseGrpcRuntimeApi.cs +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia; -using Neuroglia.Mapping; -using Neuroglia.Mediation; -using Neuroglia.Serialization; -using ProtoBuf.Grpc; -using Synapse.Apis.Runtime.Grpc.Models; -using Synapse.Infrastructure.Services; -using Synapse.Integration.Commands.Events; -using Synapse.Integration.Commands.WorkflowActivities; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Models; -using System.Threading.Channels; - -namespace Synapse.Apis.Runtime.Grpc -{ - - ///

- /// Represents the default implementation of the interface - /// - public class SynapseGrpcRuntimeApi - : ISynapseGrpcRuntimeApi - { - - /// - public SynapseGrpcRuntimeApi(IMediator mediator, IMapper mapper, IWorkflowRuntimeProxyFactory runtimeProxyFactory, IWorkflowRuntimeProxyManager runtimeProxyManager) - { - Mediator = mediator; - Mapper = mapper; - RuntimeProxyFactory = runtimeProxyFactory; - RuntimeProxyManager = runtimeProxyManager; - } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - /// - /// Gets the service used to create instances - /// - protected IWorkflowRuntimeProxyFactory RuntimeProxyFactory { get; } - - /// - /// Gets the service used to manage - /// - protected IWorkflowRuntimeProxyManager RuntimeProxyManager { get; } - - /// - public virtual async IAsyncEnumerable Connect(string runtimeId, CallContext context = default) - { - var stream = Channel.CreateUnbounded(); - var streamWriter = new AsyncStreamWriter(stream.Writer); - var runtime = RuntimeProxyManager.Register(this.RuntimeProxyFactory.CreateProxy(runtimeId, streamWriter)); - var messages = stream.Reader.ReadAllAsync(context.CancellationToken); - await using var messageEnumerator = messages.GetAsyncEnumerator(); - for (var canRead = true; canRead;) - { - try - { - canRead = await messageEnumerator.MoveNextAsync(); - } - catch (Exception ex) - when(ex is TaskCanceledException || ex is OperationCanceledException) - { - runtime.Dispose(); - break; - } - yield return messageEnumerator.Current; - } - } - - /// - public virtual async Task> StartAsync(string workflowInstanceId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsStartedCommand(workflowInstanceId), context.CancellationToken)); - } - - /// - public virtual async Task> ConsumeOrBeginCorrelateEventAsync(V1ConsumeWorkflowInstancePendingEventCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task PublishEventAsync(V1PublishEventCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> TryCorrelateAsync(V1TryCorrelateWorkflowInstanceCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> SetCorrelationMappingAsync(V1SetWorkflowInstanceCorrelationMappingCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> CreateActivityAsync(V1CreateWorkflowActivityCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task>> GetActivitiesAsync(Integration.Queries.WorkflowActivities.V1GetWorkflowActivitiesQuery query, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(query), context.CancellationToken)); - } - - /// - public virtual async Task> StartActivityAsync(string activityId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowActivities.V1StartWorkflowActivityCommand(activityId), context.CancellationToken)); - } - - /// - public virtual async Task> SuspendActivityAsync(string activityId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowActivities.V1SuspendWorkflowActivityCommand(activityId), context.CancellationToken)); - } - - /// - public virtual async Task> SkipActivityAsync(string activityId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowActivities.V1SkipWorkflowActivityCommand(activityId), context.CancellationToken)); - } - - /// - public virtual async Task> SetActivityMetadataAsync(V1SetWorkflowActivityMetadataCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> FaultActivityAsync(V1FaultWorkflowActivityCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> CompensateActivityAsync(V1CompensateActivityCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> MarkActivityAsCompensatedAsync(V1MarkActivityAsCompensatedCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> CancelActivityAsync(string activityId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowActivities.V1CancelWorkflowActivityCommand(activityId), context.CancellationToken)); - } - - /// - public virtual async Task> SetActivityOutputAsync(V1SetWorkflowActivityOutputCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> GetActivityStateDataAsync(string activityId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Queries.WorkflowActivities.V1GetActivityParentStateDataQuery(activityId), context.CancellationToken)); - } - - /// - public virtual async Task> StartSubflowAsync(V1CreateWorkflowInstanceCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(this.Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> SuspendAsync(string workflowInstanceId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsSuspendedCommand(workflowInstanceId), context.CancellationToken)); - } - - /// - public virtual async Task> FaultAsync(V1FaultWorkflowInstanceCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - /// - public virtual async Task> CancelAsync(string workflowInstanceId, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(new Application.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsCancelledCommand(workflowInstanceId), context.CancellationToken)); - } - - /// - public virtual async Task> SetOutputAsync(V1SetWorkflowInstanceOutputCommand command, CallContext context = default) - { - return GrpcApiResult.CreateFor(await this.Mediator.ExecuteAsync(Mapper.Map(command), context.CancellationToken)); - } - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Grpc/Synapse.Apis.Runtime.Grpc.csproj b/src/apis/runtime/Synapse.Apis.Runtime.Grpc/Synapse.Apis.Runtime.Grpc.csproj deleted file mode 100644 index c100d1b26..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Grpc/Synapse.Apis.Runtime.Grpc.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse runtime api grpc - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - - - - - - \ No newline at end of file diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Extensions/IServiceCollectionExtensions.cs b/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index f1f539cdc..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Synapse.Apis.Runtime.Ipc; - -namespace Synapse.Apis.Runtime.Grpc -{ - - ///

- /// Defines extensions for s - /// - public static class IServiceCollectionExtensions - { - - /// - /// Adds and configures a Intra-Process Communication (IPC) client for the Synapse Runtime API - /// - /// The to configure - /// The configured - public static IServiceCollection AddSynapseGrpcRuntimeApiClient(this IServiceCollection services) - { - services.TryAddSingleton(); - return services; - } - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Services/SynapseIpcRuntimeApiClient.cs b/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Services/SynapseIpcRuntimeApiClient.cs deleted file mode 100644 index 0af03a1c1..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Services/SynapseIpcRuntimeApiClient.cs +++ /dev/null @@ -1,286 +0,0 @@ -using Microsoft.Extensions.Logging; -using Neuroglia; -using Neuroglia.Mapping; -using Neuroglia.Mediation; -using Neuroglia.Serialization; -using Synapse.Infrastructure.Services; -using Synapse.Integration.Commands.Events; -using Synapse.Integration.Commands.WorkflowActivities; -using Synapse.Integration.Commands.WorkflowInstances; -using Synapse.Integration.Models; -using System.Threading.Channels; - -namespace Synapse.Apis.Runtime.Ipc -{ - - /// - /// Represents an Intra-Process Communication (IPC) client for the Synapse Runtime API - /// - public class SynapseIpcRuntimeApiClient - : ISynapseRuntimeApi - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The service used to create instances - /// The service used to manage - protected SynapseIpcRuntimeApiClient(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IWorkflowRuntimeProxyFactory runtimeProxyFactory, IWorkflowRuntimeProxyManager runtimeProxyManager) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Mediator = mediator; - this.Mapper = mapper; - this.RuntimeProxyFactory = runtimeProxyFactory; - this.RuntimeProxyManager = runtimeProxyManager; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - /// - /// Gets the service used to create instances - /// - protected IWorkflowRuntimeProxyFactory RuntimeProxyFactory { get; } - - /// - /// Gets the service used to manage - /// - protected IWorkflowRuntimeProxyManager RuntimeProxyManager { get; } - - /// - public virtual async IAsyncEnumerable Connect(string runtimeId) - { - if (string.IsNullOrWhiteSpace(runtimeId)) - throw new ArgumentNullException(nameof(runtimeId)); - var stream = Channel.CreateUnbounded(); - var streamWriter = new AsyncStreamWriter(stream.Writer); - var runtime = RuntimeProxyManager.Register(RuntimeProxyFactory.CreateProxy(runtimeId, streamWriter)); - await foreach (var message in stream.Reader.ReadAllAsync()) - { - yield return message; - } - runtime.Dispose(); - } - - /// - public virtual async Task StartAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - if(string.IsNullOrWhiteSpace(workflowInstanceId)) - throw new ArgumentNullException(nameof(workflowInstanceId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsStartedCommand(workflowInstanceId), cancellationToken); - } - - /// - public virtual async Task ConsumeOrBeginCorrelateEventAsync(V1ConsumeWorkflowInstancePendingEventCommand command, CancellationToken cancellationToken = default) - { - if(command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task PublishEventAsync(V1PublishEventCommand command, CancellationToken cancellationToken = default) - { - if (command == null) throw new ArgumentNullException(nameof(command)); - await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task> GetActivitiesAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(workflowInstanceId)) - throw new ArgumentNullException(nameof(workflowInstanceId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.WorkflowActivities.V1GetWorkflowActivitiesQuery(workflowInstanceId, true), cancellationToken); - } - - /// - public virtual async Task> GetOperativeActivitiesAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(workflowInstanceId)) - throw new ArgumentNullException(nameof(workflowInstanceId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.WorkflowActivities.V1GetWorkflowActivitiesQuery(workflowInstanceId), cancellationToken); - } - - /// - public virtual async Task> GetActivitiesAsync(string workflowInstanceId, string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(workflowInstanceId)) - throw new ArgumentNullException(nameof(workflowInstanceId)); - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.WorkflowActivities.V1GetWorkflowActivitiesQuery(workflowInstanceId, true, activityId), cancellationToken); - } - - /// - public virtual async Task> GetOperativeActivitiesAsync(string workflowInstanceId, string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(workflowInstanceId)) - throw new ArgumentNullException(nameof(workflowInstanceId)); - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.WorkflowActivities.V1GetWorkflowActivitiesQuery(workflowInstanceId, false, activityId), cancellationToken); - } - - /// - public virtual async Task CreateActivityAsync(V1CreateWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task StartActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowActivities.V1StartWorkflowActivityCommand(activityId), cancellationToken); - } - - /// - public virtual async Task SuspendActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowActivities.V1SuspendWorkflowActivityCommand(activityId), cancellationToken); - } - - /// - public virtual async Task SkipActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowActivities.V1SkipWorkflowActivityCommand(activityId), cancellationToken); - } - - /// - public virtual async Task SetActivityMetadataAsync(V1SetWorkflowActivityMetadataCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task FaultActivityAsync(V1FaultWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task CancelActivityAsync(string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowActivities.V1CancelWorkflowActivityCommand(activityId), cancellationToken); - } - - /// - public virtual async Task SetActivityOutputAsync(V1SetWorkflowActivityOutputCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task GetActivityStateDataAsync(string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Queries.WorkflowActivities.V1GetActivityParentStateDataQuery(activityId), cancellationToken); - } - - /// - public virtual async Task TryCorrelateAsync(V1TryCorrelateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task SetCorrelationMappingAsync(V1SetWorkflowInstanceCorrelationMappingCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task StartSubflowAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task FaultAsync(V1FaultWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task CompensateActivityAsync(V1CompensateActivityCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task MarkActivityAsCompensatedAsync(V1MarkActivityAsCompensatedCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - /// - public virtual async Task SuspendAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(workflowInstanceId)) - throw new ArgumentNullException(nameof(workflowInstanceId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsSuspendedCommand(workflowInstanceId), cancellationToken); - } - - /// - public virtual async Task CancelAsync(string workflowInstanceId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(workflowInstanceId)) - throw new ArgumentNullException(nameof(workflowInstanceId)); - return await this.Mediator.ExecuteAndUnwrapAsync(new Application.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsCancelledCommand(workflowInstanceId), cancellationToken); - } - - /// - public virtual async Task SetOutputAsync(V1SetWorkflowInstanceOutputCommand command, CancellationToken cancellationToken = default) - { - if (command == null) - throw new ArgumentNullException(nameof(command)); - return await this.Mediator.ExecuteAndUnwrapAsync(this.Mapper.Map(command), cancellationToken); - } - - } - -} diff --git a/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Synapse.Apis.Runtime.Ipc.Client.csproj b/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Synapse.Apis.Runtime.Ipc.Client.csproj deleted file mode 100644 index a1bc6fee6..000000000 --- a/src/apis/runtime/Synapse.Apis.Runtime.Ipc.Client/Synapse.Apis.Runtime.Ipc.Client.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - net6.0 - $(MSBuildProjectName.Replace(" ", "_").Replace(".Client", "")) - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse runtime api ipc intra process client - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - This package contains the IPC client for the runtime API of Synapse WFMS - - - - \ - True - - - - - - - \ No newline at end of file diff --git a/src/apps/Synapse.Cli/Commands/Command.cs b/src/apps/Synapse.Cli/Commands/Command.cs deleted file mode 100644 index c80462360..000000000 --- a/src/apps/Synapse.Cli/Commands/Command.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands -{ - - ///

- /// Represents the base class for all implementations - /// - public abstract class Command - : System.CommandLine.Command - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to interact with the remote Synapse Management API - /// The 's name - /// The 's description - protected Command(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi, string name, string description) - : base(name, description) - { - this.ServiceProvider = serviceProvider; - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.SynapseManagementApi = synapseManagementApi; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to interact with the remote Synapse Management API - /// - protected ISynapseManagementApi SynapseManagementApi { get; } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/CorrelationCommand.cs b/src/apps/Synapse.Cli/Commands/CorrelationCommand.cs deleted file mode 100644 index a29019af0..000000000 --- a/src/apps/Synapse.Cli/Commands/CorrelationCommand.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Synapse.Cli.Commands.Correlations; - -namespace Synapse.Cli.Commands -{ - - ///

- /// Represents the used to manage s - /// - public class CorrelationCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "correlations"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Manages correlations"; - - /// - public CorrelationCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("correl"); - this.AddAlias("crl"); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Correlations/GetCorrelationCommand.cs b/src/apps/Synapse.Cli/Commands/Correlations/GetCorrelationCommand.cs deleted file mode 100644 index ccf2d6782..000000000 --- a/src/apps/Synapse.Cli/Commands/Correlations/GetCorrelationCommand.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Synapse.Cli.Commands.Correlations -{ - - ///

- /// Represents the used to get a - /// - internal class GetCorrelationCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "get"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Gets the correlation with the specified id"; - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to interact with the remote Synapse Management API - public GetCorrelationCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddArgument(new Argument("id", "The id of the correlation to get")); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the correlation to get - /// A new awaitable - public async Task HandleAsync(string id) - { - var correlation = await this.SynapseManagementApi.GetCorrelationByIdAsync(id); - Console.WriteLine(JObject.FromObject(correlation).ToString(Formatting.Indented)); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Correlations/ListCorrelationsCommand.cs b/src/apps/Synapse.Cli/Commands/Correlations/ListCorrelationsCommand.cs deleted file mode 100644 index 282eb13ab..000000000 --- a/src/apps/Synapse.Cli/Commands/Correlations/ListCorrelationsCommand.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands.Correlations -{ - ///

- /// Represents the used to list s - /// - internal class ListCorrelationsCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "list"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Lists/filters correlations"; - - /// - public ListCorrelationsCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("ls"); - this.AddOption(CommandOptions.Filter); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The ODATA filter to use - /// A new awaitable - public async Task HandleAsync(string filter) - { - var query = string.Empty; - var filters = new List(); - if (!string.IsNullOrWhiteSpace(filter)) - filters.Add(filter); - if (filters.Any()) - query = $"$filter={string.Join(" AND ", filters)}"; - var correlations = await this.SynapseManagementApi.GetCorrelationsAsync(query); - var table = new Table(); - table.Expand = true; - table.Border(TableBorder.None); - table.AddColumn("ID"); - table.AddColumn("LIFETIME"); - table.AddColumn("OUTCOME TYPE"); - table.AddColumn("OUTCOME TARGET"); - table.AddColumn("CONDITION TYPE"); - table.AddColumn("CONDITIONS"); - table.AddColumn("CONTEXTS"); - table.AddColumn("CREATED AT"); - table.AddColumn("LAST MODIFIED"); - foreach (var correlation in correlations) - { - table.AddRow(correlation.Id, correlation.Lifetime.ToString(), correlation.Outcome.Type.ToString(), correlation.Outcome.Target, correlation.ConditionType.ToString(), correlation.Conditions.Count.ToString(), correlation.Contexts.Count.ToString(), correlation.CreatedAt.ToString(), correlation.LastModified.ToString()); - } - AnsiConsole.Write(table); - } - - private static class CommandOptions - { - - public static Option Filter - { - get - { - var option = new Option("--filter") - { - Description = "The ODATA filter to apply" - }; - option.AddAlias("-f"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/SystemCommand.cs b/src/apps/Synapse.Cli/Commands/SystemCommand.cs deleted file mode 100644 index 3f150ca55..000000000 --- a/src/apps/Synapse.Cli/Commands/SystemCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Synapse.Cli.Commands.Systems; - -namespace Synapse.Cli.Commands -{ - - ///

- /// Represents the used to manage Synapse - /// - public class SystemCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "system"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Manages Synapse"; - - /// - public SystemCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("sys"); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Systems/InstallCommand.cs b/src/apps/Synapse.Cli/Commands/Systems/InstallCommand.cs deleted file mode 100644 index 8c3563cf8..000000000 --- a/src/apps/Synapse.Cli/Commands/Systems/InstallCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Synapse.Cli.Commands.Systems.Installs; - -namespace Synapse.Cli.Commands.Systems -{ - - ///

- /// Represents the used to cancel the execution of a - /// - internal class InstallCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "install"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Installs Synapse"; - - /// - public InstallCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Systems/Installs/DockerInstallCommand.cs b/src/apps/Synapse.Cli/Commands/Systems/Installs/DockerInstallCommand.cs deleted file mode 100644 index fd6680f0d..000000000 --- a/src/apps/Synapse.Cli/Commands/Systems/Installs/DockerInstallCommand.cs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace Synapse.Cli.Commands.Systems.Installs -{ - - ///

- /// Represents the used to perform a Docker Synapse install - /// - internal class DockerInstallCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "docker"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Installs Synapse on Docker"; - - /// - public DockerInstallCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi, IHttpClientFactory httpClientFactory) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.HttpClient = httpClientFactory.CreateClient(); - this.AddOption(CommandOptions.Name); - this.AddOption(CommandOptions.HostName); - this.AddOption(CommandOptions.CloudEventSinkUri); - this.AddOption(CommandOptions.SkipCertificateValidation); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Gets the service used to perform HTTP requests - /// - protected HttpClient HttpClient { get; } - - /// - /// Handles the - /// - /// The Synapse container name" - /// Synapse's host name - /// The Synapse cloud event sink uri - /// A boolean indicating whether or not to skip certificate validation - /// A new awaitable - public async Task HandleAsync(string name, string hostName, Uri ceSink, bool skipCertificateValidation) - { - var environmentVariables = new List(); - environmentVariables.Add(@$"-e ""{EnvironmentVariables.Api.HostName.Name} = {hostName}"""); - environmentVariables.Add(@$"-e ""{EnvironmentVariables.CloudEvents.Sink.Uri.Name} = {ceSink}"""); - if(skipCertificateValidation) - environmentVariables.Add(@$"-e ""{EnvironmentVariables.SkipCertificateValidation.Name} = {skipCertificateValidation}"""); - var args = @$"run --name {name} -v /var/run/docker.sock:/var/run/docker.sock --add-host=host.docker.internal:host-gateway -p 42286:42286 -p 41387:41387 -d --restart unless-stopped {string.Join(" ", environmentVariables)} ghcr.io/serverlessworkflow/synapse:latest"; - var process = Process.Start("docker", args); - await process.WaitForExitAsync(); - await Task.Delay(500); //wait for the server to run - var uri = "http://localhost:42286"; - try - { - Process.Start(uri); - } - catch - { - try - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - Process.Start(new ProcessStartInfo("cmd", $"/c start {uri.Replace("&", "^&")}") { CreateNoWindow = true }); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - Process.Start("xdg-open", uri); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - Process.Start("open", uri); - else - throw new NotSupportedException(); - } - catch (NotSupportedException) - { - throw; - } - } - } - - private static class CommandOptions - { - - public static Option Name - { - get - { - var option = new Option("--name") - { - Description = "The Synapse container name" - }; - option.SetDefaultValue("synapse"); - option.AddAlias("-n"); - return option; - } - } - - public static Option HostName - { - get - { - var option = new Option("--hostname") - { - Description = "Synapse's host name" - }; - option.SetDefaultValue("synapse"); - option.AddAlias("-hn"); - return option; - } - } - - public static Option CloudEventSinkUri - { - get - { - var option = new Option("--ce-sink") - { - Description = "The Synapse cloud event sink uri" - }; - option.SetDefaultValue(new Uri("https://en37uhd2he6t4.x.pipedream.net")); - option.AddAlias("-ces"); - return option; - } - } - - public static Option SkipCertificateValidation - { - get - { - var option = new Option("--skip-certificate-validation") - { - Description = "Skips certificate validation when performing http requests" - }; - option.AddAlias("-scv"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Systems/Installs/NativeInstallCommand.cs b/src/apps/Synapse.Cli/Commands/Systems/Installs/NativeInstallCommand.cs deleted file mode 100644 index e20bdb774..000000000 --- a/src/apps/Synapse.Cli/Commands/Systems/Installs/NativeInstallCommand.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Diagnostics; -using System.IO.Compression; -using System.Runtime.InteropServices; - -namespace Synapse.Cli.Commands.Systems.Installs -{ - - ///

- /// Represents the used to perform a native Synapse install - /// - internal class NativeInstallCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "native"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Installs Synapse on the current OS"; - - /// - public NativeInstallCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi, IHttpClientFactory httpClientFactory) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.HttpClient = httpClientFactory.CreateClient(); - this.AddOption(CommandOptions.Directory); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Gets the service used to perform HTTP requests - /// - protected HttpClient HttpClient { get; } - - /// - /// Handles the - /// - /// The directory to install Synapse to - /// A new awaitable - public async Task HandleAsync(string directory) - { - var process = Process.Start(new ProcessStartInfo("cmd.exe", @"/c echo ;%PATH%; | find /C /I ""synapse""") { RedirectStandardOutput = true }); - var output = await process!.StandardOutput.ReadToEndAsync(); - process.Dispose(); - if (int.Parse(output.Trim()) > 0) - return; //already installed - if (string.IsNullOrWhiteSpace(directory)) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "CNCF", "Synapse"); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - directory = Path.Combine("usr", "local", "cncf", "synapse"); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - directory = Path.Combine("Applications", "CNCF", "Synapse"); - else - throw new PlatformNotSupportedException(); - } - var directoryInfo = new DirectoryInfo(directory); - if (!directoryInfo.Exists) - directoryInfo.Create(); - await this.InstallJQIfNotExistsAsync(); - var target = null as string; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - target = "win-x64.zip"; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - target = "linux-x64.tar.gz"; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - target = "osx-64.tar.gz"; - else - throw new PlatformNotSupportedException(); - using var packageStream = new MemoryStream(); - await AnsiConsole.Progress() - .Columns(new ProgressColumn[] - { - new TaskDescriptionColumn(), - new ProgressBarColumn(), - new PercentageColumn(), - new RemainingTimeColumn(), - new SpinnerColumn(), - }) - .HideCompleted(true) - .StartAsync(async context => - { - await Task.Run(async () => - { - var task = context.AddTask($"Downloading [u]synapse-{target}[/]", new ProgressTaskSettings - { - AutoStart = false - }); - await this.HttpClient.DownloadAsync($"https://github.com/serverlessworkflow/synapse/releases/download/v{typeof(NativeInstallCommand).Assembly.GetName()!.Version!.ToString(3)}/synapse-{target}", packageStream, task); - }); - }); - AnsiConsole.Status() - .Start("Extracting [u]synapse-{target}[/]...", ctx => - { - using var archive = new ZipArchive(packageStream, ZipArchiveMode.Read); - archive.ExtractToDirectory(directoryInfo.FullName, true); - }); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var pathValue = Environment.GetEnvironmentVariable("PATH")!; - if (!pathValue.Trim().EndsWith(';')) - pathValue += ";"; - pathValue += directoryInfo.FullName; - Environment.SetEnvironmentVariable("PATH", pathValue, EnvironmentVariableTarget.User); - } - } - - /// - /// Installs JQ - /// - /// A new awaitable - protected virtual async Task InstallJQIfNotExistsAsync() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - var directory = new DirectoryInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "jq")); - if (directory.Exists) - return; //already installed - directory.Create(); - var process = Process.Start(new ProcessStartInfo("cmd.exe", @"/c echo ;%PATH%; | find /C /I ""jq""") { RedirectStandardOutput = true }); - var output = await process!.StandardOutput.ReadToEndAsync(); - process.Dispose(); - if (int.Parse(output.Trim()) > 0) - return; //already installed - using var stream = new MemoryStream(); - await AnsiConsole.Progress() - .Columns(new ProgressColumn[] - { - new TaskDescriptionColumn(), - new ProgressBarColumn(), - new PercentageColumn(), - new RemainingTimeColumn(), - new SpinnerColumn() - }) - .HideCompleted(true) - .StartAsync(async context => - { - await Task.Run(async () => - { - var task = context.AddTask($"Downloading [u]jq[/]", new ProgressTaskSettings - { - AutoStart = false - }); - await this.HttpClient.DownloadAsync($"https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe", stream, task); - }); - }); - var file = new FileInfo(Path.Combine(directory.FullName, "jq.exe")); - using var fileStream = file.Create(); - stream.Position = 0; - await stream.CopyToAsync(fileStream); - await fileStream.FlushAsync(); - var pathValue = Environment.GetEnvironmentVariable("PATH")!; - if (!pathValue.Trim().EndsWith(';')) - pathValue += ";"; - pathValue += directory.FullName; - Environment.SetEnvironmentVariable("PATH", pathValue, EnvironmentVariableTarget.User); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - var process = Process.Start("bash", @"-c ""$ command -v foo >/dev/null 2>&1 || { echo 'I require foo but it's not installed. Aborting.' >&2; exit 1; }"""); - await process.WaitForExitAsync(); - if (process.ExitCode == 0) - return; - process.Dispose(); - process = Process.Start("bash", @"-c ""apt-get update"""); - await process.WaitForExitAsync(); - process.Dispose(); - process = Process.Start("bash", @"-c ""apt-get install jq -y"""); - await process.WaitForExitAsync(); - process.Dispose(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - var process = Process.Start("brew install jq"); - await process.WaitForExitAsync(); - process.Dispose(); - } - else - throw new PlatformNotSupportedException(); - } - - private static class CommandOptions - { - - public static Option Directory - { - get - { - var option = new Option("--directory") - { - Description = "The directory to install Synapse to" - }; - option.AddAlias("-d"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowCommand.cs deleted file mode 100644 index bd6469306..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowCommand.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Synapse.Cli.Commands.Workflows; - -namespace Synapse.Cli.Commands -{ - ///

- /// Represents the used to manage s - /// - public class WorkflowCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "workflow"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Manages workflows"; - - /// - public WorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("workflows"); - this.AddAlias("wf"); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowInstanceCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowInstanceCommand.cs deleted file mode 100644 index 42e692df3..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowInstanceCommand.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Synapse.Cli.Commands.WorkflowInstances; - -namespace Synapse.Cli.Commands -{ - - ///

- /// Represents the used to manage s - /// - public class WorkflowInstanceCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "workflow-instance"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Manages workflow instances"; - - /// - public WorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("workflow-instances"); - this.AddAlias("instance"); - this.AddAlias("instances"); - this.AddAlias("wfi"); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowInstances/CancelWorkflowInstanceCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowInstances/CancelWorkflowInstanceCommand.cs deleted file mode 100644 index 97070eeb9..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowInstances/CancelWorkflowInstanceCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands.WorkflowInstances -{ - ///

- /// Represents the used to cancel the execution of a - /// - internal class CancelWorkflowInstanceCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "cancel"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Cancels the execution of the specified workflow instance"; - - /// - public CancelWorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.Add(new Argument("id") { Description = "The id of the workflow instance to cancel the execution of (ex: myworkflow-XqGg49onskelivig7ND6ig)" }); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflow instance to cancel the execution of - /// A new awaitable - public async Task HandleAsync(string id) - { - await this.SynapseManagementApi.CancelWorkflowInstanceAsync(id); - Console.WriteLine($"The execution of the workflow instance with id '{id}' has been successfully cancelled"); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowInstances/DeleteWorkflowInstanceCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowInstances/DeleteWorkflowInstanceCommand.cs deleted file mode 100644 index a56fd0d1a..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowInstances/DeleteWorkflowInstanceCommand.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands.WorkflowInstances -{ - ///

- /// Represents the used to delete a single - /// - internal class DeleteWorkflowInstanceCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "delete"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Deletes the specified workflow instance"; - - /// - public DeleteWorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("del"); - this.Add(new Argument("id") { Description = "The id of the workflow instance to delete (ex: myworkflow-XqGg49onskelivig7ND6ig). Note that failing to specify the version will delete all version of the specified workflow" }); - this.Add(CommandOptions.Confirm); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflow instance to delete - /// A boolean indicating whether or not to ask for the user's confirmation - /// A new awaitable - public async Task HandleAsync(string id, bool y) - { - if (!y) - { - Console.Write($"Are you sure you wish to delete the workflow instance with id '{id}'? Press 'y' to confirm, or any other key to cancel: "); - var inputKey = Console.ReadKey(); - Console.WriteLine(); - if (inputKey.Key != ConsoleKey.Y) - { - Console.WriteLine("Deletion cancelled"); - return; - } - } - await this.SynapseManagementApi.DeleteWorkflowInstanceAsync(id); - Console.WriteLine($"The workflow instance with id '{id}' has been successfully deleted"); - } - - private static class CommandOptions - { - - public static Option Confirm - { - get - { - var option = new Option("-y", () => false) - { - Description = "Delete the workflow instance without prompting confirmation" - }; - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceCommand.cs deleted file mode 100644 index 41cd50f00..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceCommand.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Synapse.Cli.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to get a - /// - internal class GetWorkflowInstanceCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "get"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Gets the workflow instance with the specified id"; - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to interact with the remote Synapse Management API - public GetWorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddArgument(new Argument("id", "The id of the workflow instance to get")); - this.AddOption(CommandOptions.Output); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflow instance to get - /// A boolean indicating whether or not to only show the output of the specified wporkflow instance - /// A new awaitable - public async Task HandleAsync(string id, bool output) - { - var workflowInstance = await this.SynapseManagementApi.GetWorkflowInstanceByIdAsync(id); - var jobj = JObject.FromObject(workflowInstance); - var outputJson = jobj.ToString(Formatting.Indented); - if (output) - { - switch (workflowInstance.Status) - { - case V1WorkflowInstanceStatus.Completed: - outputJson = jobj.Property(nameof(V1WorkflowInstance.Output), StringComparison.OrdinalIgnoreCase)!.ToString(Formatting.Indented); - break; - case V1WorkflowInstanceStatus.Faulted: - outputJson = jobj.Property(nameof(V1WorkflowInstance.Error), StringComparison.OrdinalIgnoreCase)!.ToString(Formatting.Indented); - break; - default: - - break; - } - } - Console.WriteLine(outputJson); - } - - private static class CommandOptions - { - - public static Option Output - { - get - { - var option = new Option("--output") - { - Description = "Shows only the output of the specified workflow instance, or the error it encountered in case it did not successfully complete" - }; - option.AddAlias("-o"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowInstances/ListWorkflowInstancesCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowInstances/ListWorkflowInstancesCommand.cs deleted file mode 100644 index e066862eb..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowInstances/ListWorkflowInstancesCommand.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to list s - /// - internal class ListWorkflowInstancesCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "list"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Lists/filters workflow instances"; - - /// - public ListWorkflowInstancesCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("ls"); - this.AddOption(CommandOptions.Filter); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The ODATA filter to apply - /// A new awaitable - public async Task HandleAsync(string filter) - { - var query = string.Empty; - if (!string.IsNullOrWhiteSpace(filter)) - query = $"$filter={filter}"; - var workflowInstances = await this.SynapseManagementApi.GetWorkflowInstancesAsync(query); - var table = new Table(); - table.Expand = true; - table.Border(TableBorder.None); - table.AddColumn("ID"); - table.AddColumn("WORKFLOW"); - table.AddColumn("KEY"); - table.AddColumn("STATUS"); - table.AddColumn("ACTIVATION TYPE"); - table.AddColumn("CREATED AT"); - table.AddColumn("STARTED AT"); - table.AddColumn("EXECUTED AT"); - foreach (var workflowInstance in workflowInstances) - { - var status = workflowInstance.Status switch - { - V1WorkflowInstanceStatus.Starting | V1WorkflowInstanceStatus.Resuming => $"[navyblue]{workflowInstance.Status}[/]", - V1WorkflowInstanceStatus.Running => $"[dodgerblue2]{workflowInstance.Status}[/]", - V1WorkflowInstanceStatus.Suspending => $"[indianred1]{workflowInstance.Status}[/]", - V1WorkflowInstanceStatus.Suspended => $"[orangered1]{workflowInstance.Status}[/]", - V1WorkflowInstanceStatus.Cancelling => $"[purple_2]{workflowInstance.Status}[/]", - V1WorkflowInstanceStatus.Cancelled => $"[mediumvioletred]{workflowInstance.Status}[/]", - V1WorkflowInstanceStatus.Faulted => $"[red3_1]{workflowInstance.Status}[/]", - V1WorkflowInstanceStatus.Completed => $"[chartreuse2_1]{workflowInstance.Status}[/]", - _ => workflowInstance.Status.ToString() - }; - table.AddRow - ( - workflowInstance.Id, - workflowInstance.WorkflowId, - workflowInstance.Key, - status, - workflowInstance.ActivationType.ToString(), - workflowInstance.CreatedAt.ToString(), - (workflowInstance.StartedAt.HasValue ? workflowInstance.StartedAt.ToString() : "-")!, - (workflowInstance.ExecutedAt.HasValue ? workflowInstance.ExecutedAt.ToString() : "-")! - ); - } - AnsiConsole.Write(table); - } - - private static class CommandOptions - { - - public static Option Filter - { - get - { - var option = new Option("--filter") - { - Description = "The ODATA filter to apply" - }; - option.AddAlias("-f"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowInstances/ResumeWorkflowInstanceCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowInstances/ResumeWorkflowInstanceCommand.cs deleted file mode 100644 index a9849d9a1..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowInstances/ResumeWorkflowInstanceCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands.WorkflowInstances -{ - ///

- /// Represents the used to resume the execution of a - /// - internal class ResumeWorkflowInstanceCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "resume"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Resumes the execution of the specified workflow instance"; - - /// - public ResumeWorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.Add(new Argument("id") { Description = "The id of the workflow instance to resume the execution of (ex: myworkflow-XqGg49onskelivig7ND6ig)" }); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflow instance to cancel the execution of - /// A new awaitable - public async Task HandleAsync(string id) - { - await this.SynapseManagementApi.ResumeWorkflowInstanceAsync(id); - Console.WriteLine($"The execution of the workflow instance with id '{id}' has been successfully resumed"); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/WorkflowInstances/SuspendWorkflowInstanceCommand.cs b/src/apps/Synapse.Cli/Commands/WorkflowInstances/SuspendWorkflowInstanceCommand.cs deleted file mode 100644 index 1c9b6e10b..000000000 --- a/src/apps/Synapse.Cli/Commands/WorkflowInstances/SuspendWorkflowInstanceCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands.WorkflowInstances -{ - ///

- /// Represents the used to suspend the execution of a - /// - internal class SuspendWorkflowInstanceCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "suspend"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Suspends the execution of the specified workflow instance"; - - /// - public SuspendWorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.Add(new Argument("id") { Description = "The id of the workflow instance to suspend the execution of (ex: myworkflow-XqGg49onskelivig7ND6ig)" }); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflow instance to cancel the execution of - /// A new awaitable - public async Task HandleAsync(string id) - { - await this.SynapseManagementApi.SuspendWorkflowInstanceAsync(id); - Console.WriteLine($"The execution of the workflow instance with id '{id}' has been successfully suspended"); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Workflows/DeleteWorkflowCommand.cs b/src/apps/Synapse.Cli/Commands/Workflows/DeleteWorkflowCommand.cs deleted file mode 100644 index 827f82646..000000000 --- a/src/apps/Synapse.Cli/Commands/Workflows/DeleteWorkflowCommand.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli.Commands.Workflows -{ - - ///

- /// Represents the used to delete a single - /// - internal class DeleteWorkflowCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "delete"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Deletes the specified workflow"; - - /// - public DeleteWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("del"); - this.Add(new Argument("id") { Description = "The id of the workflow to delete (ex: myworkflow:1.0). Note that failing to specify the version will delete all version of the specified workflow" }); - this.Add(CommandOptions.Confirm); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflow to delete - /// A boolean indicating whether or not to ask for the user's confirmation - /// A new awaitable - public async Task HandleAsync(string id, bool y) - { - var components = id.Split(':', StringSplitOptions.RemoveEmptyEntries); - if (!y) - { - if (components.Length == 2) - Console.Write($"Are you sure you wish to delete the workflow with id '{id}'? Press 'y' to confirm, or any other key to cancel: "); - else - Console.Write($"Are you sure you wish to delete all version of the workflow with id '{id}'? Press 'y' to confirm, or any other key to cancel: "); - var inputKey = Console.ReadKey(); - Console.WriteLine(); - if (inputKey.Key != ConsoleKey.Y) - { - Console.WriteLine("Deletion cancelled"); - return; - } - } - await this.SynapseManagementApi.DeleteWorkflowAsync(id); - if (components.Length == 2) - Console.WriteLine($"The workflow with id '{id}' has been successfully deleted"); - else - Console.WriteLine($"All version of the workflow with id '{id}' have been successfully deleted"); - } - - private static class CommandOptions - { - - public static Option Confirm - { - get - { - var option = new Option("-y", () => false) - { - Description = "Delete the workflow(s) without prompting confirmation" - }; - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Workflows/DeployWorkflowCommand.cs b/src/apps/Synapse.Cli/Commands/Workflows/DeployWorkflowCommand.cs deleted file mode 100644 index d88e0a63b..000000000 --- a/src/apps/Synapse.Cli/Commands/Workflows/DeployWorkflowCommand.cs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using ServerlessWorkflow.Sdk.Services.IO; - -namespace Synapse.Cli.Commands.Workflows -{ - - ///

- /// Represents the used to create a new - /// - internal class DeployWorkflowCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "deploy"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Deploys a new workflow"; - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to interact with the remote Synapse Management API - /// The service used to read s - public DeployWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi, IWorkflowReader workflowReader) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.WorkflowReader = workflowReader; - this.Add(CommandOptions.File); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Gets the service used to read s - /// - protected IWorkflowReader WorkflowReader { get; } - - /// - /// Handles the - /// - /// The definition's file path - /// A new awaitable - public async Task HandleAsync(string file) - { - var workflowDefinition = null as WorkflowDefinition; - var stream = null as Stream; - if (!string.IsNullOrWhiteSpace(file)) - stream = File.OpenRead(file); - else - throw new InvalidOperationException("You must specifiy exactly one of the following options: --file"); - try - { - workflowDefinition = await this.WorkflowReader.ReadAsync(stream); - var workflow = await this.SynapseManagementApi.CreateWorkflowAsync(new() { Definition = workflowDefinition }); - Console.WriteLine($"The workflow with id '{workflow.Id}' has been successfully deployed"); - } - catch - { - throw; - } - finally - { - await stream.DisposeAsync(); - } - } - - private static class CommandOptions - { - - public static Option File - { - get - { - var option = new Option("--file") - { - Description = "The file that contains the workflow definition to deploy" - }; - option.AddAlias("-f"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Workflows/GetWorkflowCommand.cs b/src/apps/Synapse.Cli/Commands/Workflows/GetWorkflowCommand.cs deleted file mode 100644 index b6098e803..000000000 --- a/src/apps/Synapse.Cli/Commands/Workflows/GetWorkflowCommand.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Synapse.Cli.Commands.Workflows -{ - - ///

- /// Represents the used to get a - /// - internal class GetWorkflowCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "get"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Gets the workflow with the specified id"; - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to interact with the remote Synapse Management API - public GetWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddArgument(new Argument("id", "The id of the workflow to get")); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflow to get - /// A new awaitable - public async Task HandleAsync(string id) - { - var workflow = await this.SynapseManagementApi.GetWorkflowByIdAsync(id); - Console.WriteLine(JObject.FromObject(workflow).ToString(Formatting.Indented)); - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Workflows/ListWorkflowsCommand.cs b/src/apps/Synapse.Cli/Commands/Workflows/ListWorkflowsCommand.cs deleted file mode 100644 index aa0e43473..000000000 --- a/src/apps/Synapse.Cli/Commands/Workflows/ListWorkflowsCommand.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Cli.Commands.Workflows -{ - - ///

- /// Represents the used to list s - /// - internal class ListWorkflowsCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "list"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Lists/filters workflows"; - - /// - public ListWorkflowsCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.AddAlias("ls"); - this.AddOption(CommandOptions.Id); - this.AddOption(CommandOptions.Filter); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Handles the - /// - /// The id of the workflows to list - /// The ODATA filter to use - /// A new awaitable - public async Task HandleAsync(string id, string filter) - { - var query = string.Empty; - var filters = new List(); - if (!string.IsNullOrWhiteSpace(id)) - filters.Add($"startswith({nameof(V1Workflow.Definition)}/{nameof(WorkflowDefinition.Id)},'{id}')"); - if (!string.IsNullOrWhiteSpace(filter)) - filters.Add(filter); - if (filters.Any()) - query = $"$filter={string.Join(" AND ", filters)}"; - var workflows = await this.SynapseManagementApi.GetWorkflowsAsync(query); - var table = new Table(); - table.Expand = true; - table.Border(TableBorder.None); - table.AddColumn("ID"); - table.AddColumn("VERSION"); - table.AddColumn("CREATED AT"); - table.AddColumn("LAST INSTANCIATED AT"); - foreach(var workflow in workflows) - { - table.AddRow(workflow.Definition.Id!, workflow.Definition.Version, workflow.CreatedAt.ToString(), (workflow.LastInstanciated.HasValue ? workflow.LastInstanciated.ToString() : "-")!); - } - AnsiConsole.Write(table); - } - - private static class CommandOptions - { - - public static Option Id - { - get - { - var option = new Option("--id") - { - Description = "The id of the workflow to list the versions of" - }; - return option; - } - } - - public static Option Filter - { - get - { - var option = new Option("--filter") - { - Description = "The ODATA filter to apply" - }; - option.AddAlias("-f"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Commands/Workflows/RunWorkflowCommand.cs b/src/apps/Synapse.Cli/Commands/Workflows/RunWorkflowCommand.cs deleted file mode 100644 index 3c7065d11..000000000 --- a/src/apps/Synapse.Cli/Commands/Workflows/RunWorkflowCommand.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Serialization; - -namespace Synapse.Cli.Commands.Workflows -{ - - ///

- /// Represents the used to run a workflow - /// - internal class RunWorkflowCommand - : Command - { - - /// - /// Gets the 's name - /// - public const string CommandName = "run"; - /// - /// Gets the 's description - /// - public const string CommandDescription = "Runs a workflow"; - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to interact with the remote Synapse Management API - /// The service used to serialize/deserialize to/from JSON - public RunWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseManagementApi synapseManagementApi, IJsonSerializer jsonSerializer) - : base(serviceProvider, loggerFactory, synapseManagementApi, CommandName, CommandDescription) - { - this.JsonSerializer = jsonSerializer; - this.Add(new Argument("id") { Description = "The id of the workflow to run (ex: myworkflow:1.0). Note that failing to specify the version will run the latest version of the specified workflow" }); - this.Add(CommandOptions.Input); - this.Handler = CommandHandler.Create(this.HandleAsync); - } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - /// Handles the - /// - /// The id of the workflow to run. Failing to specify the version will run the latest version of the specified workflow - /// The input data JSON - /// A new awaitable - public async Task HandleAsync(string id, string input) - { - var inputData = null as Dynamic; - if (!string.IsNullOrWhiteSpace(input)) - inputData = await this.JsonSerializer.DeserializeAsync(input); - var instance = await this.SynapseManagementApi.CreateWorkflowInstanceAsync(new() - { - WorkflowId = id, - ActivationType = V1WorkflowInstanceActivationType.Manual, - AutoStart = true, - InputData = inputData - }); - Console.WriteLine($"The workflow instance with id '{instance.Id}' has been successfully created and started"); - } - - private static class CommandOptions - { - - public static Option Input - { - get - { - var option = new Option("--input") - { - Description = "The workflow's input data " - }; - option.AddAlias("-i"); - return option; - } - } - - } - - } - -} diff --git a/src/apps/Synapse.Cli/Extensions/HttpClientExtensions.cs b/src/apps/Synapse.Cli/Extensions/HttpClientExtensions.cs deleted file mode 100644 index a314c1527..000000000 --- a/src/apps/Synapse.Cli/Extensions/HttpClientExtensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli -{ - ///

- /// Defines extensions for s - /// - public static class HttpClientExtensions - { - - /// - /// Download the file at the specified uri - /// - /// The used to download - /// The file uri - /// The output - /// The to use - /// A - /// A new awaitable - public static async Task DownloadAsync(this HttpClient client, string fileUri, Stream outputStream, ProgressTask progress, CancellationToken cancellationToken = default) - { - using (HttpResponseMessage response = await client.GetAsync(fileUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken)) - { - response.EnsureSuccessStatusCode(); - progress.MaxValue(response.Content.Headers.ContentLength ?? 0); - progress.StartTask(); - var filename = fileUri.Substring(fileUri.LastIndexOf('/') + 1); - using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken); - var buffer = new byte[8192]; - while (true) - { - var bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); - if (bytesRead == 0) - break; - progress.Increment(bytesRead); - await outputStream.WriteAsync(buffer, 0, bytesRead, cancellationToken); - } - } - } - - } - -} diff --git a/src/apps/Synapse.Cli/Extensions/IServiceCollectionExtensions.cs b/src/apps/Synapse.Cli/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index 038659b5c..000000000 --- a/src/apps/Synapse.Cli/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Neuroglia; - -namespace Synapse.Cli -{ - - ///

- /// Defines extensions for s - /// - public static class IServiceCollectionExtensions - { - - /// - /// Adds and configures all s loaded from the specified assemblies - /// - /// The to configure - /// The configured - public static IServiceCollection AddCliCommands(this IServiceCollection services) - { - foreach (Type commandType in TypeCacheUtil.FindFilteredTypes - ( - "synctl-cmd", - t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType && typeof(Command).IsAssignableFrom(t) && t.IsPublic, - typeof(Commands.Command).Assembly) - ) - { - services.AddSingleton(typeof(Command), commandType); - } - return services; - } - - } - -} diff --git a/src/apps/Synapse.Cli/Platform.cs b/src/apps/Synapse.Cli/Platform.cs deleted file mode 100644 index 0fb6ddde8..000000000 --- a/src/apps/Synapse.Cli/Platform.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Cli -{ - - ///

- /// Enumerates all the platforms supported by Synapse - /// - public enum Platform - { - /// - /// Indicates that Synapse runs natively directly on the operating system - /// - Native, - /// - /// Indicates that Synapse runs on Docker - /// - Docker - } - -} diff --git a/src/apps/Synapse.Cli/Program.cs b/src/apps/Synapse.Cli/Program.cs deleted file mode 100644 index ae5afc6c6..000000000 --- a/src/apps/Synapse.Cli/Program.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Neuroglia.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using ServerlessWorkflow.Sdk; -using Synapse; -using Synapse.Cli; -using System.CommandLine.Builder; -using System.CommandLine.Parsing; - -var parser = BuildCommandLineParser(); -await parser.InvokeAsync(args); - -static Parser BuildCommandLineParser() -{ - var serviceProvider = BuildServiceProvider(); - var rootCommand = new RootCommand(); - foreach (var command in serviceProvider.GetServices()) - { - rootCommand.AddCommand(command); - } - return new CommandLineBuilder(rootCommand) - .UseDefaults() - .UseExceptionHandler((ex, context) => - { - AnsiConsole.MarkupLine($"[red]{ex.Message}[/]"); - var inner = ex.InnerException; - while(inner != null) - { - AnsiConsole.MarkupLine($"[red]{inner.Message}[/]"); - inner = inner.InnerException; - } - }) - .Build(); -} - -static IServiceProvider BuildServiceProvider() -{ - IServiceCollection services = new ServiceCollection(); - services.AddLogging(); - services.AddNewtonsoftJsonSerializer(settings => - { - settings.ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }; - settings.NullValueHandling = NullValueHandling.Ignore; - }); - services.AddServerlessWorkflow(); - services.AddSynapseRestApiClient(http => http.BaseAddress = new Uri("http://localhost:42286")); //todo: config based - services.AddCliCommands(); - return services.BuildServiceProvider(); -} \ No newline at end of file diff --git a/src/apps/Synapse.Cli/Properties/GlobalUsings.cs b/src/apps/Synapse.Cli/Properties/GlobalUsings.cs deleted file mode 100644 index 17d37a564..000000000 --- a/src/apps/Synapse.Cli/Properties/GlobalUsings.cs +++ /dev/null @@ -1,6 +0,0 @@ -global using Microsoft.Extensions.Logging; -global using Spectre.Console; -global using Synapse.Apis.Management; -global using Synapse.Integration.Models; -global using System.CommandLine; -global using System.CommandLine.NamingConventionBinder; diff --git a/src/apps/Synapse.Cli/Properties/launchSettings.json b/src/apps/Synapse.Cli/Properties/launchSettings.json deleted file mode 100644 index e600bbe84..000000000 --- a/src/apps/Synapse.Cli/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "Synapse.Cli": { - "commandName": "Project", - "commandLineArgs": "sys install native" - } - } -} \ No newline at end of file diff --git a/src/apps/Synapse.Cli/Synapse.Cli.csproj b/src/apps/Synapse.Cli/Synapse.Cli.csproj deleted file mode 100644 index ddba7a9ad..000000000 --- a/src/apps/Synapse.Cli/Synapse.Cli.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - net6.0 - enable - enable - Exe - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse synctl app cli - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Properties - embedded - synctl - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/Synapse.Server/.config/dotnet-tools.json b/src/apps/Synapse.Server/.config/dotnet-tools.json deleted file mode 100644 index 23f4f077a..000000000 --- a/src/apps/Synapse.Server/.config/dotnet-tools.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "dotnet-ef": { - "version": "6.0.3", - "commands": [ - "dotnet-ef" - ] - } - } -} \ No newline at end of file diff --git a/src/apps/Synapse.Server/Dockerfile b/src/apps/Synapse.Server/Dockerfile deleted file mode 100644 index e084e3d29..000000000 --- a/src/apps/Synapse.Server/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base -WORKDIR /app -EXPOSE 42286 41387 -RUN apt-get update -RUN apt-get install -y jq - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/apps/Synapse.Server/Synapse.Server.csproj", "src/apps/Synapse.Server/"] -COPY ["src/apis/runtime/Synapse.Apis.Runtime.Grpc/Synapse.Apis.Runtime.Grpc.csproj", "src/apis/runtime/Synapse.Apis.Runtime.Grpc/"] -COPY ["src/core/Synapse.Application/Synapse.Application.csproj", "src/core/Synapse.Application/"] -COPY ["src/core/Synapse.Infrastructure/Synapse.Infrastructure.csproj", "src/core/Synapse.Infrastructure/"] -COPY ["src/core/Synapse.Domain/Synapse.Domain.csproj", "src/core/Synapse.Domain/"] -COPY ["src/core/Synapse.Integration/Synapse.Integration.csproj", "src/core/Synapse.Integration/"] -COPY ["src/apis/runtime/Synapse.Apis.Runtime.Core/Synapse.Apis.Runtime.Core.csproj", "src/apis/runtime/Synapse.Apis.Runtime.Core/"] -COPY ["src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Synapse.Apis.Runtime.Grpc.Client.csproj", "src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/"] -COPY ["src/apis/management/Synapse.Apis.Management.Http/Synapse.Apis.Management.Http.csproj", "src/apis/management/Synapse.Apis.Management.Http/"] -COPY ["src/apis/management/Synapse.Apis.Management.Http.Client/Synapse.Apis.Management.Http.Client.csproj", "src/apis/management/Synapse.Apis.Management.Http.Client/"] -COPY ["src/apis/management/Synapse.Apis.Management.Core/Synapse.Apis.Management.Core.csproj", "src/apis/management/Synapse.Apis.Management.Core/"] -COPY ["src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/Synapse.Apis.Monitoring.WebSocket.csproj", "src/apis/monitoring/Synapse.Apis.Monitoring.WebSocket/"] -COPY ["src/apis/monitoring/Synapse.Apis.Monitoring.Core/Synapse.Apis.Monitoring.Core.csproj", "src/apis/monitoring/Synapse.Apis.Monitoring.Core/"] -COPY ["src/runtime/Synapse.Runtime.Docker/Synapse.Runtime.Docker.csproj", "src/runtime/Synapse.Runtime.Docker/"] -COPY ["src/runtime/Synapse.Runtime.Kubernetes/Synapse.Runtime.Kubernetes.csproj", "src/runtime/Synapse.Runtime.Kubernetes/"] -COPY ["src/apis/management/Synapse.Apis.Management.Grpc/Synapse.Apis.Management.Grpc.csproj", "src/apis/management/Synapse.Apis.Management.Grpc/"] -COPY ["src/apis/management/Synapse.Apis.Management.Grpc.Client/Synapse.Apis.Management.Grpc.Client.csproj", "src/apis/management/Synapse.Apis.Management.Grpc.Client/"] -COPY ["src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj", "src/dashboard/Synapse.Dashboard/"] -COPY ["src/runtime/Synapse.Runtime.Native/Synapse.Runtime.Native.csproj", "src/runtime/Synapse.Runtime.Native/"] -RUN dotnet restore "src/apps/Synapse.Server/Synapse.Server.csproj" -COPY . . -WORKDIR "/src/src/apps/Synapse.Server" -RUN dotnet build "Synapse.Server.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "Synapse.Server.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "Synapse.dll"] \ No newline at end of file diff --git a/src/apps/Synapse.Server/KubernetesLocalProcessConfig.yaml b/src/apps/Synapse.Server/KubernetesLocalProcessConfig.yaml deleted file mode 100644 index 8e1f53128..000000000 --- a/src/apps/Synapse.Server/KubernetesLocalProcessConfig.yaml +++ /dev/null @@ -1,8 +0,0 @@ -version: 0.1 -env: - - name: KUBERNETES_SERVICE_HOST - value: kubernetes.docker.internal - - name: KUBERNETES_SERVICE_PORT - value: 6443 - - name: SYNAPSE_API_HOSTNAME - value: synapse \ No newline at end of file diff --git a/src/apps/Synapse.Server/Program.cs b/src/apps/Synapse.Server/Program.cs deleted file mode 100644 index 25e5d4407..000000000 --- a/src/apps/Synapse.Server/Program.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.OData; -using Neuroglia.Caching; -using Neuroglia.Data.Expressions.JQ; -using Neuroglia.Eventing; -using ProtoBuf.Grpc.Server; -using Swashbuckle.AspNetCore.SwaggerUI; -using Synapse; -using Synapse.Apis.Management.Grpc; -using Synapse.Apis.Management.Http; -using Synapse.Apis.Monitoring.WebSocket; -using Synapse.Apis.Runtime.Grpc; -using Synapse.Application.Configuration; -using Synapse.Runtime; - -WebApplicationBuilder builder = WebApplication.CreateBuilder(args); -builder.Services.AddLogging(builder => -{ - builder.AddSimpleConsole(options => - { - options.TimestampFormat = "[MM/dd/yyyy HH:mm:ss] "; - }); -}); -builder.Services.AddMemoryDistributedCache(); -builder.Services.AddCodeFirstGrpc(); -builder.Services.AddSynapse(builder.Configuration, synapse => -{ - synapse - .UseHttpManagementApi() - .UseWebSocketMonitoringApi(); - - if (builder.Environment.RunsInKubernetes()) - { - synapse.UseKubernetesRuntime(); - } - else if (builder.Environment.RunsInDocker()) - { - synapse.UseDockerRuntime(); - } - else - { - synapse.UseNativeRuntime(); - } -}); -builder.Services.AddJQExpressionEvaluator(); - -using WebApplication app = builder.Build(); -if (app.Environment.IsDevelopment()) -{ - app.UseWebAssemblyDebugging(); -} -else -{ - app.UseExceptionHandler("/error"); -} -app.UseCloudEvents(); -app.UseBlazorFrameworkFiles(); -app.UseStaticFiles(); -app.UseODataRouteDebug(); -app.UseRouting(); -app.UseAuthentication(); -app.UseAuthorization(); -app.UseSwagger(builder => -{ - builder.RouteTemplate = "api/{documentName}/doc/oas.{json|yaml}"; -}); -app.UseSwaggerUI(builder => -{ - builder.DocExpansion(DocExpansion.None); - builder.SwaggerEndpoint("/api/v1/doc/oas.json", "Synapse API v1"); - builder.RoutePrefix = "api/doc"; - builder.DisplayOperationId(); -}); -app.MapControllers(); -app.MapGrpcService(); -app.MapGrpcService(); -app.MapHub("/api/ws"); -app.MapFallbackToFile("index.html"); -app.MapFallbackToFile("/workflows/{param?}", "index.html"); -app.MapFallbackToFile("/workflow-instances/{param?}", "index.html"); - -await app.RunAsync(); \ No newline at end of file diff --git a/src/apps/Synapse.Server/Properties/launchSettings.json b/src/apps/Synapse.Server/Properties/launchSettings.json deleted file mode 100644 index d40682b14..000000000 --- a/src/apps/Synapse.Server/Properties/launchSettings.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:42286", - "sslPort": 0 - } - }, - "profiles": { - "Synapse.Server": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "SYNAPSE_PERSISTENCE_WRITEMODEL_DEFAULT_REPOSITORY": "EventStore", - "ASPNETCORE_ENVIRONMENT": "Development", - "SYNAPSE_PERSISTENCE_READMODEL_DEFAULT_REPOSITORY": "MongoDB", - "SYNAPSE_API_HOSTNAME": "localhost", - "SYNAPSE_SKIP_CERTIFICATE_VALIDATION": "true" - }, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:42286", - "dotnetRunMessages": true - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development", - "SYNAPSE_API_HOSTNAME": "localhost" - } - }, - "Docker": { - "commandName": "Docker", - "launchBrowser": true, - "launchUrl": "{Scheme}://{ServiceHost}:42286", - "environmentVariables": { - "SYNAPSE_PERSISTENCE_WRITEMODEL_DEFAULT_REPOSITORY": "EventStore", - "ASPNETCORE_ENVIRONMENT": "Development", - "SYNAPSE_PERSISTENCE_READMODEL_DEFAULT_REPOSITORY": "MongoDB", - "SYNAPSE_API_HOSTNAME": "synapse" - }, - "DockerfileRunArguments": "--name synapse -e SYNAPSE_API_HOSTNAME=synapse -e DOCKER__SECRETS__DIRECTORY=C:\\Users\\Public\\secrets\\ -e SYNAPSE_SKIP_CERTIFICATE_VALIDATION=true -v /var/run/docker.sock:/var/run/docker.sock --add-host=host.docker.internal:host-gateway -p 42286:42286 -p 41387:41387", - "publishAllPorts": true - }, - "Bridge to Kubernetes": { - "commandName": "AzureDevSpacesLocal", - "launchBrowser": true - } - } -} \ No newline at end of file diff --git a/src/apps/Synapse.Server/Synapse.Server.csproj b/src/apps/Synapse.Server/Synapse.Server.csproj deleted file mode 100644 index 214c09e04..000000000 --- a/src/apps/Synapse.Server/Synapse.Server.csproj +++ /dev/null @@ -1,52 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse synctl app cli - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Properties - Linux - ..\..\.. - ghcr.io/serverlessworkflow/synapse - win10-x64;linux-x64;osx-x64 - false - Synapse - synapse-icon.ico - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/Synapse.Server/appsettings.Development.json b/src/apps/Synapse.Server/appsettings.Development.json deleted file mode 100644 index b90915095..000000000 --- a/src/apps/Synapse.Server/appsettings.Development.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "DetailedErrors": true -} diff --git a/src/apps/Synapse.Server/appsettings.json b/src/apps/Synapse.Server/appsettings.json deleted file mode 100644 index 9cc22bc60..000000000 --- a/src/apps/Synapse.Server/appsettings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "AllowedHosts": "*", - "Kestrel": { - "Endpoints": { - "Http": { - "Url": "http://+:42286", - "Protocols": "Http1AndHttp2" - }, - "gRPC": { - "Url": "http://+:41387", - "Protocols": "Http2" - } - } - }, - "CloudEvents": { - "Sink": { - "Uri": "https://webhook.site/70e57ac3-c28c-41cc-9a61-070810da8b3b" - } - } -} diff --git a/src/apps/Synapse.Server/synapse-icon.ico b/src/apps/Synapse.Server/synapse-icon.ico deleted file mode 100644 index 836a0e277..000000000 Binary files a/src/apps/Synapse.Server/synapse-icon.ico and /dev/null differ diff --git a/src/apps/Synapse.Worker/AuthorizationInfo.cs b/src/apps/Synapse.Worker/AuthorizationInfo.cs deleted file mode 100644 index 69b18c927..000000000 --- a/src/apps/Synapse.Worker/AuthorizationInfo.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using System.Text; - -namespace Synapse.Worker; - -///

-/// Describes the credentials used to authenticate a user agent with an application -/// -public class AuthorizationInfo -{ - - /// - /// Initializes a new - /// - public AuthorizationInfo() { } - - /// - /// Initializes a new - /// - /// The authorization scheme - /// The authorization parameters - public AuthorizationInfo(string scheme, string parameters) - { - this.Scheme = scheme; - this.Parameters = parameters; - } - - /// - /// Gets the authorization scheme - /// - public virtual string Scheme { get; set; } = null!; - - /// - /// Gets the authorization parameters - /// - public virtual string Parameters { get; set; } = null!; - - /// - public override string ToString() => $"{this.Scheme} {this.Parameters}"; - - /// - /// Creates a new from the specified - /// - /// The current - /// The that describes the authentication mechanism to use - /// A - /// A new - /// - /// - public static async Task CreateAsync(IServiceProvider serviceProvider, AuthenticationDefinition? authentication, CancellationToken cancellationToken = default) - { - if (authentication == null) return null; - string scheme; - string value; - switch (authentication.Properties) - { - case BasicAuthenticationProperties basic: - scheme = "Basic"; - value = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{basic.Username}:{basic.Password}")); - break; - case BearerAuthenticationProperties bearer: - scheme = "Bearer"; - value = bearer.Token; - break; - case OAuth2AuthenticationProperties oauth: - scheme = "Bearer"; - var token = await serviceProvider.GetRequiredService().GetTokenAsync(oauth, cancellationToken); - if (token == null) throw new NullReferenceException($"Failed to generate an OAUTH2 token"); - value = token.AccessToken!; - break; - default: - throw new NotSupportedException($"The specified authentication schema '{EnumHelper.Stringify(authentication.Scheme)}' is not supported"); - } - return new(scheme, value); - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Configuration/ApiClientConfiguration.cs b/src/apps/Synapse.Worker/Configuration/ApiClientConfiguration.cs deleted file mode 100644 index f5a986341..000000000 --- a/src/apps/Synapse.Worker/Configuration/ApiClientConfiguration.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Synapse.Worker.Configuration -{ - /// - /// Represents the object used to configure a Synapse API client - /// - public class ApiClientConfiguration - { - - /// - /// Gets/sets the scheme of the API to connect to - /// - public virtual string? Scheme { get; set; } - - /// - /// Gets/sets the port number of the API to connect to - /// - public virtual int? Port { get; set; } - - /// - /// Gets the default HTTP-based - /// - public static ApiClientConfiguration HttpDefault - { - get - { - return new() { Scheme = "http", Port = 42286 }; - } - } - - /// - /// Gets the default GRPC-based - /// - public static ApiClientConfiguration GrpcDefault - { - get - { - return new() { Scheme = "http", Port = 41387 }; - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Configuration/ApiClientConfigurationCollection.cs b/src/apps/Synapse.Worker/Configuration/ApiClientConfigurationCollection.cs deleted file mode 100644 index a1e73d683..000000000 --- a/src/apps/Synapse.Worker/Configuration/ApiClientConfigurationCollection.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Configuration -{ - - ///

- /// Represents the object used to configure the Synapse APIs clients used to by the application - /// - public class ApiClientConfigurationCollection - { - - /// - /// Gets the default Synapse server's host name - /// - public static string DefaultHostName - { - get - { - var env = EnvironmentVariables.Api.HostName.Value; - if (!string.IsNullOrWhiteSpace(env)) - return env; - return "synapse"; - } - } - - /// - /// Gets/sets the Synapse server's host name. Defaults to - /// - public virtual string HostName { get; set; } = DefaultHostName; - - /// - /// Gets/sets an object used to configure Synapse http-based APIs - /// - public virtual ApiClientConfiguration Http { get; set; } = ApiClientConfiguration.HttpDefault; - - /// - /// Gets/sets an object used to configure Synapse GRPC-based APIs - /// - public virtual ApiClientConfiguration Grpc { get; set; } = ApiClientConfiguration.GrpcDefault; - - } - -} diff --git a/src/apps/Synapse.Worker/Configuration/ApplicationOptions.cs b/src/apps/Synapse.Worker/Configuration/ApplicationOptions.cs deleted file mode 100644 index c372705d2..000000000 --- a/src/apps/Synapse.Worker/Configuration/ApplicationOptions.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Synapse.Worker.Configuration -{ - - /// - /// Represents the object used to configure the application - /// - public class ApplicationOptions - { - - /// - /// Initializes a new - /// - public ApplicationOptions() - { - var env = EnvironmentVariables.SkipCertificateValidation.Value; - if (!string.IsNullOrWhiteSpace(env) - && bool.TryParse(env, out var skipCertificateValidation)) - this.SkipCertificateValidation = skipCertificateValidation; - } - - /// - /// Gets/sets a boolean indicating whether or not to skip certificate validation when performing http requests - /// - public virtual bool SkipCertificateValidation { get; set; } - - /// - /// Gets/sets the object used to configure the Synapse APIs clients used to by the application - /// - public virtual ApiClientConfigurationCollection Api { get; set; } = new(); - - /// - /// Gets/sets the options used to configure the runtime's event correlation feature - /// - public virtual CorrelationOptions Correlation { get; set; } = new(); - - } - -} diff --git a/src/apps/Synapse.Worker/Configuration/CorrelationOptions.cs b/src/apps/Synapse.Worker/Configuration/CorrelationOptions.cs deleted file mode 100644 index 807cd9bd9..000000000 --- a/src/apps/Synapse.Worker/Configuration/CorrelationOptions.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Synapse.Worker.Configuration -{ - /// - /// Represents the object used to configure the runtime's event correlation feature - /// - public class CorrelationOptions - { - - /// - /// Gets the default timeout, in seconds - /// - public const int DefaultTimeout = 30; - - /// - /// Initializes a new - /// - public CorrelationOptions() - { - string raw; - raw = EnvironmentVariables.Runtime.Correlation.Mode.Value; - if (!string.IsNullOrWhiteSpace(raw)) - this.Mode = EnumHelper.Parse(raw); - if (this.Mode == RuntimeCorrelationMode.Passive) - return; - raw = EnvironmentVariables.Runtime.Correlation.MaxActiveDuration.Value; - if (!string.IsNullOrWhiteSpace(raw) - && TimeSpan.TryParse(raw, out TimeSpan timeout)) - this.Timeout = timeout; - else - this.Timeout = TimeSpan.FromSeconds(DefaultTimeout); - } - - /// - /// Gets/sets the application's correlation mode - /// - public RuntimeCorrelationMode Mode { get; set; } - - /// - /// Gets/sets the amount of time after which the Synapse Runner falls back to the , if is set to - /// - public TimeSpan? Timeout { get; set; } - - } - -} diff --git a/src/apps/Synapse.Worker/Dockerfile b/src/apps/Synapse.Worker/Dockerfile deleted file mode 100644 index d6c286817..000000000 --- a/src/apps/Synapse.Worker/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. - -FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base -WORKDIR /app -RUN apt-get update -RUN apt-get install -y jq - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/apps/Synapse.Worker/Synapse.Worker.csproj", "src/apps/Synapse.Worker/"] -COPY ["src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/Synapse.Apis.Runtime.Grpc.Client.csproj", "src/apis/runtime/Synapse.Apis.Runtime.Grpc.Client/"] -COPY ["src/apis/runtime/Synapse.Apis.Runtime.Core/Synapse.Apis.Runtime.Core.csproj", "src/apis/runtime/Synapse.Apis.Runtime.Core/"] -COPY ["src/core/Synapse.Integration/Synapse.Integration.csproj", "src/core/Synapse.Integration/"] -COPY ["src/apis/management/Synapse.Apis.Management.Grpc.Client/Synapse.Apis.Management.Grpc.Client.csproj", "src/apis/management/Synapse.Apis.Management.Grpc.Client/"] -COPY ["src/apis/management/Synapse.Apis.Management.Core/Synapse.Apis.Management.Core.csproj", "src/apis/management/Synapse.Apis.Management.Core/"] -RUN dotnet restore "src/apps/Synapse.Worker/Synapse.Worker.csproj" -COPY . . -WORKDIR "/src/src/apps/Synapse.Worker" -RUN dotnet build "Synapse.Worker.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "Synapse.Worker.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "Synapse.Worker.dll"] \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Extensions/ActionDefinitionExtensions.cs b/src/apps/Synapse.Worker/Extensions/ActionDefinitionExtensions.cs deleted file mode 100644 index 24d157094..000000000 --- a/src/apps/Synapse.Worker/Extensions/ActionDefinitionExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker -{ - ///

- /// Defines extensions for s - /// - public static class ActionDefinitionExtensions - { - - /// - /// Determines whether or not to use the 's results - /// - /// The to check - /// A boolean indicating whether or not the operation was successfull - public static bool UseResults(this ActionDefinition action) => action.ActionDataFilter == null || (action.ActionDataFilter != null && action.ActionDataFilter.UseResults); - - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Extensions/HttpClientExtensions.cs b/src/apps/Synapse.Worker/Extensions/HttpClientExtensions.cs deleted file mode 100644 index ef38fa2ad..000000000 --- a/src/apps/Synapse.Worker/Extensions/HttpClientExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker -{ - - ///

- /// Defines extensions for s - /// - public static class HttpClientExtensions - { - - /// - /// Configures the to use the specified - /// - /// The to configure - /// An object that describes the authorization mechanism to use - /// A new awaitable - public static void UseAuthorization(this HttpClient httpClient, AuthorizationInfo? authorization) - { - if (authorization == null) return; - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authorization.Scheme, authorization.Parameters); - } - - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Extensions/HttpResponseMessageExtensions.cs b/src/apps/Synapse.Worker/Extensions/HttpResponseMessageExtensions.cs deleted file mode 100644 index a26620bb4..000000000 --- a/src/apps/Synapse.Worker/Extensions/HttpResponseMessageExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker -{ - ///

- /// Defines extensions for s - /// - public static class HttpResponseMessageExtensions - { - - /// - /// Ensures that the returned a success status code, and throws if it's not the case - /// - /// The - /// The reponse contents, if any - /// The exception is thrown if the has a non-success status code - public static void EnsureSuccessStatusCode(this HttpResponseMessage response, string? responseContent) - { - if (response.IsSuccessStatusCode) - return; - throw new HttpRequestException($"The server responsed with a non-success status code '{(int)response.StatusCode} {response.StatusCode}' to HTTP request {response.RequestMessage!.Method} {response.RequestMessage!.RequestUri}{Environment.NewLine}{(string.IsNullOrWhiteSpace(responseContent) ? "" : responseContent)}", null, response.StatusCode); - } - - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Extensions/IWorkflowRuntimeContextExtensions.cs b/src/apps/Synapse.Worker/Extensions/IWorkflowRuntimeContextExtensions.cs deleted file mode 100644 index aec022a60..000000000 --- a/src/apps/Synapse.Worker/Extensions/IWorkflowRuntimeContextExtensions.cs +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Dynamic; - -namespace Synapse.Worker -{ - - ///

- /// Defines extensions for s - /// - public static class IWorkflowRuntimeContextExtensions - { - - /// - /// Evaluates an object against the specified data - /// - /// The current - /// The object to evaluate - /// The data to evaluate the object against - /// The current , if any - /// A - /// The evaluated object - public static async Task EvaluateObjectAsync(this IWorkflowRuntimeContext context, object expressionObject, object data, AuthorizationInfo? authorization, CancellationToken cancellationToken = default) - { - var json = JsonConvert.SerializeObject(expressionObject, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); ; - foreach (var match in Regex.Matches(json, @"""\$\{.+?\}""", RegexOptions.Compiled).Cast()) - { - var expression = Regex.Unescape(match.Value[3..^2].Trim().Replace(@"\""", @"""")); - var evaluationResult = await context.EvaluateAsync(expression, data, authorization, cancellationToken); - if (evaluationResult == null) continue; - var valueToken = JToken.FromObject(evaluationResult); - var value = null as string; - if (valueToken != null) - { - value = valueToken.Type switch - { - JTokenType.String => JsonConvert.SerializeObject(valueToken), - _ => valueToken.ToString(), - }; - } - if (string.IsNullOrEmpty(value)) value = "null"; - json = json.Replace(match.Value, value); - } - return JsonConvert.DeserializeObject(json)!; - } - - /// - /// Evaluates an object against the specified data - /// - /// The current - /// The object to evaluate - /// The data to evaluate the object against - /// A - /// The evaluated object - public static Task EvaluateObjectAsync(this IWorkflowRuntimeContext context, object expressionObject, object data, CancellationToken cancellationToken = default) - { - return EvaluateObjectAsync(context, expressionObject, data, null, cancellationToken); - } - - /// - /// Evaluates the specified condition expression - /// - /// The to use - /// The condition expression to evaluate - /// The data to perform the evaluation against - /// A - /// A boolean indicating whether or not the condition expression matches to the specified data - public static async Task EvaluateConditionAsync(this IWorkflowRuntimeContext context, string expression, object data, CancellationToken cancellationToken = default) - { - var result = await context.EvaluateAsync(expression, data, cancellationToken); - if (result == null) - return false; - if (result is bool success) - return success; - else - return true; - } - - /// - /// Filters the input of the specified - /// - /// The to use - /// The to filter the input for - /// The input data to filter - /// A - /// The filtered input - public static async Task FilterInputAsync(this IWorkflowRuntimeContext context, StateDefinition state, object input, CancellationToken cancellationToken = default) - { - if (state.DataFilter == null - || string.IsNullOrWhiteSpace(state.DataFilter.Input)) - return input; - return await context.EvaluateAsync(state.DataFilter.Input, input, cancellationToken); - } - - /// - /// Filters the input of the specified - /// - /// The to use - /// The to filter the input for - /// The input data to filter - /// A - /// The filtered input - public static async Task FilterInputAsync(this IWorkflowRuntimeContext context, ActionDefinition action, object input, CancellationToken cancellationToken = default) - { - if (action.ActionDataFilter == null - || string.IsNullOrWhiteSpace(action.ActionDataFilter.FromStateData)) - return input; - return await context.EvaluateAsync(action.ActionDataFilter.FromStateData, input, cancellationToken); - } - - /// - /// Filters the output of the specified - /// - /// The to use - /// The to filter the output for - /// The output data to filter - /// A - /// The filtered output - public static async Task FilterOutputAsync(this IWorkflowRuntimeContext context, StateDefinition state, object output, CancellationToken cancellationToken = default) - { - if (state.DataFilter == null || string.IsNullOrWhiteSpace(state.DataFilter.Output)) return output; - return await context.EvaluateAsync(state.DataFilter.Output, output, cancellationToken); - } - - /// - /// Filters the output of the specified - /// - /// The to use - /// The to filter the output for - /// The output data to filter - /// The current , if any - /// A - /// The filtered output - public static async Task FilterOutputAsync(this IWorkflowRuntimeContext context, ActionDefinition action, object output, AuthorizationInfo? authorization, CancellationToken cancellationToken = default) - { - if (action.ActionDataFilter == null - || string.IsNullOrWhiteSpace(action.ActionDataFilter.Results)) - return output; - return await context.EvaluateAsync(action.ActionDataFilter.Results, output, authorization, cancellationToken); - } - - /// - /// Filters the output of the specified - /// - /// The to use - /// The to filter the output for - /// The output data to filter - /// A - /// The filtered output - public static Task FilterOutputAsync(this IWorkflowRuntimeContext context, ActionDefinition action, object output, CancellationToken cancellationToken = default) - { - return FilterOutputAsync(context, action, output, cancellationToken); - } - - /// - /// Filters output data based on the specified - /// - /// The to use - /// The used to filter the specified output - /// The output data to filter - /// A - /// The filtered output - public static async Task FilterOutputAsync(this IWorkflowRuntimeContext context, EventDataFilterDefinition filter, object output, CancellationToken cancellationToken = default) - { - if (filter == null - || string.IsNullOrWhiteSpace(filter.Data)) - return output; - return await context.EvaluateAsync(filter.Data, output, cancellationToken); - } - - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Extensions/ODataQueryOptionsExtensions.cs b/src/apps/Synapse.Worker/Extensions/ODataQueryOptionsExtensions.cs deleted file mode 100644 index f80d88336..000000000 --- a/src/apps/Synapse.Worker/Extensions/ODataQueryOptionsExtensions.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker -{ - ///

- /// Defines extensions for - /// - public static class ODataQueryOptionsExtensions - { - - /// - /// Converts the specified to an ODATA query string - /// - /// The to convert - /// An ODATA query string - public static string ToQueryString(this ODataQueryOptions queryOptions) - { - if (queryOptions == null) - throw new ArgumentNullException(nameof(queryOptions)); - var values = new List(12); - if (!string.IsNullOrWhiteSpace(queryOptions.Compute)) - values.Add($"$compute={queryOptions.Compute}"); - if (queryOptions.Count.HasValue && queryOptions.Count.Value) - values.Add($"$count={queryOptions.Count}"); - if (!string.IsNullOrWhiteSpace(queryOptions.Expand)) - values.Add($"$expand={queryOptions.Expand}"); - if (!string.IsNullOrWhiteSpace(queryOptions.Filter)) - values.Add($"$filter={queryOptions.Filter}"); - if (!string.IsNullOrWhiteSpace(queryOptions.Format)) - values.Add($"$format={queryOptions.Format}"); - if (!string.IsNullOrWhiteSpace(queryOptions.OrderBy)) - values.Add($"$orderBy={queryOptions.OrderBy}"); - if (!string.IsNullOrWhiteSpace(queryOptions.Search)) - values.Add($"$search={queryOptions.Search}"); - if (!string.IsNullOrWhiteSpace(queryOptions.Select)) - values.Add($"$select={queryOptions.Select}"); - if (queryOptions.Skip.HasValue) - values.Add($"$skip={queryOptions.Skip}"); - if (queryOptions.Top.HasValue) - values.Add($"$top={queryOptions.Top}"); - return string.Join("&", values); - } - - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Extensions/OperationTypeExtensions.cs b/src/apps/Synapse.Worker/Extensions/OperationTypeExtensions.cs deleted file mode 100644 index 2ed327c8f..000000000 --- a/src/apps/Synapse.Worker/Extensions/OperationTypeExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Microsoft.OpenApi.Models; - -namespace Synapse.Worker -{ - ///

- /// Defines extensions for s - /// - public static class OperationTypeExtensions - { - - /// - /// Converts the into a new - /// - /// The to convert - /// The reuslting - public static HttpMethod ToHttpMethod(this OperationType operationType) - { - return operationType switch - { - OperationType.Delete => HttpMethod.Delete, - OperationType.Get => HttpMethod.Get, - OperationType.Head => HttpMethod.Head, - OperationType.Options => HttpMethod.Options, - OperationType.Patch => HttpMethod.Patch, - OperationType.Post => HttpMethod.Post, - OperationType.Put => HttpMethod.Put, - OperationType.Trace => HttpMethod.Trace, - _ => throw new NotSupportedException($"The specified {nameof(OperationType)} '{operationType}' is not supported"), - }; - } - - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Extensions/StateDefinitionExtensions.cs b/src/apps/Synapse.Worker/Extensions/StateDefinitionExtensions.cs deleted file mode 100644 index 396d3d8a3..000000000 --- a/src/apps/Synapse.Worker/Extensions/StateDefinitionExtensions.cs +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker -{ - ///

- /// Defines extensions for s - /// - public static class StateDefinitionExtensions - { - - /// - /// Gets the described by the specified metadata - /// - /// The the to get belongs to - /// An that describes the to get - /// The described by the specified metadata - public static ActionDefinition GetAction(this StateDefinition state, IDictionary metadata) - { - ActionDefinition action; - if (!metadata.TryGetValue(V1WorkflowActivityMetadata.Action, out var actionName)) - throw new ArgumentException($"The metadata is missing the required metadata field '{V1WorkflowActivityMetadata.Action}'"); - switch (state) - { - case OperationStateDefinition operationState: - if (!operationState.TryGetAction(actionName, out action)) - throw new NullReferenceException($"Failed to find an action with the specified name '{actionName}' in the state with name '{state.Name}'"); - break; - case ForEachStateDefinition forEachState: - if (!forEachState.TryGetAction(actionName, out action)) - throw new NullReferenceException($"Failed to find an action with the specified name '{actionName}' in the state with name '{state.Name}'"); - break; - case CallbackStateDefinition callbackState: - action = callbackState.Action!; - break; - case ParallelStateDefinition parallelState: - if (!metadata.TryGetValue(V1WorkflowActivityMetadata.Branch, out var branchName)) - throw new ArgumentException($"The metadata is missing the required metadata field '{V1WorkflowActivityMetadata.Branch}'"); - if (!parallelState.TryGetBranch(branchName, out var branch)) - throw new NullReferenceException($"Failed to find a branch with the specified name '{branchName}' in the state with name '{state.Name}'"); - if (!branch.TryGetAction(actionName, out action)) - throw new NullReferenceException($"Failed to find an action with the specified name '{action.Name}' in the branch with name '{branch.Name}'"); - break; - case EventStateDefinition eventState: - if (!metadata.TryGetValue(V1WorkflowActivityMetadata.Trigger, out var triggerId)) - throw new ArgumentException($"The metadata is missing the required metadata field '{V1WorkflowActivityMetadata.Trigger}'"); - if (!int.TryParse(triggerId, out var triggerIndex)) - throw new ArgumentException($"The metadata has an unexpected value '{triggerId}' set for the metadata field '{V1WorkflowActivityMetadata.Trigger}'"); - if (!eventState.TryGetTrigger(triggerIndex, out var trigger)) - throw new NullReferenceException($"Failed to find an event state trigger at the specified index '{triggerIndex}' in the state with name'{state.Name}'"); - if (!trigger.TryGetAction(actionName, out action)) - throw new NullReferenceException($"Failed to find an action with the specified name '{action.Name}' in the trigger at the specified index '{triggerIndex}'"); - break; - default: - throw new NotSupportedException($"The specified {nameof(StateDefinition)} type '{state.GetType().Name}' is not supported in this context"); - } - return action; - } - - /// - /// Attempts to get the described by the specified metadata - /// - /// The the to get belongs to - /// The metadata that describes the to get - /// The described by the specified metadata - /// A boolean indicating whether or not the action described bv the specified metadata could be resolved - public static bool TryGetAction(this StateDefinition state, IDictionary metadata, out ActionDefinition action) - { - action = null!; - try - { - action = state.GetAction(metadata); - return action != null; - } - catch - { - return false; - } - } - - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/OAuth2Token.cs b/src/apps/Synapse.Worker/OAuth2Token.cs deleted file mode 100644 index d842c6b0f..000000000 --- a/src/apps/Synapse.Worker/OAuth2Token.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker -{ - - ///

- /// Describes an OAUTH2 token - /// - public class OAuth2Token - { - - /// - /// Initializes a new - /// - protected OAuth2Token() - { - this.CreatedAt = DateTime.UtcNow; - } - - /// - /// Gets the UTC date and time at which the has been created - /// - public virtual DateTime CreatedAt { get; protected set; } - - /// - /// Gets the OAUTH2 token type - /// - [Newtonsoft.Json.JsonProperty("token_type")] - [System.Text.Json.Serialization.JsonPropertyName("token_type")] - public virtual string? TokenType { get; protected set; } - - /// - /// Gets the OAUTH2 token id - /// - [Newtonsoft.Json.JsonProperty("token_id")] - [System.Text.Json.Serialization.JsonPropertyName("token_id")] - public virtual string? TokenId { get; protected set; } - - /// - /// Gets the OAUTH2 access token - /// - [Newtonsoft.Json.JsonProperty("access_token")] - [System.Text.Json.Serialization.JsonPropertyName("access_token")] - public virtual string? AccessToken { get; protected set; } - - /// - /// Gets the OAUTH2 refresh token - /// - [Newtonsoft.Json.JsonProperty("refresh_token")] - [System.Text.Json.Serialization.JsonPropertyName("refresh_token")] - public virtual string? RefreshToken { get; protected set; } - - /// - /// Gets the Time To Live, in seconds - /// - [Newtonsoft.Json.JsonProperty("expires_in")] - [System.Text.Json.Serialization.JsonPropertyName("expires_in")] - public virtual int Ttl { get; protected set; } - - /// - /// Gets the UTC date and time at which the expires - /// - [Newtonsoft.Json.JsonProperty("expires_on")] - [System.Text.Json.Serialization.JsonPropertyName("expires_on")] - public virtual DateTime ExpiresAt { get; protected set; } - - /// - /// Gets a boolean indicating whether or not the has expired - /// - public virtual bool HasExpired => DateTime.UtcNow > this.ExpiresAt; - - } - -} diff --git a/src/apps/Synapse.Worker/Program.cs b/src/apps/Synapse.Worker/Program.cs deleted file mode 100644 index 74bd32e58..000000000 --- a/src/apps/Synapse.Worker/Program.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using GraphQL.Client.Abstractions; -using GraphQL.Client.Abstractions.Websocket; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Http; -using Neuroglia.AsyncApi; -using Neuroglia.Data.Expressions.JQ; -using Synapse.Apis.Management.Grpc; -using Synapse.Apis.Runtime.Grpc; -using Synapse.Integration.Serialization.Converters; -using System.Diagnostics; - -if (args.Any() - && args.Contains("--debug") - && !Debugger.IsAttached) - Debugger.Launch(); -AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); -using var host = Host.CreateDefaultBuilder(args) - .ConfigureAppConfiguration(config => - { - config.AddJsonFile("appsettings.json", true, true); - config.AddKeyPerFile("/run/secrets/synapse", true, true); - }) - .ConfigureServices((context, services) => - { - var applicationOptions = new ApplicationOptions(); - context.Configuration.Bind(applicationOptions); - services.AddSingleton(Options.Create(applicationOptions)); - - services.AddLogging(builder => - { - builder.AddSimpleConsole(options => - { - options.TimestampFormat = "[HH:mm:ss] "; - }); - }); - services.AddAsyncApiClientFactory(asyncApi => - { - asyncApi.UseAllBindings(); - }); - services.AddSynapseGrpcManagementApiClient(); - services.AddSynapseGrpcRuntimeApiClient(); - - services.AddNewtonsoftJsonSerializer(settings => - { - settings.Converters.Add(new FilteredExpandoObjectConverter()); - settings.NullValueHandling = NullValueHandling.Ignore; - settings.DefaultValueHandling = DefaultValueHandling.Ignore; - }); - services.AddServerlessWorkflow(); - services.AddJQExpressionEvaluator(); - services.AddHttpClient(); - services.AddTransient(); - services.AddTransient(provider => provider.GetRequiredService()); - services.AddTransient(provider => provider.GetRequiredService()); - - services.AddSingleton(); - services.AddSingleton(provider => provider.GetRequiredService()); - - services.AddSingleton(); - services.AddSingleton(provider => provider.GetRequiredService()); - services.AddHostedService(provider => provider.GetRequiredService()); - - services.AddSingleton(); - services.AddSingleton(provider => provider.GetRequiredService()); - - services.AddSingleton(); - services.AddSingleton(provider => provider.GetRequiredService()); - - services.AddSingleton(); - services.AddSingleton(provider => provider.GetRequiredService()); - services.AddHostedService(provider => provider.GetRequiredService()); - - services.AddSingleton(); - - if (applicationOptions.SkipCertificateValidation) - { - System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; - services.ConfigureAll(options => - { - options.HttpMessageHandlerBuilderActions.Add(builder => - { - builder.PrimaryHandler = new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true - }; - }); - }); - } - }) - .Build(); -await host.RunAsync(); \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Properties/GlobalUsings.cs b/src/apps/Synapse.Worker/Properties/GlobalUsings.cs deleted file mode 100644 index f3062d130..000000000 --- a/src/apps/Synapse.Worker/Properties/GlobalUsings.cs +++ /dev/null @@ -1,20 +0,0 @@ -global using CloudNative.CloudEvents; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Options; -global using Neuroglia; -global using Neuroglia.Data.Expressions; -global using Neuroglia.Serialization; -global using Newtonsoft.Json; -global using Newtonsoft.Json.Linq; -global using ServerlessWorkflow.Sdk; -global using ServerlessWorkflow.Sdk.Models; -global using Synapse.Integration.Events; -global using Synapse.Integration.Events.WorkflowActivities; -global using Synapse.Integration.Models; -global using Synapse.Worker.Configuration; -global using Synapse.Worker.Services; -global using System.Collections; -global using System.Net.Http.Headers; -global using System.Reactive; -global using System.Reactive.Linq; -global using System.Text.RegularExpressions; \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Properties/launchSettings.json b/src/apps/Synapse.Worker/Properties/launchSettings.json deleted file mode 100644 index bbd27b591..000000000 --- a/src/apps/Synapse.Worker/Properties/launchSettings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "profiles": { - "Synapse.Worker": { - "commandName": "Project" - }, - "Docker": { - "commandName": "Docker" - } - } -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Services/FileBasedSecretManager.cs b/src/apps/Synapse.Worker/Services/FileBasedSecretManager.cs deleted file mode 100644 index 70bd92a8f..000000000 --- a/src/apps/Synapse.Worker/Services/FileBasedSecretManager.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Microsoft.Extensions.Hosting; -using System.Runtime.InteropServices; - -namespace Synapse.Worker.Services -{ - - ///

- /// Represents a file-based implementation of the interface - /// - public class FileBasedSecretManager - : BackgroundService, ISecretManager - { - - /// - /// Gets the default directory for file-based secrets - /// - public static string DefaultSecretDirectory - { - get - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "secrets"); - else - return "/run/secrets/synapse"; - } - } - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to provide s - public FileBasedSecretManager(ILogger logger, ISerializerProvider serializerProvider) - { - this.Logger = logger; - this.SerializerProvider = serializerProvider; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to provide s - /// - protected ISerializerProvider SerializerProvider { get; } - - /// - /// Gets an containing the key/value mappings of all loaded secrets - /// - protected Dictionary Secrets { get; } = new(); - - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - var directory = new DirectoryInfo(DefaultSecretDirectory); //todo: replace with options-based values - if (!directory.Exists) - directory.Create(); - foreach (var file in directory.GetFiles()) - { - using var stream = file.OpenRead(); - var mediaTypeName = MimeTypes.GetMimeType(file.Name); - var serializer = this.SerializerProvider.GetSerializerFor(mediaTypeName); - if (serializer == null) - { - this.Logger.LogWarning("Skipped loading secret '{secretFile}': failed to find a serializer for the specified media type '{mediaType}'", file.Name, mediaTypeName); - continue; - } - try - { - var secret = await serializer.DeserializeAsync(stream, stoppingToken); - this.Secrets.Add(file.Name, secret); - } - catch (Exception ex) - { - this.Logger.LogWarning("Skipped loading secret '{secretFile}': an exception occured while deserializing the secret object: {ex}", file.Name, ex.ToString()); - continue; - } - } - } - - /// - public virtual async Task> GetSecretsAsync(CancellationToken cancellationToken) - { - return await Task.FromResult(this.Secrets); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IActionProcessor.cs b/src/apps/Synapse.Worker/Services/IActionProcessor.cs deleted file mode 100644 index c24f750a9..000000000 --- a/src/apps/Synapse.Worker/Services/IActionProcessor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Synapse.Worker.Services -{ - /// - /// Defines the fundamentals of a service used to process activities - /// - public interface IActionProcessor - : IWorkflowActivityProcessor - { - - /// - /// Gets the to process - /// - ActionDefinition Action { get; } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IEndProcessor.cs b/src/apps/Synapse.Worker/Services/IEndProcessor.cs deleted file mode 100644 index e697b009a..000000000 --- a/src/apps/Synapse.Worker/Services/IEndProcessor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Synapse.Worker.Services -{ - /// - /// Defines the fundamentals of a service used to process activities - /// - public interface IEndProcessor - : IWorkflowActivityProcessor - { - - /// - /// Gets the to process - /// - EndDefinition? End { get; } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IIntegrationEventBus.cs b/src/apps/Synapse.Worker/Services/IIntegrationEventBus.cs deleted file mode 100644 index ddf67ffd2..000000000 --- a/src/apps/Synapse.Worker/Services/IIntegrationEventBus.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Reactive.Subjects; - -namespace Synapse.Worker.Services -{ - - ///

- /// Defines the fundamentals of a service used to publish and subscribe to events - /// - public interface IIntegrationEventBus - : IDisposable - { - - /// - /// Gets the outbound stream - /// - ISubject OutboundStream { get; } - - /// - /// Gets the inbound stream - /// - ISubject InboundStream { get; } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IOAuth2TokenManager.cs b/src/apps/Synapse.Worker/Services/IOAuth2TokenManager.cs deleted file mode 100644 index f4d345888..000000000 --- a/src/apps/Synapse.Worker/Services/IOAuth2TokenManager.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services -{ - ///

- /// Defines the fundamentals of a service used to manage s - /// - public interface IOAuth2TokenManager - { - - /// - /// Gets an - /// - /// The properties used to configure the way the to get should be generated - /// A - /// An - Task GetTokenAsync(OAuth2AuthenticationProperties properties, CancellationToken cancellationToken = default); - - } - -} diff --git a/src/apps/Synapse.Worker/Services/ISecretManager.cs b/src/apps/Synapse.Worker/Services/ISecretManager.cs deleted file mode 100644 index ccced3349..000000000 --- a/src/apps/Synapse.Worker/Services/ISecretManager.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services -{ - - ///

- /// Defines the fundamentals of a service used to manage secrets - /// - public interface ISecretManager - { - - /// - /// Gets all available secrets - /// - /// A - /// A new that contains the key/value mappings of all available secrets - Task> GetSecretsAsync(CancellationToken cancellationToken); - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IStateProcessor.cs b/src/apps/Synapse.Worker/Services/IStateProcessor.cs deleted file mode 100644 index 7dc68efe9..000000000 --- a/src/apps/Synapse.Worker/Services/IStateProcessor.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Synapse.Worker.Services -{ - - /// - /// Defines the fundamentals of a service used to process activities - /// - public interface IStateProcessor - : IWorkflowActivityProcessor - { - - /// - /// Gets the to process - /// - StateDefinition State { get; } - - } - - /// - /// Defines the fundamentals of a service used to process activities - /// - public interface IStateProcessor - : IStateProcessor - where TState : StateDefinition - { - - /// - /// Gets the to process - /// - new TState State { get; } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/ITransitionProcessor.cs b/src/apps/Synapse.Worker/Services/ITransitionProcessor.cs deleted file mode 100644 index 60907fd97..000000000 --- a/src/apps/Synapse.Worker/Services/ITransitionProcessor.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Synapse.Worker.Services -{ - /// - /// Defines the fundamentals of a service used to process activities - /// - public interface ITransitionProcessor - : IWorkflowActivityProcessor - { - - /// - /// Gets the to process - /// - TransitionDefinition Transition { get; } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IWorkflowActivityProcessor.cs b/src/apps/Synapse.Worker/Services/IWorkflowActivityProcessor.cs deleted file mode 100644 index 6ba918100..000000000 --- a/src/apps/Synapse.Worker/Services/IWorkflowActivityProcessor.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Synapse.Worker.Services -{ - - /// - /// Defines the fundamentals of a service used to process instances - /// - public interface IWorkflowActivityProcessor - : IObservable, IDisposable - { - - /// - /// Gets the to process - /// - V1WorkflowActivity Activity { get; } - - /// - /// Processes the - /// - /// A - /// A new awaitable - Task ProcessAsync(CancellationToken cancellationToken); - - /// - /// Suspends the processing of the - /// - /// A - /// A new awaitable - Task SuspendAsync(CancellationToken cancellationToken); - - /// - /// Terminates the 's execution - /// - /// A - /// A new awaitable - Task TerminateAsync(CancellationToken cancellationToken); - - } - - /// - /// Defines the fundamentals of a service used to process instances - /// - /// The type of the to process - public interface IWorkflowActivityProcessor - : IWorkflowActivityProcessor - where TActivity : V1WorkflowActivity - { - - /// - /// Gets the to process - /// - new TActivity Activity { get; } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IWorkflowActivityProcessorFactory.cs b/src/apps/Synapse.Worker/Services/IWorkflowActivityProcessorFactory.cs deleted file mode 100644 index 5618c8855..000000000 --- a/src/apps/Synapse.Worker/Services/IWorkflowActivityProcessorFactory.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace Synapse.Worker.Services -{ - - /// - /// Defines the fundamentals of a service used to create s - /// - public interface IWorkflowActivityProcessorFactory - { - - /// - /// Creates a new for the specified - /// - /// The to create a new for - /// A new - IWorkflowActivityProcessor Create(V1WorkflowActivity activity); - - /// - /// Creates a new for the specified - /// - /// The type of the to create a new for - /// The to create a new for - /// A new - IWorkflowActivityProcessor Create(TActivity activity) - where TActivity : V1WorkflowActivity; - - /// - /// Creates a new for the specified - /// - /// The type of the to create - /// The to create a new for - /// A new - TProcessor Create(V1WorkflowActivity activity); - - /// - /// Creates a new for the specified - /// - /// The type of the to create a new for - /// The type of the to create - /// The to create a new for - /// A new - TProcessor Create(TActivity activity) - where TProcessor : IWorkflowActivityProcessor - where TActivity : V1WorkflowActivity; - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IWorkflowFacade.cs b/src/apps/Synapse.Worker/Services/IWorkflowFacade.cs deleted file mode 100644 index ef861e95b..000000000 --- a/src/apps/Synapse.Worker/Services/IWorkflowFacade.cs +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services -{ - - ///

- /// Defines the fundamentals of a runtime specific API facade for Synapse - /// - public interface IWorkflowFacade - { - - /// - /// Gets the current workflow's definition - /// - WorkflowDefinition Definition { get; } - - /// - /// Gets the current workflow instance - /// - V1WorkflowInstance Instance { get; } - - /// - /// Starts the current workflow - /// - /// A - /// A new awaitable - Task StartAsync(CancellationToken cancellationToken = default); - - /// - /// Transitions the current workflow to the specified state - /// - /// The state to transition the current workflow to - /// A - /// A new awaitable - Task TransitionToAsync(StateDefinition state, CancellationToken cancellationToken = default); - - /// - /// Creates a new activity - /// - /// The type of activity to create - /// The activity's input data - /// The activity's metadata - /// The activity's parent, if any - /// A - /// The newly created activity - Task CreateActivityAsync(V1WorkflowActivityType type, object? input, IDictionary? metadata, V1WorkflowActivity? parent, CancellationToken cancellationToken = default); - - /// - /// Attempts to consume a pending - /// - /// The of the to consume - /// A - /// The consumed , if any - Task ConsumeOrBeginCorrelateEventAsync(EventDefinition eventDefinition, CancellationToken cancellationToken = default); - - /// - /// Sets the specified correlation mapping - /// - /// The correlation key to set - /// The value of the correlation key to set - /// A - /// A new awaitable - Task SetCorrelationMappingAsync(string key, string value, CancellationToken cancellationToken = default); - - /// - /// Attempts to correlate the specified - /// - /// The to correlate - /// An containing the context attributes used to correlate the specified - /// A - /// A boolean indicating whether or not the specified could be correlated - Task TryCorrelateAsync(V1Event e, IEnumerable? contextAttributes, CancellationToken cancellationToken = default); - - /// - /// Gets all the activities (including non-operative ones) of the current workflow - /// - /// A - /// A new containing all the activities (including non-operative ones) of the current workflow instance - Task> GetActivitiesAsync(CancellationToken cancellationToken = default); - - /// - /// Gets the current workflow's operative activities - /// - /// A - /// A new containing the current workflow's operative activities - Task> GetOperativeActivitiesAsync(CancellationToken cancellationToken = default); - - /// - /// Gets all the activities (including non-operative ones) of the specified workflow activity - /// - /// The activity to get the children of - /// A - /// A new containing all the children (including non-operative ones) of the specified activity - Task> GetActivitiesAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Gets the operative child activities of the specified activity - /// - /// The activity to get the operative child activities of - /// A - /// A new containing the current specified activity's operative children - Task> GetOperativeActivitiesAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Initializes the specified activity - /// - /// The activity to initialize - /// A - /// A new awaitable - Task InitializeActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Starts or resumes the specified activity - /// - /// The activity to starts or resumes - /// A - /// A new awaitable - Task StartOrResumeActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Suspends the execution of the specified activity - /// - /// The activity to suspend the execution of - /// A - /// A new awaitable - Task SuspendActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Skips the execution of the specified activity - /// - /// The activity to skip the execution of - /// A - /// A new awaitable - Task SkipActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Updates the specified 's metadata - /// - /// The to update the metadata of - /// A - /// A new awaitable - Task SetActivityMetadataAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Cancels the execution of the specified activity - /// - /// The activity to cancel the execution of - /// A - /// A new awaitable - Task CancelActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Faults the execution of the specified activity - /// - /// The activity to fault - /// The that cause the activity to fault - /// A - /// A new awaitable - Task FaultActivityAsync(V1WorkflowActivity activity, Exception ex, CancellationToken cancellationToken = default); - - /// - /// Faults the execution of the specified activity - /// - /// The activity to fault - /// The error that cause the activity to fault - /// A - /// A new awaitable - Task FaultActivityAsync(V1WorkflowActivity activity, Integration.Models.Error error, CancellationToken cancellationToken = default); - - /// - /// Compensates the specified - /// - /// The to compensate - /// A - /// A new awaitable - Task CompensateActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Marks the specified as compensated - /// - /// The id of the to mark as compensated - /// A - /// A new awaitable - Task MarkActivityAsCompensatedAsync(string activityId, CancellationToken cancellationToken = default); - - /// - /// Gets the data of the the specified belongs to - /// - /// The to get the data for - /// A - /// The data of the the specified belongs to - Task GetActivityStateDataAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default); - - /// - /// Starts a subflow - /// - /// The id of the workflow to start - /// The data to input to the workflow to start - /// A - /// The id of the subflow's newly created workflow instance - Task StartSubflowAsync(string workflowId, object? input, CancellationToken cancellationToken = default); - - /// - /// Marks the as suspended - /// - /// A - /// A new awaitable - Task SuspendAsync(CancellationToken cancellationToken = default); - - /// - /// Marks the as cancelled - /// - /// A - /// A new awaitable - Task CancelAsync(CancellationToken cancellationToken = default); - - /// - /// Faults the execution of the current workflow instance - /// - /// The that cause the current workflow to fault - /// A - /// A new awaitable - Task FaultAsync(Exception ex, CancellationToken cancellationToken = default); - - /// - /// Sets the output of the current workflow instance - /// - /// The current workflow instance's output - /// A - /// A new awaitable - Task SetOutputAsync(object? output, CancellationToken cancellationToken = default); - - /// - /// Handles the specified activity event - /// - /// The activity that has produced the event - /// The event to handle - /// A - /// A new awaitable - Task On(V1WorkflowActivity activity, IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken = default); - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IWorkflowRuntime.cs b/src/apps/Synapse.Worker/Services/IWorkflowRuntime.cs deleted file mode 100644 index 2b59d921c..000000000 --- a/src/apps/Synapse.Worker/Services/IWorkflowRuntime.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace Synapse.Worker.Services -{ - - /// - /// Defines the fundamentals of a service used to run a - /// - public interface IWorkflowRuntime - : IHostedService, IDisposable - { - - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IWorkflowRuntimeContext.cs b/src/apps/Synapse.Worker/Services/IWorkflowRuntimeContext.cs deleted file mode 100644 index 1b3254752..000000000 --- a/src/apps/Synapse.Worker/Services/IWorkflowRuntimeContext.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services -{ - - ///

- /// Defines the fundamentals of a service used to represent a workflow's runtime context - /// - public interface IWorkflowRuntimeContext - { - - /// - /// Gets the service used a Synapse API helper facade - /// - IWorkflowFacade Workflow { get; } - - /// - /// Gets an containing temporary runtime data that will not be persisted - /// - IDictionary Data { get; } - - /// - /// Initializes the - /// - /// A - /// A new awaitable - Task InitializeAsync(CancellationToken cancellationToken); - - /// - /// Evaluates a runtime expression against an object - /// - /// The runtime expression to evaluate - /// The data to evaluate the expression against - /// The current , if any - /// A - /// The evaluation result - Task EvaluateAsync(string runtimeExpression, object? data, AuthorizationInfo? authorization, CancellationToken cancellationToken = default); - - /// - /// Evaluates a runtime expression against an object - /// - /// The runtime expression to evaluate - /// The data to evaluate the expression against - /// A - /// The evaluation result - Task EvaluateAsync(string runtimeExpression, object? data, CancellationToken cancellationToken = default); - - /// - /// Reads the secret with the specified name - /// - /// The type of the secret to get - /// The name of the secret to get - /// A - /// The secret with the specified name - Task GetSecretAsync(string secret, CancellationToken cancellationToken = default); - - /// - /// Publishes the specified - /// - /// The to publish - /// A - /// A new awaitable - Task PublishEventAsync(V1Event e, CancellationToken cancellationToken = default); - - } - -} diff --git a/src/apps/Synapse.Worker/Services/IntegrationEventBus.cs b/src/apps/Synapse.Worker/Services/IntegrationEventBus.cs deleted file mode 100644 index 56544d9cc..000000000 --- a/src/apps/Synapse.Worker/Services/IntegrationEventBus.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Reactive.Subjects; - -namespace Synapse.Worker.Services -{ - ///

- /// Represents the default implementation of the interface - /// - public class IntegrationEventBus - : IIntegrationEventBus - { - - private bool _Disposed; - - /// - /// Gets the used to stream produced s - /// - public Subject OutboundStream { get; } = new(); - - ISubject IIntegrationEventBus.OutboundStream => this.OutboundStream; - - /// - /// Gets the used to stream consumed s - /// - public Subject InboundStream { get; } = new(); - - ISubject IIntegrationEventBus.InboundStream => this.InboundStream; - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.OutboundStream.Dispose(); - this.InboundStream.Dispose(); - } - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/OAuth2TokenManager.cs b/src/apps/Synapse.Worker/Services/OAuth2TokenManager.cs deleted file mode 100644 index bdc560489..000000000 --- a/src/apps/Synapse.Worker/Services/OAuth2TokenManager.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using GraphQL.Client.Abstractions.Utilities; -using IdentityModel.Client; - -namespace Synapse.Worker.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class OAuth2TokenManager - : IOAuth2TokenManager - { - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to serialize/deserialize to/from JSON - /// The service used to perform HTTP requests - public OAuth2TokenManager(ILogger logger, IJsonSerializer jsonSerializer, HttpClient httpClient) - { - this.Logger = logger; - this.JsonSerializer = jsonSerializer; - this.HttpClient = httpClient; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - /// Gets the service used to perform HTTP requests - /// - protected HttpClient HttpClient { get; } - - /// - /// Gets a containing all active s - /// - protected Dictionary Tokens { get; } = new(); - - /// - public virtual async Task GetTokenAsync(OAuth2AuthenticationProperties oauthProperties, CancellationToken cancellationToken = default) - { - if (oauthProperties == null) - throw new ArgumentNullException(nameof(oauthProperties)); - var tokenKey = $"{oauthProperties.ClientId}@{oauthProperties.Authority}"; - var json = await this.JsonSerializer.SerializeAsync(oauthProperties, cancellationToken); - var properties = await this.JsonSerializer.DeserializeAsync>(json, cancellationToken); - properties = properties.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value)).ToDictionary(kvp => kvp.Key.ToSnakeCase(), kvp => kvp.Value); - properties.Remove("authority"); - if(this.Tokens.TryGetValue(tokenKey, out var token) - && token != null) - { - if (token.HasExpired - && !string.IsNullOrWhiteSpace(token.RefreshToken)) - { - properties["grant_type"] = "refresh_token"; - properties["refresh_token"] = token.RefreshToken; - } - else - { - return token; - } - } - var discoveryDocument = await this.HttpClient.GetDiscoveryDocumentAsync(oauthProperties.Authority.ToString(), cancellationToken); - using var request = new HttpRequestMessage(HttpMethod.Post, new Uri(oauthProperties.Authority, discoveryDocument.TokenEndpoint)) - { - Content = new FormUrlEncodedContent(properties) - }; - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - json = await response.Content?.ReadAsStringAsync(cancellationToken)!; - if (!response.IsSuccessStatusCode) - { - this.Logger.LogError("An error occured while generating a new JWT token: {details}", json); - response.EnsureSuccessStatusCode(json); - } - token = await this.JsonSerializer.DeserializeAsync(json, cancellationToken); - this.Tokens[tokenKey] = token; - return token; - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/ActionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/ActionProcessor.cs deleted file mode 100644 index 4004990cb..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/ActionProcessor.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process s - /// - public abstract class ActionProcessor - : WorkflowActivityProcessor, IActionProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - /// The to process - protected ActionProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, ActionDefinition action) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.Action = action; - } - - /// - /// Gets the to process - /// - public ActionDefinition Action { get; } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - if (this.Action.Sleep != null - && this.Action.Sleep.Before.HasValue) - await Task.Delay(this.Action.Sleep.Before.Value, cancellationToken); - if (string.IsNullOrWhiteSpace(this.Action.Condition) - || await this.Context.EvaluateConditionAsync(this.Action.Condition, this.Activity.Input.ToObject()!, cancellationToken)) - return; - this.Logger.LogInformation("Skipping execution of workflow activity with id '{activityId}' because the expression of the processed action with name '{actionName}' evaluated to false.", this.Activity.Id, this.Action.Name); - this.Activity.Status = V1WorkflowActivityStatus.Skipped; - await this.OnNextAsync(new V1WorkflowActivitySkippedIntegrationEvent(this.Activity.Id), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - /// - protected override async Task OnNextAsync(IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken) - { - if (e is V1WorkflowActivityCompletedIntegrationEvent - && this.Action.Sleep != null - && this.Action.Sleep.After.HasValue) - await Task.Delay(this.Action.Sleep.After.Value, cancellationToken); - await base.OnNextAsync(e, cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/AsyncApiFunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/AsyncApiFunctionProcessor.cs deleted file mode 100644 index fa31e1b7b..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/AsyncApiFunctionProcessor.cs +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.AsyncApi; -using Neuroglia.AsyncApi.Client; -using Neuroglia.AsyncApi.Client.Services; -using Neuroglia.AsyncApi.Models; -using Neuroglia.AsyncApi.Services.IO; -using System.Dynamic; - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process Async API s - /// - public class AsyncApiFunctionProcessor - : FunctionProcessor - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to created s - /// The service used to read s - /// The service used to create s - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public AsyncApiFunctionProcessor(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IHttpClientFactory httpClientFactory, IAsyncApiDocumentReader asyncApiReader, IAsyncApiClientFactory asyncApiClientFactory, IOptions options, V1WorkflowActivity activity, - ActionDefinition action, FunctionDefinition function) - : base(serviceProvider, loggerFactory, context, activityProcessorFactory, options, activity, action, function) - { - this.HttpClient = httpClientFactory.CreateClient(); - this.AsyncApiReader = asyncApiReader; - this.AsyncApiClientFactory = asyncApiClientFactory; - } - - /// - /// Gets the used by the to execute the function - /// - protected HttpClient HttpClient { get; } - - /// - /// Gets the service used to read s - /// - protected IAsyncApiDocumentReader AsyncApiReader { get; } - - /// - /// Gets the service used to create s - /// - protected IAsyncApiClientFactory AsyncApiClientFactory { get; } - - /// - /// Gets the that defines the operation to invoke - /// - protected AsyncApiDocument Document { get; set; } = null!; - - /// - /// Gets the key of the Async API channel the operation to execute belongs to - /// - protected string ChannelKey { get; set; } = null!; - - /// - /// Gets the Async API channel the operation to execute belongs to - /// - protected ChannelDefinition Channel { get; set; } = null!; - - /// - /// Gets the Async API operation to execute - /// - protected OperationDefinition Operation { get; set; } = null!; - - /// - /// Gets the type of the Async API operation to execute - /// - protected OperationType OperationType { get; set; } - - /// - /// Gets the payload to use when invoking the remote Async API operation to process - /// - protected object? Payload { get; set; } - - /// - /// Gets the service used to interact with the remote Async API - /// - protected IAsyncApiClient AsyncApiClient { get; set; } = null!; - - /// - /// Gets an that represents the 's subscription to messages publishes by the Async API operation to process - /// - protected IDisposable? Subscription { get; set; } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - await base.InitializeAsync(cancellationToken); - var components = this.Function.Operation.Split('#', StringSplitOptions.RemoveEmptyEntries); - if (components.Length != 2) - throw new FormatException($"The 'operation' property of the Async API function with name '{this.Function.Name}' has an invalid value '{this.Function.Operation}'. Async API functions expect a value in the following format: #"); - var asyncApiUri = components[0]; - var operationId = components[1]; - try - { - using (HttpRequestMessage request = new(System.Net.Http.HttpMethod.Get, asyncApiUri)) - { - using HttpResponseMessage response = await this.HttpClient.SendAsync(request, cancellationToken); - if (!response.IsSuccessStatusCode) - { - var responseContent = await response.Content.ReadAsStringAsync(cancellationToken); - this.Logger.LogInformation("Failed to retrieve the Async API document at location '{uri}'. The remote server responded with a non-success status code '{statusCode}'.", asyncApiUri, response.StatusCode); - this.Logger.LogDebug("Response content:/r/n{responseContent}", response.Content == null ? "None" : responseContent); - response.EnsureSuccessStatusCode(responseContent); - } - using Stream responseStream = await response.Content!.ReadAsStreamAsync(cancellationToken)!; - this.Document = await this.AsyncApiReader.ReadAsync(responseStream, cancellationToken); - } - var channel = this.Document.Channels - .FirstOrDefault(c => c.Value.DefinesOperationWithId(operationId)); - this.ChannelKey = channel.Key; - this.Channel = channel.Value; - if (this.Channel == null) - throw new NullReferenceException($"Failed to find an operation with id '{this.Operation}' in the specified AsyncAPI document"); - this.Operation = this.Channel.GetOperationById(operationId); - this.OperationType = this.Channel.Publish == this.Operation ? OperationType.Publish : OperationType.Subscribe; - await this.BuildPayloadAsync(cancellationToken); - this.AsyncApiClient = this.AsyncApiClientFactory.CreateClient(this.Document); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - - } - - /// - /// Builds the payload to use when invoking the remote Async API operation to process - /// - /// A - protected virtual async Task BuildPayloadAsync(CancellationToken cancellationToken) - { - if (this.Activity.Input == null) - this.Payload = new Dictionary(); - if (this.FunctionReference.Arguments == null) - return; - var json = JsonConvert.SerializeObject(this.FunctionReference.Arguments, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); - foreach (Match match in Regex.Matches(json, @"""\$\{.+?\}""")) - { - var expression = match.Value[3..^2].Trim(); - var evaluationResult = await this.Context.EvaluateAsync(expression, this.Activity.Input!.ToObject()!, cancellationToken); - if (evaluationResult == null) - { - Console.WriteLine($"Evaluation result of expression {expression} on data {JsonConvert.SerializeObject(this.Activity.Input)} is NULL"); //todo: replace with better message - continue; - } - var valueToken = JToken.FromObject(evaluationResult); - var value = null as string; - if (valueToken != null) - { - value = valueToken.Type switch - { - JTokenType.String => @$"""{valueToken}""", - _ => valueToken.ToString(), - }; - } - if (string.IsNullOrEmpty(value)) - value = "null"; - json = json.Replace(match.Value, value); - } - this.Payload = JsonConvert.DeserializeObject(json)!; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - await base.ProcessAsync(cancellationToken); - try - { - switch (this.OperationType) - { - case OperationType.Publish: - var message = new MessageBuilder() - .WithPayload(this.Payload) - .Build(); - await this.AsyncApiClient.PublishAsync(this.ChannelKey, message, cancellationToken); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, new()), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - break; - case OperationType.Subscribe: - this.Subscription = await this.AsyncApiClient.SubscribeToAsync(this.ChannelKey, Observer.Create(this.OnMessageAsync), cancellationToken); - break; - default: - throw new NotSupportedException($"The specified {nameof(OperationType)} '{this.OperationType}' is not supported"); - } - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// A new awaitable - protected virtual async void OnMessageAsync(IMessage message) - { - try - { - if (message == null) - throw new ArgumentNullException(nameof(message)); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, message.Payload), this.CancellationTokenSource.Token); - await this.OnCompletedAsync(this.CancellationTokenSource.Token); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, this.CancellationTokenSource.Token); - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - this.HttpClient.Dispose(); - this.Subscription?.Dispose(); - } - base.Dispose(disposing); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/AsyncFunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/AsyncFunctionProcessor.cs deleted file mode 100644 index d3bb9750d..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/AsyncFunctionProcessor.cs +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - ///

- /// Represents an implementation used to process - /// - public class AsyncFunctionProcessor - : ActionProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - /// The the to process belongs to - /// The to process - /// The that defines the to produce, thus triggering the async call - /// The that defines the to consume, thus ending the async call - public AsyncFunctionProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, StateDefinition state, ActionDefinition action, EventDefinition triggerEvent, EventDefinition resultEvent) - : base(loggerFactory, context, activityProcessorFactory, options, activity, action) - { - this.State = state; - this.TriggerEvent = triggerEvent; - this.ResultEvent = resultEvent; - } - - /// - /// Gets the the to process belongs to - /// - protected StateDefinition State { get; } - - /// - /// Gets the that defines the to produce, thus triggering the async call - /// - protected EventDefinition TriggerEvent { get; } - - /// - /// Gets the that defines the to consume, thus ending the async call - /// - protected EventDefinition ResultEvent { get; } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var processor = base.CreateProcessorFor(activity); - var cancellationToken = this.CancellationTokenSource.Token; - switch (processor) - { - case ProduceEventProcessor produceEventProcessor: - processor.OfType() - .SubscribeAsync - ( - async result => await this.OnProduceEventResultAsync(result, cancellationToken), - async ex => await this.OnErrorAsync(ex, cancellationToken), - async () => await this.OnProduceEventCompletedAsync(processor, cancellationToken) - ); - break; - case ConsumeEventProcessor consumeEventProcessor: - processor.SubscribeAsync - ( - async result => await this.OnConsumeEventResultAsync(result, cancellationToken), - async ex => await this.OnErrorAsync(ex, cancellationToken), - async () => await this.OnConsumeEventCompletedAsync(processor, cancellationToken) - ); - break; - default: - processor.Dispose(); - throw new NotSupportedException($"The specified {nameof(IWorkflowActivityProcessor)} '{processor.GetType()}' is not supported"); - } - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - { - var input = await this.Context.FilterInputAsync(this.Action, this.Activity.Input.ToObject()!, cancellationToken); - if (!string.IsNullOrWhiteSpace(this.Action.Event!.DataExpression)) input = await this.Context.EvaluateAsync(this.Action.Event!.DataExpression, input, cancellationToken); - else if (this.Action.Event.Data != null) input = await this.Context.EvaluateObjectAsync(this.Action.Event!.Data.ToObject()!, input!, cancellationToken); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name }, - { V1WorkflowActivityMetadata.Action, this.Action.Name! }, - { V1WorkflowActivityMetadata.Event, this.Action.Event!.ProduceEvent } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.ProduceEvent, input, metadata, this.Activity, cancellationToken); - } - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (IWorkflowActivityProcessor processor in this.Processors.ToList()) - { - await processor.ProcessAsync(cancellationToken); - } - } - - /// - /// Handles the next of a - /// - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnProduceEventResultAsync(V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) - { - var input = await this.Context.FilterInputAsync(this.Action, this.Activity.Input.ToObject()!, cancellationToken); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, this.Action.Name! }, - { V1WorkflowActivityMetadata.Event, this.Action.Event!.ResultEvent } - }; - var activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.ConsumeEvent, input, metadata, this.Activity, cancellationToken); -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - - /// - /// Handles the completion of a - /// - /// The that has run to completion - /// A - /// A new awaitable - protected virtual async Task OnProduceEventCompletedAsync(IWorkflowActivityProcessor processor, CancellationToken cancellationToken) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - foreach (var childProcessor in this.Processors) - { - await childProcessor.ProcessAsync(cancellationToken); - } - } - - /// - /// Handles the next of a - /// - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnConsumeEventResultAsync(IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken) - { - if (e is V1WorkflowActivityCompletedIntegrationEvent completedEvent) - { - var output = await this.Context.FilterOutputAsync(this.Action, completedEvent.Output, cancellationToken); - if (output == null) - output = new(); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - } - } - - /// - /// Handles the completion of a - /// - /// The that has run to completion - /// A - /// A new awaitable - protected virtual async Task OnConsumeEventCompletedAsync(IWorkflowActivityProcessor processor, CancellationToken cancellationToken) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - await this.OnCompletedAsync(cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/BranchProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/BranchProcessor.cs deleted file mode 100644 index 3f450fea2..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/BranchProcessor.cs +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process 's branches - /// - public class BranchProcessor - : WorkflowActivityProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to serialize/deserialize to/from JSON - /// The service used to access the current - /// The to process - /// The that defines the to process - /// The to process - public BranchProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IJsonSerializer jsonSerializer, IOptions options, V1WorkflowActivity activity, ParallelStateDefinition state, BranchDefinition branch) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.JsonSerializer = jsonSerializer; - this.State = state; - this.Branch = branch; - } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - /// Gets the that defines the to process - /// - public ParallelStateDefinition State { get; } - - /// - /// Gets the to process - /// - public BranchDefinition Branch { get; } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var processor = (ActionProcessor)base.CreateProcessorFor(activity); - processor.SubscribeAsync - ( - async e => await this.OnNextActionAsync(processor, e), - async ex => await this.OnActionFaultedAsync(processor, ex), - async () => await this.OnActionCompletedAsync(processor) - ); - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - { - var input = null as object; - var metadata = null as Dictionary; - switch (this.Branch.ActionMode) - { - case ActionExecutionMode.Parallel: - foreach (var action in this.Branch.Actions) - { - input = await this.Context.FilterInputAsync(action, this.Activity.Input, cancellationToken); - metadata = new() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, action.Name! }, - { V1WorkflowActivityMetadata.Branch, this.Branch.Name } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, cancellationToken); - } - break; - case ActionExecutionMode.Sequential: - var firstAction = this.Branch.Actions.First(); - input = await this.Context.FilterInputAsync(firstAction, this.Activity.Input, cancellationToken); - metadata = new() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, firstAction.Name! }, - { V1WorkflowActivityMetadata.Branch, this.Branch.Name } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, cancellationToken); - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.Branch.ActionMode}' is not supported"); - } - } - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { - this.CreateProcessorFor(activity); - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (var processor in this.Processors.ToList()) - { - await processor.ProcessAsync(cancellationToken); - } - } - - /// - /// Aggregates the output of the completed actions activities that belongs to the processed - /// - /// A - /// The result of the action outputs aggregation - protected virtual async Task AggregateActionOutputsAsync(CancellationToken cancellationToken) - { - var output = this.Activity.Input.ToObject(); - foreach (var activity in (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Action && a.Status == V1WorkflowActivityStatus.Completed) - .OrderBy(a => a.ExecutedAt)) - { - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Action, out var actionName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Action}'"); - if (!this.Branch.TryGetAction(actionName, out var action)) - throw new NullReferenceException($"Failed to find an action with name '{actionName}' in the operation state with name '{this.State.Name}'"); - if (action.UseResults()) - { - var expression = action.ActionDataFilter?.ToStateData?.Trim(); - var json = await this.JsonSerializer.SerializeAsync(activity.Output, cancellationToken); - if (string.IsNullOrWhiteSpace(expression)) - { - expression = json; - } - else - { - if (expression.StartsWith("${")) - expression = expression[2..^1]; - expression = $"{expression} = {json}"; - } - output = await this.Context.EvaluateAsync(expression, output, cancellationToken); - } - } - return output!; - } - - /// - /// Handles s - /// - /// The to handle the for - /// The to handle - /// A new awaitable - protected virtual async Task OnNextActionAsync(IActionProcessor processor, IV1WorkflowActivityIntegrationEvent e) - { - switch (e) - { - case V1WorkflowActivitySkippedIntegrationEvent: - case V1WorkflowActivityCompletedIntegrationEvent: - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - var output = null as object; - switch (this.Branch.ActionMode) - { - case ActionExecutionMode.Parallel: - var activities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, this.CancellationTokenSource.Token)) - .Where(a => a.Type == V1WorkflowActivityType.Action) - .ToList(); - if (activities.All(a => a.Status >= V1WorkflowActivityStatus.Faulted)) - { - output = await this.AggregateActionOutputsAsync(this.CancellationTokenSource.Token); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), this.CancellationTokenSource.Token); - } - break; - case ActionExecutionMode.Sequential: - output = await this.AggregateActionOutputsAsync(this.CancellationTokenSource.Token); - if (processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Action, out var currentActionName) - && this.Branch.TryGetNextAction(currentActionName, out ActionDefinition nextAction)) - { - var input = await this.Context.FilterInputAsync(nextAction, output, this.CancellationTokenSource.Token); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, nextAction.Name! }, - { V1WorkflowActivityMetadata.Branch, this.Branch.Name } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, this.CancellationTokenSource.Token); - } - else - { - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), this.CancellationTokenSource.Token); - } - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.Branch.ActionMode}' is not supported"); - } - } - break; - } - } - - /// - /// Handles an 's - /// - /// The that has thrown the specified - /// The thrown - /// A new awaitable - protected virtual async Task OnActionFaultedAsync(ActionProcessor processor, Exception ex) - { - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - foreach (var childProcessor in this.Processors) - { - if (childProcessor == processor) - continue; - await childProcessor.TerminateAsync(this.CancellationTokenSource.Token); - this.Processors.TryRemove(processor); - childProcessor.Dispose(); - } - await base.OnErrorAsync(ex, this.CancellationTokenSource.Token); - } - } - - /// - /// Handles the completion of the specified - /// - /// The that has completed - /// A new awaitable - protected virtual async Task OnActionCompletedAsync(ActionProcessor processor) - { - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - var activities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, this.CancellationTokenSource.Token)) - .Where(a => a.Type == V1WorkflowActivityType.Action); - if (activities.All(a => a.Status >= V1WorkflowActivityStatus.Faulted)) - { - await base.OnCompletedAsync(this.CancellationTokenSource.Token); - return; - } - foreach (var activity in activities - .Where(p => p.Status == V1WorkflowActivityStatus.Pending)) - { - var nextProcessor = this.CreateProcessorFor(activity); - await nextProcessor.ProcessAsync(this.CancellationTokenSource.Token); - } - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/ConsumeEventProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/ConsumeEventProcessor.cs deleted file mode 100644 index 3630a2120..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/ConsumeEventProcessor.cs +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process s to consume - /// - public class ConsumeEventProcessor - : WorkflowActivityProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to publish and subscribe to integration events - /// The service used to access the current - /// The to process - /// The that defines the to consume - public ConsumeEventProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IIntegrationEventBus integrationEventBus, IOptions options, V1WorkflowActivity activity, EventDefinition eventDefinition) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.IntegrationEventBus = integrationEventBus; - this.EventDefinition = eventDefinition; - } - - /// - /// Gets the service used to publish and subscribe to integration events - /// - protected IIntegrationEventBus IntegrationEventBus { get; } - - /// - /// Gets the subscription - /// - protected IDisposable Subscription { get; private set; } = null!; - - /// - /// Gets the that defines the to consume - /// - public EventDefinition EventDefinition { get; } - - /// - /// Gets the used to monitor the time limit before which the current should go into - /// - protected Timer IdleTimer { get; private set; } = null!; - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.EventDefinition.Correlations != null) - { - foreach (var correlation in this.EventDefinition.Correlations) - { - var value = correlation.ContextAttributeValue; - if (!string.IsNullOrWhiteSpace(value) - && value.IsRuntimeExpression()) - value = (await this.Context.EvaluateAsync(value, this.Activity.Input.ToObject()!, cancellationToken))!.ToString(); - if (!string.IsNullOrWhiteSpace(value)) - await this.Context.Workflow.SetCorrelationMappingAsync(correlation.ContextAttributeName, value!, cancellationToken); - } - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - this.Subscription = this.IntegrationEventBus.InboundStream.SubscribeAsync(this.OnEventAsync); - var e = await this.Context.Workflow.ConsumeOrBeginCorrelateEventAsync(this.EventDefinition, cancellationToken); - if (e != null) - { - await this.OnEventAsync(e); - return; - } - if (this.Options.Correlation.Timeout.HasValue) - this.IdleTimer = new Timer(async state => await this.OnIdleTimerExpiredAsync(state, cancellationToken), null, this.Options.Correlation.Timeout.Value, this.Options.Correlation.Timeout.Value); - } - - /// - /// Switches to passive correlation mode - /// - /// A - /// A new awaitable - protected virtual async Task SwitchToPassiveModeAsync(CancellationToken cancellationToken) - { - using (await this.Lock.LockAsync(cancellationToken)) - { - //todo: passive correlation mode: await this.Context.WaitForEventsAsync(new[] { this.EventDefinition }, V1TriggerCorrelationMode.Exclusive, V1TriggerConditionType.AllOf, cancellationToken); - } - } - - /// - /// Handles the expiration of the iddle - /// - /// The 's state - /// A - /// A new awaitable - protected virtual async Task OnIdleTimerExpiredAsync(object? state, CancellationToken cancellationToken) - { - this.IdleTimer.Dispose(); - this.IdleTimer = null!; - if (this.Options.Correlation.Mode == RuntimeCorrelationMode.Dual) - { - await this.SwitchToPassiveModeAsync(cancellationToken); - } - else - { - //TODO - //await this.OnTimeoutAsync(); - } - } - - /// - /// Handles an incoming - /// - /// The to handle - /// A new awaitable - protected virtual async Task OnEventAsync(CloudEvent e) - { - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - if (this.IdleTimer != null) - { - this.IdleTimer.Dispose(); - this.IdleTimer = null!; - } - if ((!string.IsNullOrWhiteSpace(this.EventDefinition.Source) && !Regex.IsMatch(e.Source!.ToString(), this.EventDefinition.Source, RegexOptions.IgnoreCase)) - || (!string.IsNullOrWhiteSpace(this.EventDefinition.Type) && !Regex.IsMatch(e.Type!, this.EventDefinition.Type, RegexOptions.IgnoreCase))) - return; - var enveloppe = V1Event.CreateFrom(e); - if (!await this.Context.Workflow.TryCorrelateAsync(enveloppe, this.EventDefinition.Correlations?.Select(c => c.ContextAttributeName)!, this.CancellationTokenSource.Token)) - return; - object output; - if (this.EventDefinition.DataOnly) - output = e.Data!; - else - output = enveloppe; - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), this.CancellationTokenSource.Token); - await this.OnCompletedAsync(this.CancellationTokenSource.Token); - } - } - - /// - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - this.IdleTimer?.Dispose(); - this.Subscription?.Dispose(); - this.Subscription?.Dispose(); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/EndProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/EndProcessor.cs deleted file mode 100644 index b9c17376c..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/EndProcessor.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents an used to process s - /// - public class EndProcessor - : WorkflowActivityProcessor, IEndProcessor - { - - /// - public EndProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, EndDefinition end) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.End = end; - } - - /// - /// Gets the to process - /// - public EndDefinition? End { get; } - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - //TODO - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - //TODO - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/EventStateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/EventStateProcessor.cs deleted file mode 100644 index 20a9b0b9a..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/EventStateProcessor.cs +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process s - /// - public class EventStateProcessor - : StateProcessor - { - - /// - public EventStateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, EventStateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - - } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var processor = (EventStateTriggerProcessor)base.CreateProcessorFor(activity); - processor.OfType().SubscribeAsync - ( - async result => await this.OnTriggerResultAsync(processor, result, this.CancellationTokenSource.Token), - async ex => await this.OnErrorAsync(ex, this.CancellationTokenSource.Token), - async () => await this.OnTriggerCompletedAsync(processor, this.CancellationTokenSource.Token) - ); - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status <= V1WorkflowActivityStatus.Pending) - { - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name } - }; - foreach (var trigger in this.State.Triggers) - { - metadata[V1WorkflowActivityMetadata.Trigger] = this.State.Triggers.IndexOf(trigger).ToString(); - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.EventTrigger, this.Activity.Input, metadata, this.Activity, cancellationToken); - } - } - foreach (var childActivity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(childActivity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (IWorkflowActivityProcessor childProcessor in this.Processors.ToList()) - { - await childProcessor.ProcessAsync(cancellationToken); - } - } - - /// - /// Handles the next 's - /// - /// The that returned the - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnTriggerResultAsync(IWorkflowActivityProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) - { - using (await this.Lock.LockAsync(cancellationToken)) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Completed) - return; - var completedActivities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(p => p.Type == V1WorkflowActivityType.EventTrigger && p.Status == V1WorkflowActivityStatus.Completed) - .ToList(); - if (this.State.Exclusive - || completedActivities.Count == this.State.Triggers.Count) - { - var output = new object(); - foreach (var activity in completedActivities - .Where(p => p.Output != null)) - { - output = output.Merge(activity.Output.ToObject()); - } - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - } - } - - /// - /// Handles the completion of the specified - /// - /// The to handle the completion of - /// A - /// A new awaitable - protected virtual Task OnTriggerCompletedAsync(IWorkflowActivityProcessor processor, CancellationToken cancellationToken) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - return Task.CompletedTask; - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/EventStateTriggerProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/EventStateTriggerProcessor.cs deleted file mode 100644 index b4d0f6dfa..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/EventStateTriggerProcessor.cs +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process s - /// - public class EventStateTriggerProcessor - : WorkflowActivityProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to serialize/deserialize to/from JSON - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public EventStateTriggerProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, IJsonSerializer jsonSerializer, - IOptions options, V1WorkflowActivity activity, EventStateDefinition state, EventStateTriggerDefinition trigger) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.JsonSerializer = jsonSerializer; - this.State = state; - this.Trigger = trigger; - } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - /// Gets the to process - /// - public EventStateDefinition State { get; } - - /// - /// Gets the to process - /// - public EventStateTriggerDefinition Trigger { get; } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - IWorkflowActivityProcessor processor = base.CreateProcessorFor(activity); - CancellationToken cancellationToken = this.CancellationTokenSource.Token; - switch (processor) - { - case ConsumeEventProcessor consumeEventProcessor: - processor.OfType().SubscribeAsync - ( - async result => await this.OnEventResultAsync(consumeEventProcessor, result, cancellationToken), - async ex => await this.OnErrorAsync(ex, cancellationToken), - async () => await this.OnEventCompletedAsync(consumeEventProcessor, cancellationToken) - ); - break; - case ActionProcessor actionProcessor: - processor.OfType().SubscribeAsync - ( - async result => await this.OnActionResultAsync(actionProcessor, result, cancellationToken), - async ex => await this.OnErrorAsync(ex, cancellationToken), - async () => await this.OnActionCompletedAsync(actionProcessor, cancellationToken) - ); - break; - default: - throw new NotSupportedException($"The specified execution pointer type '{processor.GetType().Name}' is not supported in this context"); - } - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status <= V1WorkflowActivityStatus.Pending) - { - foreach (var eventReference in this.Trigger.Events) - { - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Event, eventReference } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.ConsumeEvent, this.Activity.Input, metadata, this.Activity, cancellationToken); - } - } - foreach (V1WorkflowActivity childActivity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(childActivity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (IWorkflowActivityProcessor childProcessor in this.Processors) - { - await childProcessor.ProcessAsync(cancellationToken); - } - } - - private bool _Triggered; - /// - /// Handles the next 's - /// - /// The that returned the - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnEventResultAsync(ConsumeEventProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) - { - using var asyncLock = await this.Lock.LockAsync(cancellationToken); - var consumeEventActivities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.ConsumeEvent) - .ToList(); - if (this.State.Exclusive) - { - foreach (IWorkflowActivityProcessor childProcessor in this.Processors - .Where(p => p.Activity.Type == V1WorkflowActivityType.ConsumeEvent) - .ToList()) - { - if (childProcessor == processor) - continue; - await childProcessor.TerminateAsync(cancellationToken); - this.Processors.TryRemove(childProcessor); - childProcessor.Dispose(); - } - } - if (this.State.Exclusive - || (consumeEventActivities.All(p => p.Status == V1WorkflowActivityStatus.Completed) && consumeEventActivities.Count == this.Trigger.Events.Count && !this._Triggered)) - { - this._Triggered = true; - var output = this.Activity.Input.ToObject(); - if (this.Trigger.DataFilter != null - && this.Trigger.DataFilter.UseData) - { - foreach (var consumeEventActivity in consumeEventActivities - .Where(p => p.Status == V1WorkflowActivityStatus.Completed && p.Output != null)) - { - var eventOutput = consumeEventActivity.Output.ToObject(); - if (!string.IsNullOrWhiteSpace(this.Trigger.DataFilter.Data)) - eventOutput = await this.Context.EvaluateAsync(this.Trigger.DataFilter.Data, eventOutput, cancellationToken); - if (string.IsNullOrWhiteSpace(this.Trigger.DataFilter.ToStateData)) - { - output = output.Merge(eventOutput); - } - else - { - var expression = this.Trigger.DataFilter.ToStateData.Trim(); - var json = await this.JsonSerializer.SerializeAsync(eventOutput, cancellationToken); - if (expression.StartsWith("${")) - expression = expression[2..^1]; - expression = $"{expression} = {json}"; - output = await this.Context.EvaluateAsync(expression, output, cancellationToken); - } - } - } - if (this.Trigger.Actions == null - || !this.Trigger.Actions.Any()) - { - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - return; - } - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Trigger, this.State.Triggers.IndexOf(this.Trigger).ToString() } - }; - switch (this.Trigger.ActionMode) - { - case ActionExecutionMode.Parallel: - foreach (ActionDefinition triggerAction in this.Trigger.Actions) - { - metadata[V1WorkflowActivityMetadata.Action] = triggerAction.Name!; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, await this.Context.FilterInputAsync(triggerAction, output!, cancellationToken), metadata, this.Activity, cancellationToken); - } - break; - case ActionExecutionMode.Sequential: - var action = this.Trigger.Actions.First(); - metadata[V1WorkflowActivityMetadata.Action] = action.Name!; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, await this.Context.FilterInputAsync(action, output!, cancellationToken), metadata, this.Activity, cancellationToken); - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.Trigger.ActionMode}' is not supported"); - } - foreach (V1WorkflowActivity activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { - var childProcessor = this.CreateProcessorFor(activity); - await childProcessor.ProcessAsync(cancellationToken); - } - } - } - - /// - /// Handles the completion of the specified - /// - /// The to handle the completion of - /// A - /// A new awaitable - protected virtual async Task OnEventCompletedAsync(ConsumeEventProcessor processor, CancellationToken cancellationToken) - { - using var asyncLock = await this.Lock.LockAsync(cancellationToken); - this.Processors.TryRemove(processor); - processor.Dispose(); - } - - /// - /// Handles the next 's - /// - /// The that has returned the - /// The - /// A to handle - /// A new awaitable - protected virtual async Task OnActionResultAsync(ActionProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) - { - using var asyncLock = await this.Lock.LockAsync(cancellationToken); - if (this.Trigger.ActionMode == ActionExecutionMode.Sequential) - { - if (this.Trigger.TryGetNextAction(processor.Action.Name!, out ActionDefinition action)) - { - var input = this.Activity.Input.ToObject().Merge(e.Output); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Trigger, this.State.Triggers.IndexOf(this.Trigger).ToString() }, - { V1WorkflowActivityMetadata.Action, action.Name! } - }; - var activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, await this.Context.FilterInputAsync(action, input, cancellationToken), metadata, this.Activity, cancellationToken); - var subProcessor = this.CreateProcessorFor(activity); - await subProcessor.ProcessAsync(cancellationToken); - } - else - { - await this.OnNextAsync(e, cancellationToken); - } - } - else - { - var childActivities = (await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - .Where(p => p.Type == V1WorkflowActivityType.Action - && p.Status == V1WorkflowActivityStatus.Completed - && p.Output != null) - .ToList(); - if (childActivities.Count == this.Trigger.Actions.Count) - { - var output = new object(); - foreach (var activity in childActivities - .Where(p => p.Status == V1WorkflowActivityStatus.Completed && p.Output != null)) - { - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Trigger, out var rawTriggerId)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Trigger}'"); - if (!int.TryParse(rawTriggerId, out var triggerId)) - throw new ArgumentException($"The '{V1WorkflowActivityMetadata.Trigger}' metadata field of activity '{activity.Id}' is not a valid integer"); - if (!this.State.TryGetTrigger(triggerId, out var trigger)) - throw new NullReferenceException($"Failed to find a trigger ath the specified index '{triggerId}' in the event state with name '{this.State.Name}'"); - if (!trigger.TryGetAction(rawTriggerId, out var action)) - throw new NullReferenceException($"Failed to find an action with name '{rawTriggerId}' in the event trigger with at index '{triggerId}', inside state with name '{this.State.Name}'"); - if (action.UseResults()) - { - var expression = action.ActionDataFilter?.ToStateData?.Trim(); - var json = await this.JsonSerializer.SerializeAsync(activity.Output, cancellationToken); - if (string.IsNullOrWhiteSpace(expression)) - { - expression = json; - } - else - { - if (expression.StartsWith("${")) - expression = expression[2..^1]; - expression = $"{expression} = {json}"; - } - output = await this.Context.EvaluateAsync(expression, output, cancellationToken); - } - } - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - } - } - } - - /// - /// Handles the completion of the specified - /// - /// The that has completed - /// A - /// A new awaitable - protected virtual async Task OnActionCompletedAsync(ActionProcessor processor, CancellationToken cancellationToken) - { - using var asyncLock = await this.Lock.LockAsync(cancellationToken); - this.Processors.TryRemove(processor); - processor.Dispose(); - IEnumerable childActivities = await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken); - if (childActivities - .Where(a => a.Type != V1WorkflowActivityType.End && a.Type != V1WorkflowActivityType.Transition) - .All(p => p.Status == V1WorkflowActivityStatus.Completed)) - { - await base.OnCompletedAsync(cancellationToken); - return; - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/ForEachStateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/ForEachStateProcessor.cs deleted file mode 100644 index bd0a6998d..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/ForEachStateProcessor.cs +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process s - /// - public class ForEachStateProcessor - : StateProcessor - { - - /// - public ForEachStateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IJsonSerializer jsonSerializer, IOptions options, V1WorkflowActivity activity, ForEachStateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - this.JsonSerializer = jsonSerializer; - } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var cancellationToken = this.CancellationTokenSource.Token; - var processor = (IterationProcessor)base.CreateProcessorFor(activity); - processor.OfType().SubscribeAsync - ( - async result => await this.OnIterationResultAsync(processor, result, cancellationToken), - async ex => await this.OnErrorAsync(ex, cancellationToken), - async () => await this.OnIterationCompletedAsync(processor, cancellationToken) - ); - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - { - var inputCollection = await this.GetInputCollectionAsync(cancellationToken); - if (!inputCollection.Any()) - { - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input!.ToObject()!), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - return; - } - var iterationParamValue = inputCollection.First(); - var input = (DynamicObject)this.Activity.Input!; - var iterationParam = this.State.IterationParameter; - if (string.IsNullOrWhiteSpace(iterationParam)) - iterationParam = "item"; - input.Set(iterationParam, iterationParamValue); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Iteration, "0" } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Iteration, input.ToObject(), metadata, this.Activity, cancellationToken); - } - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (var childProcessor in this.Processors) - { - await childProcessor.ProcessAsync(cancellationToken); - } - } - - /// - /// Gets the 's input - /// - /// A - /// The 's input - protected virtual async Task> GetInputCollectionAsync(CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(this.State.InputCollection)) - return Array.Empty(); - var inputCollection = await this.Context.EvaluateAsync(this.State.InputCollection, this.Activity.Input!.ToObject(), cancellationToken); - if (inputCollection == null) - throw new NullReferenceException($"Failed to resolve the input collection defined by expression '{this.State.InputCollection}'"); - var enumerable = inputCollection as IEnumerable; - if (enumerable is Dynamic dyn) - enumerable = (IEnumerable)dyn.ToObject()!; - return enumerable!.OfType(); - } - - /// - /// Gets the 's output - /// - /// A - /// The 's output - protected virtual async Task> GetOutputCollectionAsync(CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(this.State.OutputCollection)) - return new(); - var outputCollection = await this.Context.EvaluateAsync(this.State.OutputCollection, this.Activity.Input!.ToObject(), cancellationToken); - if (outputCollection == null) - return new(); - var enumerable = outputCollection as IEnumerable; - if (enumerable is Dynamic dyn) - enumerable = (IEnumerable)dyn.ToObject()!; - return enumerable!.OfType().ToList(); - } - - /// - /// Handles the produced by the specified - /// - /// The that has produced the to handle - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnIterationResultAsync(IterationProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) - { - var iterationIndex = int.Parse(processor.Activity.Metadata[V1WorkflowActivityMetadata.Iteration]); - var inputCollection = await this.GetInputCollectionAsync(cancellationToken); - if ((this.State.BatchSize.HasValue && this.State.BatchSize.Value <= iterationIndex) - || inputCollection.Count() - 1 <= iterationIndex) - { - var outputCollection = await this.GetOutputCollectionAsync(cancellationToken); - foreach (var iterationActivity in (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Iteration && a.Status == V1WorkflowActivityStatus.Completed && a.Output != null) - .ToList()) - { - outputCollection.Add(iterationActivity.Output!.ToObject()!); - } - var output = this.Activity.Input.ToObject(); - var expression = this.State.IterationParameter; - if (string.IsNullOrWhiteSpace(expression)) - expression = "item"; - if (expression.StartsWith("${")) - expression = expression[2..^1]; - expression = $". |= del(.{expression})"; - if (output != null) - output = await this.Context.EvaluateAsync(expression, output, cancellationToken); - if (!string.IsNullOrWhiteSpace(this.State.OutputCollection)) - { - expression = this.State.OutputCollection.Trim(); - var outputCollectionJson = await this.JsonSerializer.SerializeAsync(outputCollection, cancellationToken); - if (expression.StartsWith("${")) - expression = expression[2..^1]; - expression = $"{expression} = {outputCollectionJson}"; - output = await this.Context.EvaluateAsync(expression, output, cancellationToken); - } - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - return; - } - iterationIndex += 1; - var iterationParameterValue = inputCollection.ElementAt(iterationIndex); - var input = DynamicObject.FromObject(await this.Context.Workflow.GetActivityStateDataAsync(this.Activity, cancellationToken))!; - input.Set(this.State.IterationParameter!, iterationParameterValue); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Iteration, iterationIndex.ToString() } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Iteration, input.ToObject(), metadata, this.Activity, cancellationToken); - } - - /// - /// Handles the completion of the specified - /// - /// The to handle the completion of - /// A - /// A new awaitable - protected virtual async Task OnIterationCompletedAsync(IterationProcessor processor, CancellationToken cancellationToken) - { - var iterationIndex = int.Parse(processor.Activity.Metadata[V1WorkflowActivityMetadata.Iteration]); - this.Processors.TryRemove(processor); - processor.Dispose(); - var inputCollection = await this.GetInputCollectionAsync(cancellationToken); - if ((this.State.BatchSize.HasValue && this.State.BatchSize.Value - 1 <= iterationIndex) - || inputCollection.Count() - 1 <= iterationIndex) - { - await this.OnCompletedAsync(cancellationToken); - return; - } - var childActivities = await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken); - foreach (var childActivity in childActivities - .Where(p => p.Status == V1WorkflowActivityStatus.Pending)) - { - var nextProcessor = this.CreateProcessorFor(childActivity); - await nextProcessor.ProcessAsync(cancellationToken); - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/FunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/FunctionProcessor.cs deleted file mode 100644 index fac9b51dd..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/FunctionProcessor.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors; - - -///

-/// Represents the base class for all s used to process s -/// -public abstract class FunctionProcessor - : ActionProcessor -{ - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public FunctionProcessor(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, ActionDefinition action, FunctionDefinition function) - : base(loggerFactory, context, activityProcessorFactory, options, activity, action) - { - this.ServiceProvider = serviceProvider; - this.Function = function; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the to process - /// - protected FunctionDefinition Function { get; } - - /// - /// Gets the to process - /// - protected FunctionReference FunctionReference - { - get - { - return this.Action.Function!; - } - } - - /// - /// Gets the object used to configure the authentication mechanism to use when invoking the function - /// - protected AuthenticationDefinition? Authentication { get; private set; } - - /// - /// Gets the object that describes the authorization resolved using the specified - /// - protected AuthorizationInfo? Authorization { get; private set; } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (!string.IsNullOrWhiteSpace(this.Function.AuthRef)) - { - if (this.Context.Workflow.Definition.TryGetAuthentication(this.Function.AuthRef, out var auth)) - { - if (auth.Properties is SecretBasedAuthenticationProperties secretBased) - { - auth.Properties = auth.Scheme switch - { - AuthenticationScheme.Basic => await this.Context.GetSecretAsync(secretBased.Secret), - AuthenticationScheme.Bearer => await this.Context.GetSecretAsync(secretBased.Secret), - AuthenticationScheme.OAuth2 => await this.Context.GetSecretAsync(secretBased.Secret), - _ => throw new NotSupportedException($"The specified {nameof(AuthenticationScheme)} '{auth.Scheme}' is not supported"), - }; - } - this.Authentication = auth; - if (this.Authentication != null) - { - this.Authorization = await AuthorizationInfo.CreateAsync(this.ServiceProvider, this.Authentication, cancellationToken); - } - } - else - { - throw new NullReferenceException($"Failed to find the authentication definition with name '{this.Function.AuthRef}'"); - } - } - } - - /// - protected override async Task OnNextAsync(IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken) - { - if (e is V1WorkflowActivityCompletedIntegrationEvent completedEvent) - { - var output = completedEvent.Output.ToObject(); - if (this.Action.ActionDataFilter != null) - { - output = await this.Context.FilterOutputAsync(this.Action, output!, this.Authorization, cancellationToken); - } - await base.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - } - else - { - await base.OnNextAsync(e, cancellationToken); - } - } - -} \ No newline at end of file diff --git a/src/apps/Synapse.Worker/Services/Processors/GraphQLFunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/GraphQLFunctionProcessor.cs deleted file mode 100644 index 01d842214..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/GraphQLFunctionProcessor.cs +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using GraphQL; -using GraphQL.Client.Abstractions; -using GraphQL.Client.Abstractions.Websocket; -using GraphQL.Client.Http; -using System.Dynamic; - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process GraphQL s - /// - public class GraphQLFunctionProcessor - : FunctionProcessor - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to create s - /// The service used to serialize and deserialize GraphQL requests/responses - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public GraphQLFunctionProcessor(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IHttpClientFactory httpClientFactory, IGraphQLWebsocketJsonSerializer serializer, IOptions options, V1WorkflowActivity activity, - ActionDefinition action, FunctionDefinition function) - : base(serviceProvider, loggerFactory, context, activityProcessorFactory, options, activity, action, function) - { - this.HttpClient = httpClientFactory.CreateClient(); - this.Serializer = serializer; - } - - /// - /// Gets the used by the to execute the function - /// - protected HttpClient HttpClient { get; } - - /// - /// Gets the service used to process GraphQL requests - /// - protected IGraphQLClient GraphQLClient { get; private set; } = null!; - - /// - /// Gets the service used to serialize and deserialize GraphQL requests/responses - /// - protected IGraphQLWebsocketJsonSerializer Serializer { get; } - - /// - /// Gets the of the GraphQL endpoint to request - /// - protected Uri EndpointUri { get; private set; } = null!; - - /// - /// Gets the name of the GraphQL operation to execute - /// - protected string OperationName { get; private set; } = null!; - - /// - /// Gets the type of the GraphQL operation to execute - /// - protected string OperationType { get; private set; } = null!; - - /// - /// Gets a string that represents the GraphQL request arguments - /// - protected string? Arguments { get; private set; } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - await base.InitializeAsync(cancellationToken); - this.HttpClient.UseAuthorization(this.Authorization); - var operationComponents = this.Function.Operation.Split('#', StringSplitOptions.RemoveEmptyEntries); - if (operationComponents.Length != 3) - throw new FormatException($"The 'operation' property of the GraphQL function with name '{this.Function.Name}' has an invalid value '{this.Function.Operation}'. GraphQL functions expect a value in the following format: ##"); - if (!Uri.TryCreate(operationComponents[0], new(), out var endpointUri) - || endpointUri == null) - throw new FormatException($"The value specified as GraphQL endpoint operation component '{operationComponents[0]}' is not a valid uri"); - this.EndpointUri = endpointUri; - this.OperationType = operationComponents[1].ToLower(); - if (this.OperationType != "query" - && this.OperationType != "mutation") - throw new FormatException($"The value specified as GraphQL request type component '{operationComponents[1]}' is not supported"); - this.OperationName = operationComponents[2]; - await this.BuildArgumentsAsync(cancellationToken); - var options = new GraphQLHttpClientOptions() - { - EndPoint = this.EndpointUri - }; - this.GraphQLClient = new GraphQLHttpClient(options, this.Serializer, this.HttpClient); - } - - /// - /// Builds the GraphQL arguments string - /// - /// A - protected virtual async ValueTask BuildArgumentsAsync(CancellationToken cancellationToken) - { - var args = null as IDictionary; - if (this.Activity.Input == null) - args = new Dictionary(); - if (this.FunctionReference.Arguments == null) - return; - string json = JsonConvert.SerializeObject(this.FunctionReference.Arguments, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); - foreach (Match match in Regex.Matches(json, @"""\$\{.+?\}""")) - { - var expression = match.Value[3..^2].Trim(); - var evaluationResult = await this.Context.EvaluateAsync(expression, this.Activity.Input!.ToObject()!, cancellationToken); - if (evaluationResult == null) - { - Console.WriteLine($"Evaluation result of expression {expression} on data {JsonConvert.SerializeObject(this.Activity.Input)} is NULL"); //todo: replace with better message - continue; - } - var valueToken = JToken.FromObject(evaluationResult); - var value = null as string; - if (valueToken != null) - { - value = valueToken.Type switch - { - JTokenType.String => @$"""{valueToken}""", - _ => valueToken.ToString(), - }; - } - if (string.IsNullOrEmpty(value)) - value = "null"; - json = json.Replace(match.Value, value); - } - args = JsonConvert.DeserializeObject(json)!; - this.Arguments = string.Join(", ", args.Select(a => $"{a.Key}: {this.SerializeToGraphQL(a.Value)}")); - } - - /// - /// Serializes the specified value to GraphQL - /// - /// The value to serialize to GraphQL - /// The serialized value - protected virtual string? SerializeToGraphQL(object? value) - { - if (value == null) - return null!; - var valueType = value.GetType(); - if (valueType.IsPrimitiveType()) - return JsonConvert.SerializeObject(value, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); - if (value is IEnumerable enumerable - && !typeof(IDictionary).IsAssignableFrom(valueType)) - { - var values = new List(); - foreach (var elem in enumerable) - { - values.Add(this.SerializeToGraphQL(elem)!); - } - return $"[ {string.Join(", ", values)} ]"; - } - var properties = new Dictionary(); - if (value is IDictionary dictionary) - { - properties = dictionary.ToDictionary(kvp => kvp.Key, kvp => this.SerializeToGraphQL(kvp.Value))!; - } - else - { - foreach (var property in value.GetType().GetProperties()) - { - properties.Add(property.Name, this.SerializeToGraphQL(property.GetValue(value))!); - } - } - return $"{{{string.Join($", ", properties.Select(p => $"{p.Key}: {p.Value}"))}}}"; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - await base.ProcessAsync(cancellationToken); - if (this.Activity.Status == V1WorkflowActivityStatus.Skipped) - return; - var request = new GraphQLRequest() - { - Query = @$"{this.OperationType}{{ - {this.OperationName}({this.Arguments}){this.FunctionReference.SelectionSet} -}}" - }; - try - { - var response = null as IGraphQLResponse; - response = this.OperationType switch - { - "query" => await this.GraphQLClient.SendMutationAsync(request, cancellationToken), - "mutation" => await this.GraphQLClient.SendQueryAsync(request, cancellationToken), - _ => throw new NotSupportedException($"The specified GraphQL operation type '{this.OperationType}' is not supported"), - }; - if (response.Errors != null - && response.Errors.Any()) - throw new Exception($"An error occured while executing the GraphQL function with name '{this.Function.Name}':{Environment.NewLine}{string.Join(Environment.NewLine, response.Errors.Select(e => $"{e.Path}: {e.Message}"))}"); - var output = null as object; - if (response.Data is JToken token) - output = token.ToObject(); - else - output = response.Data; - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - catch (GraphQLHttpRequestException ex) - { - await this.OnErrorAsync(new Exception($"An error occured while executing the GraphQL request: the server returned a non-success status code '{ex.StatusCode}'.{Environment.NewLine}Details: {ex.Content}"), cancellationToken); - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - this.HttpClient.Dispose(); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/GrpcFunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/GrpcFunctionProcessor.cs deleted file mode 100644 index 98cfef2f9..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/GrpcFunctionProcessor.cs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Google.Protobuf.Reflection; -using Grpc.Net.Client; -using ProtoBuf.Grpc.Client; - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process GRPC s - /// - public class GrpcFunctionProcessor - : FunctionProcessor - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public GrpcFunctionProcessor(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, - ActionDefinition action, FunctionDefinition function) - : base(serviceProvider, loggerFactory, context, activityProcessorFactory, options, activity, action, function) - { - - } - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - var proto = ""; //todo - - var fileDescriptorSet = new FileDescriptorSet(); - fileDescriptorSet.Add("" /* todo */, true, new StringReader(proto)); - fileDescriptorSet.Process(); - var errors = fileDescriptorSet.GetErrors(); - - foreach (var file in fileDescriptorSet.Files) - { - foreach (var service in file.Services) - { - foreach (var method in service.Methods) - { - //todo: if method.Deprecated => display warning - - } - } - } - - var channel = GrpcChannel.ForAddress($"{EnvironmentVariables.Api.HostName.Value}:8080"); - var serviceName = ""; - var client = new GrpcClient(channel, serviceName); - - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - await base.ProcessAsync(cancellationToken); - if (this.Activity.Status == V1WorkflowActivityStatus.Skipped) - return; - - //await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - //await this.OnCompletedAsync(cancellationToken); - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/InjectStateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/InjectStateProcessor.cs deleted file mode 100644 index 98f8ccca1..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/InjectStateProcessor.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process s - /// - public class InjectStateProcessor - : StateProcessor - { - - /// - public InjectStateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, InjectStateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - - } - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - var output = this.Activity.Input!.ToObject()!; - var toInject = this.State.Data!.ToObject()!; - toInject = await this.Context.EvaluateObjectAsync(toInject, output, cancellationToken); - output = output.Merge(toInject); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/IterationProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/IterationProcessor.cs deleted file mode 100644 index cf31e6965..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/IterationProcessor.cs +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the base class for all s used to process 's iterations - /// - public class IterationProcessor - : WorkflowActivityProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to serialize/deserialize to/from JSON - /// The service used to access the current - /// The to process - /// The that defines the iteration to process - /// The index of the iteration to process - public IterationProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IJsonSerializer jsonSerializer, IOptions options, V1WorkflowActivity activity, ForEachStateDefinition state, int iterationIndex) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.JsonSerializer = jsonSerializer; - this.State = state; - this.IterationIndex = iterationIndex; - } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - /// Gets the that defines the iteration to process - /// - protected ForEachStateDefinition State { get; } - - /// - /// Gets the index of the iteration to process - /// - protected int IterationIndex { get; } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var processor = (ActionProcessor)base.CreateProcessorFor(activity); - processor.SubscribeAsync - ( - async e => await this.OnNextActionAsync(processor, e, this.CancellationTokenSource.Token), - async ex => await this.OnActionFaultedAsync(processor, ex, this.CancellationTokenSource.Token), - async () => await this.OnActionCompletedAsync(processor, this.CancellationTokenSource.Token) - ); - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - { - var input = null as object; - var metadata = null as Dictionary; - switch (this.State.Mode) - { - case ActionExecutionMode.Parallel: - foreach (var action in this.State.Actions) - { - input = await this.Context.FilterInputAsync(action, this.Activity.Input, cancellationToken); - metadata = new() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, action.Name! } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, cancellationToken); - } - break; - case ActionExecutionMode.Sequential: - var firstAction = this.State.Actions.First(); - input = await this.Context.FilterInputAsync(firstAction, this.Activity.Input, cancellationToken); - metadata = new() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, firstAction.Name! } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, cancellationToken); - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.State.Mode}' is not supported"); - } - } - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { - _ = this.CreateProcessorFor(activity); - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (IWorkflowActivityProcessor processor in this.Processors.ToList()) - { - await processor.ProcessAsync(cancellationToken); - } - } - - /// - /// Aggregates the output of the completed actions activities that belongs to the processed - /// - /// A - /// The result of the action outputs aggregation - protected virtual async Task AggregateActionOutputsAsync(CancellationToken cancellationToken) - { - object? output = null; - foreach (var activity in (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Action && a.Status == V1WorkflowActivityStatus.Completed) - .OrderBy(a => a.ExecutedAt)) - { - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Action, out var actionName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Action}'"); - if (!this.State.TryGetAction(actionName, out var action)) - throw new NullReferenceException($"Failed to find an action with name '{actionName}' in the operation state with name '{this.State.Name}'"); - if (action.UseResults()) - { - var expression = action.ActionDataFilter?.ToStateData?.Trim(); - var json = await this.JsonSerializer.SerializeAsync(activity.Output, cancellationToken); - if (string.IsNullOrWhiteSpace(expression)) - { - expression = json; - } - else - { - if (expression.StartsWith("${")) - expression = expression[2..^1]; - expression = $"{expression} = {json}"; - } - if (output == null) - output = new(); - output = await this.Context.EvaluateAsync(expression, output, cancellationToken); - } - } - return output!; - } - - /// - /// Handles s - /// - /// The to handle the for - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnNextActionAsync(IActionProcessor processor, IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken) - { - switch (e) - { - case V1WorkflowActivitySkippedIntegrationEvent: - case V1WorkflowActivityCompletedIntegrationEvent: - using (await this.Lock.LockAsync(cancellationToken)) - { - var output = null as object; - switch (this.State.Mode) - { - case ActionExecutionMode.Parallel: - var activities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Action) - .ToList(); - if (activities.All(a => a.Status >= V1WorkflowActivityStatus.Faulted)) - { - output = await this.AggregateActionOutputsAsync(cancellationToken); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - } - break; - case ActionExecutionMode.Sequential: - output = await this.AggregateActionOutputsAsync(cancellationToken); - if (processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Action, out var currentActionName) - && this.State.TryGetNextAction(currentActionName, out ActionDefinition nextAction)) - { - var input = (object)this.Activity.Input.ToObject().Merge(output); - input = await this.Context.FilterInputAsync(nextAction, input, cancellationToken); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, nextAction.Name! } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, cancellationToken); - } - else - { - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - } - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.State.Mode}' is not supported"); - } - } - break; - } - } - - /// - /// Handles an 's - /// - /// The that has thrown the specified - /// The thrown - /// A - /// A new awaitable - protected virtual async Task OnActionFaultedAsync(ActionProcessor processor, Exception ex, CancellationToken cancellationToken) - { - using (await this.Lock.LockAsync(cancellationToken)) - { - await base.OnErrorAsync(ex, cancellationToken); - } - } - - /// - /// Handles the completion of the specified - /// - /// The that has completed processing the specified - /// A - /// A new awaitable - protected virtual async Task OnActionCompletedAsync(ActionProcessor processor, CancellationToken cancellationToken) - { - using (await this.Lock.LockAsync(cancellationToken)) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - var activities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Action); - if (activities.All(a => a.Status >= V1WorkflowActivityStatus.Faulted)) - { - await base.OnCompletedAsync(cancellationToken); - return; - } - foreach (var activity in activities - .Where(p => p.Status == V1WorkflowActivityStatus.Pending)) - { - var nextProcessor = this.CreateProcessorFor(activity); - await nextProcessor.ProcessAsync(cancellationToken); - } - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/ODataFunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/ODataFunctionProcessor.cs deleted file mode 100644 index c6a26c3f8..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/ODataFunctionProcessor.cs +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Simple.OData.Client; - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process ODATA s - /// - public class ODataFunctionProcessor - : FunctionProcessor - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to create s - /// The service used to provide s - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public ODataFunctionProcessor(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IHttpClientFactory httpClientFactory, ISerializerProvider serializerProvider, IOptions options, V1WorkflowActivity activity, - ActionDefinition action, FunctionDefinition function) - : base(serviceProvider, loggerFactory, context, activityProcessorFactory, options, activity, action, function) - { - this.HttpClient = httpClientFactory.CreateClient(); - this.SerializerProvider = serializerProvider; - } - - /// - /// Gets the used by the to execute the function - /// - protected HttpClient HttpClient { get; } - - /// - /// Gets the service used to provide s - /// - protected ISerializerProvider SerializerProvider { get; } - - /// - /// Gets the service used to query the remote ODATA API - /// - protected IODataClient ODataClient { get; private set; } = null!; - - /// - /// Gets the of the OData service to query - /// - protected Uri ServiceUri { get; private set; } = null!; - - /// - /// Gets the OData entity set to query - /// - protected string EntitySet { get; private set; } = null!; - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - await base.InitializeAsync(cancellationToken); - this.HttpClient.UseAuthorization(this.Authorization); - var odataUriString = this.Function.Operation; - if (odataUriString.IsRuntimeExpression()) - { - var evaluationResult = (string?)await this.Context.EvaluateAsync(odataUriString, this.Activity.Input, cancellationToken); - if (!string.IsNullOrWhiteSpace(evaluationResult)) odataUriString = evaluationResult; - } - var components = odataUriString.Split("#", StringSplitOptions.RemoveEmptyEntries); - if (components.Length != 2) - throw new FormatException($"The 'operation' property of the ODATA function with name '{this.Function.Name}' has an invalid value '{this.Function.Operation}'. ODATA functions expect a value in the following format: #"); - this.ServiceUri = new(components.First()); - this.EntitySet = components.Last(); - this.HttpClient.BaseAddress = new($"{this.ServiceUri.Scheme}://{this.ServiceUri.Authority}"); - this.ODataClient = new ODataClient(new ODataClientSettings(this.HttpClient, this.HttpClient.BaseAddress.MakeRelativeUri(this.ServiceUri)) { PayloadFormat = ODataPayloadFormat.Json }); - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - await base.ProcessAsync(cancellationToken); - if (this.Activity.Status == V1WorkflowActivityStatus.Skipped) - return; - try - { - var parameters = this.FunctionReference.Arguments?.ToObject()!; - var inputData = this.Activity.Input!.ToObject()!; - parameters = (await this.Context.EvaluateObjectAsync(parameters, inputData, cancellationToken))!; - - var commandOptions = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(parameters)); - var command = this.EntitySet; - if (!string.IsNullOrWhiteSpace(commandOptions?.Key)) - command += $"({commandOptions.Key})"; - if (commandOptions?.QueryOptions != null) - command += $"?{commandOptions.QueryOptions.ToQueryString()}"; - var output = null as object; - if (string.IsNullOrWhiteSpace(commandOptions?.Key)) - output = await this.ODataClient.FindEntriesAsync(command, cancellationToken); - else - output = await this.ODataClient.FindEntryAsync(command, cancellationToken); - if (output == null) - output = new(); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - this.HttpClient.Dispose(); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/OpenApiFunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/OpenApiFunctionProcessor.cs deleted file mode 100644 index 9fd1affdc..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/OpenApiFunctionProcessor.cs +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; -using System.Dynamic; -using System.Net; -using System.Net.Mime; -using System.Text; - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process Open API s - /// - public class OpenApiFunctionProcessor - : FunctionProcessor - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to create s - /// The service used to provide s - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public OpenApiFunctionProcessor(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IHttpClientFactory httpClientFactory, ISerializerProvider serializerProvider, IOptions options, V1WorkflowActivity activity, - ActionDefinition action, FunctionDefinition function) - : base(serviceProvider, loggerFactory, context, activityProcessorFactory, options, activity, action, function) - { - this.HttpClient = httpClientFactory.CreateClient(); - this.SerializerProvider = serializerProvider; - } - - /// - /// Gets the used by the to execute the function - /// - protected HttpClient HttpClient { get; } - - /// - /// Gets the service used to provide s - /// - protected ISerializerProvider SerializerProvider { get; } - - /// - /// Gets the that defines the operation to invoke - /// - protected OpenApiDocument Document { get; private set; } = null!; - - /// - /// Gets the id of the to invoke, as defined by the property - /// - protected OpenApiOperation Operation { get; private set; } = null!; - - /// - /// Gets the to use to invoke the - /// - protected HttpMethod HttpMethod { get; private set; } = null!; - - /// - /// Gets/sets a containing the addresses of the available remote servers - /// - protected List Servers { get; private set; } = null!; - - /// - /// Gets/sets the uri's path segment of the function to execute - /// - protected string Path { get; private set; } = null!; - - /// - /// Gets/sets an that represents the parameters to pass to the function to execute - /// - protected IDictionary Parameters { get; private set; } = null!; - - /// - /// Gets/sets the uri's query string segment of the function to execute - /// - protected string QueryString { get; private set; } = null!; - - /// - /// Gets/sets the body of the sent to invoke the function - /// - protected object? Body { get; private set; } - - /// - /// Gets/sets a containing the headers of the sent to invoke the function - /// - protected Dictionary Headers { get; } = new Dictionary(); - - /// - /// Gets/sets a containing the cookies of the sent to invoke the function - /// - protected Dictionary Cookies { get; } = new Dictionary(); - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - try - { - await base.InitializeAsync(cancellationToken); - this.HttpClient.UseAuthorization(this.Authorization); - var openApiUriString = this.Function.Operation; - if (openApiUriString.IsRuntimeExpression()) - { - var evaluationResult = (string?)await this.Context.EvaluateAsync(openApiUriString, this.Activity.Input, cancellationToken); - if (!string.IsNullOrWhiteSpace(evaluationResult)) - openApiUriString = evaluationResult; - } - var operationComponents = openApiUriString.Split('#'); - if (operationComponents.Length != 2) - throw new FormatException($"The 'operation' property of the Open API function with name '{this.Function.Name}' has an invalid value '{this.Function.Operation}'. Open API functions expect a value in the following format: #"); - openApiUriString = operationComponents.First(); - if (openApiUriString.IsRuntimeExpression()) - { - var evaluationResult = (string?)await this.Context.EvaluateAsync(openApiUriString, this.Activity.Input, cancellationToken); - if (!string.IsNullOrWhiteSpace(evaluationResult)) - openApiUriString = evaluationResult; - } - var openApiUri = new Uri(openApiUriString); - var operationId = operationComponents.Last(); - using (HttpRequestMessage request = new(HttpMethod.Get, openApiUri)) - { - using HttpResponseMessage response = await this.HttpClient.SendAsync(request, cancellationToken); - var responseContent = response.Content == null ? null : await response.Content.ReadAsStringAsync(cancellationToken); - if (!response.IsSuccessStatusCode) - { - this.Logger.LogInformation("Failed to retrieve the Open API document at location '{uri}'. The remote server responded with a non-success status code '{statusCode}'.", openApiUri, response.StatusCode); - this.Logger.LogDebug("Response content:\r\n{responseContent}", responseContent ?? "None"); - response.EnsureSuccessStatusCode(responseContent); - } - using var responseStream = await response.Content!.ReadAsStreamAsync(cancellationToken)!; - this.Document = new OpenApiStreamReader().Read(responseStream, out var diagnostic); - } - var operation = this.Document.Paths - .SelectMany(p => p.Value.Operations) - .FirstOrDefault(o => o.Value.OperationId == operationId); - if (operation.Value == null) throw new NullReferenceException($"Failed to find an operation with id '{this.Operation}' in OpenAPI document at '{openApiUri}'"); - this.HttpMethod = operation.Key.ToHttpMethod(); - this.Operation = operation.Value; - this.Servers = this.Document.Servers.Select(s => s.Url).ToList(); - if (!this.Servers.Any()) this.Servers.Add(openApiUri.ToString().Replace(openApiUri.PathAndQuery, string.Empty)); - KeyValuePair path = this.Document.Paths.Single(p => p.Value.Operations.Any(o => o.Value.OperationId == operation.Value.OperationId)); - this.Path = path.Key; - await this.BuildParametersAsync(cancellationToken); - if (this.Parameters == null) return; - var parameters = path.Value.Parameters.ToList(); - parameters.AddRange(this.Operation.Parameters); - foreach (OpenApiParameter param in parameters - .Where(p => p.In == ParameterLocation.Cookie) - .GroupBy(p => p.Name)) - { - var match = await this.TryGetParameterAsync($@".[""{param.Name}""]", cancellationToken); - if (match.HasMatch) - this.Cookies.Add(param.Name, match.Value); - else if (param.Required) - throw new NullReferenceException($"Failed to find the definition of the required parameter '{param.Name}' in the function definition with name '{this.Function.Name}'"); - } - foreach (OpenApiParameter param in parameters - .Where(p => p.In == ParameterLocation.Header)) - { - var match = await this.TryGetParameterAsync($@".[""{param.Name}""]", cancellationToken); - if (match.HasMatch) - this.Headers.Add(param.Name, match.Value); - else if (param.Required) - throw new NullReferenceException($"Failed to find the definition of the required parameter '{param.Name}' in the function definition with name '{this.Function.Name}'"); - } - foreach (OpenApiParameter param in parameters - .Where(p => p.In == ParameterLocation.Path)) - { - var match = await this.TryGetParameterAsync($@".[""{param.Name}""]", cancellationToken); - if (match.HasMatch) - this.Path = this.Path.Replace($"{{{param.Name}}}", match.Value); - else if (param.Required) - throw new NullReferenceException($"Failed to find the definition of the required parameter '{param.Name}' in the function definition with name '{this.Function.Name}'"); - } - Dictionary queryParameters = new(); - foreach (OpenApiParameter param in parameters - .Where(p => p.In == ParameterLocation.Query)) - { - var match = await this.TryGetParameterAsync($@".[""{param.Name}""]", cancellationToken); - if (match.HasMatch) - queryParameters.Add(param.Name, match.Value); - else if (param.Required) - throw new NullReferenceException($"Failed to find the definition of the required parameter '{param.Name}' in the function definition with name '{this.Function.Name}'"); - } - this.QueryString = string.Join("&", queryParameters.Select(kvp => $"{kvp.Key}={kvp.Value}")); - if (operation.Value.RequestBody != null) - { - if (operation.Value.RequestBody.Extensions.TryGetValue("x-bodyName", out IOpenApiExtension? bodyNameExtension)) - { - this.Body = await this.Context.EvaluateAsync($".{((OpenApiString)bodyNameExtension).Value}", this.Parameters, this.Authorization, cancellationToken); - } - else - { - if (this.Parameters.TryGetValue("body", out var bodyValue)) - { - this.Body = await this.Context.EvaluateAsync(".body", this.Parameters, this.Authorization, cancellationToken); - } - else - { - if (this.Parameters is not ExpandoObject expando) - { - expando = new ExpandoObject(); - foreach (var param in this.Parameters) - { - expando.TryAdd(param.Key, param.Value); - } - } - this.Body = expando; - } - } - if (this.Body == null && operation.Value.RequestBody.Required) - throw new NullReferenceException($"Failed to determine the required body parameter for the function with name '{this.Function.Name}'"); - } - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - } - - /// - /// Builds the parameters to pass to the function to execute - /// - /// A - protected virtual async Task BuildParametersAsync(CancellationToken cancellationToken) - { - if (this.Activity.Input == null) - this.Parameters = new Dictionary(); - if (this.FunctionReference.Arguments == null) - return; - var parameters = this.FunctionReference.Arguments.ToObject()!; - var inputData = this.Activity.Input!.ToObject()!; - parameters = (await this.Context.EvaluateObjectAsync(parameters, inputData, this.Authorization, cancellationToken))!; - this.Parameters = ((ExpandoObject)parameters)!; - } - - /// - /// Attempts to retrieve the specified parameter - /// - /// A runtime expression used to reference the parameter to retrieve - /// A - /// A tuple containing both a boolean indicating if the specified parameter could be found and the parameter's value, if any - protected virtual async Task<(bool HasMatch, string Value)> TryGetParameterAsync(string expression, CancellationToken cancellationToken) - { - var value = null as string; - var token = await this.Context.EvaluateAsync(expression, this.Parameters, this.Authorization, cancellationToken); - if (token == null) return (false, value!); - value = token.ToString()!; - return (true, value); - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - await base.ProcessAsync(cancellationToken); - if (this.Activity.Status == V1WorkflowActivityStatus.Skipped) - return; - try - { - var output = null as object; - var success = false; - foreach (string server in this.Servers) - { - var requestUri = $"{server}{this.Path}"; - if (requestUri.StartsWith("//"))requestUri = $"https:{requestUri}"; - if (!string.IsNullOrWhiteSpace(this.QueryString)) requestUri += $"?{this.QueryString}"; - using var request = new HttpRequestMessage(this.HttpMethod, requestUri); - foreach (var header in this.Headers) - { - request.Headers.Add(header.Key, header.Value); - } - request.Headers.Add("Cookie", string.Join(";", this.Cookies.Select(kvp => $"{kvp.Key}={kvp.Value}"))); - if (this.Body != null) - { - var json = await this.SerializerProvider.GetJsonSerializers().First().SerializeAsync(this.Body, cancellationToken); - request.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); //todo: support other media types? - } - using var response = await this.HttpClient.SendAsync(request, cancellationToken); - if (response.StatusCode == HttpStatusCode.ServiceUnavailable)continue; - var rawContent = await response.Content.ReadAsByteArrayAsync(cancellationToken)!; - var contentString = null as string; - if (rawContent != null) contentString = Encoding.UTF8.GetString(rawContent); - if (!response.IsSuccessStatusCode) - { - this.Logger.LogError("Failed to execute the Open API operation '{operationId}' at '{uri}'. The remote server responded with a non-success status code '{statusCode}'.", this.Operation.OperationId, response.RequestMessage!.RequestUri, response.StatusCode); - this.Logger.LogDebug("Response content:\r\n{responseContent}", contentString ?? "None"); - response.EnsureSuccessStatusCode(contentString); - } - if (rawContent != null && rawContent.Length > 0) - { - var mediaType = response.Content?.Headers.ContentType?.MediaType; - if(string.IsNullOrWhiteSpace(mediaType)) this.Logger.LogWarning("Failed to determine the response's content type. Assuming {json}", MediaTypeNames.Application.Json); - var serializer = this.SerializerProvider.GetSerializersFor(mediaType).FirstOrDefault() ?? throw new NotSupportedException($"Failed to find a serializer for the specified media type '{mediaType}'"); - using var stream = new MemoryStream(rawContent!); - output = (await serializer.DeserializeAsync(stream, cancellationToken)).ToObject(); - } - success = true; - break; - } - output ??= new(); - if (!success)throw new HttpRequestException($"Failed to execute the operation activity '{this.Operation.OperationId}' (id: '{this.Activity.Id}'): No service available", null, HttpStatusCode.ServiceUnavailable); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - this.HttpClient.Dispose(); - base.Dispose(disposing); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/OperationStateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/OperationStateProcessor.cs deleted file mode 100644 index fa5539a8b..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/OperationStateProcessor.cs +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Serialization.Converters; - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process s - /// - public class OperationStateProcessor - : StateProcessor - { - - /// - public OperationStateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IJsonSerializer jsonSerializer, IOptions options, V1WorkflowActivity activity, OperationStateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - this.JsonSerializer = jsonSerializer; - } - - /// - /// Gets the service used to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var processor = (ActionProcessor)base.CreateProcessorFor(activity); - processor.SubscribeAsync - ( - async e => await this.OnNextActionAsync(processor, e), - async ex => await this.OnActionFaultedAsync(processor, ex), - async () => await this.OnActionCompletedAsync(processor) - ); - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - await this.StartAsync(); - else - await this.ResumeAsync(); - } - - /// - /// Starts the 's execution - /// - /// A new awaitable - protected virtual async Task StartAsync() - { - var input = null as object; - var metadata = null as Dictionary; - switch (this.State.ActionMode) - { - case ActionExecutionMode.Parallel: - foreach (var action in this.State.Actions) - { - input = await this.Context.FilterInputAsync(action, this.Activity.Input, this.CancellationTokenSource.Token); - metadata = new() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, action.Name! } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, this.CancellationTokenSource.Token); - } - break; - case ActionExecutionMode.Sequential: - var firstAction = this.State.Actions.First(); - input = await this.Context.FilterInputAsync(firstAction, this.Activity.Input, this.CancellationTokenSource.Token); - metadata = new() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, firstAction.Name! } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, this.CancellationTokenSource.Token); - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.State.ActionMode}' is not supported"); - } - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, this.CancellationTokenSource.Token)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - - /// - /// Resumes the 's execution - /// - /// A new awaitable - protected virtual async Task ResumeAsync() - { - var activities = await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, this.CancellationTokenSource.Token); - if (activities.Any()) - { - foreach (var activity in activities) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - else - { - switch (this.State.ActionMode) - { - case ActionExecutionMode.Parallel: - var output = await this.AggregateActionOutputsAsync(this.CancellationTokenSource.Token); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), this.CancellationTokenSource.Token); - await this.OnCompletedAsync(this.CancellationTokenSource.Token); - break; - case ActionExecutionMode.Sequential: - activities = await this.Context.Workflow.GetActivitiesAsync(this.Activity, this.CancellationTokenSource.Token); - var lastActivity = activities.LastOrDefault(a => a.Type == V1WorkflowActivityType.Action); - if (lastActivity == null) - { - await this.StartAsync(); - return; - } - if (!this.State.TryGetAction(lastActivity.Metadata, out var lastAction)) - throw new NullReferenceException($"Failed to find an action that matches the metadata specified by the activity with id '{lastActivity.Id}'"); - if (!this.State.TryGetNextAction(lastAction.Name!, out var nextAction)) - { - output = await this.AggregateActionOutputsAsync(this.CancellationTokenSource.Token); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), this.CancellationTokenSource.Token); - await this.OnCompletedAsync(this.CancellationTokenSource.Token); - return; - } - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, nextAction.Name! } - }; - var activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, await this.Context.FilterInputAsync(nextAction, lastActivity.Output, this.CancellationTokenSource.Token), metadata, this.Activity, this.CancellationTokenSource.Token); -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.State.ActionMode}' is not supported"); - } - } - - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (IWorkflowActivityProcessor processor in this.Processors.ToList()) - { - await processor.ProcessAsync(cancellationToken); - } - } - - /// - /// Aggregates the output of the completed actions activities that belongs to the processed - /// - /// A - /// The result of the action outputs aggregation - protected virtual async Task AggregateActionOutputsAsync(CancellationToken cancellationToken) - { - var output = this.Activity.Input.ToObject(); - foreach (var activity in (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Action && a.Status == V1WorkflowActivityStatus.Completed) - .OrderBy(a => a.ExecutedAt)) - { - if (activity.Output == null) - continue; - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Action, out var actionName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Action}'"); - if (!this.State.TryGetAction(actionName, out var action)) - throw new NullReferenceException($"Failed to find an action with name '{actionName}' in the operation state with name '{this.State.Name}'"); - if (action.UseResults()) - { - var expression = action.ActionDataFilter?.ToStateData?.Trim(); - var json = JsonConvert.SerializeObject(activity.Output, new JsonSerializerSettings() { Converters = new[] { new FilteredExpandoObjectConverter() }, DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore }); - if (string.IsNullOrWhiteSpace(expression)) - { - expression = json; - } - else - { - if (expression.StartsWith("${")) - expression = expression[2..^1]; - expression = $"{expression} = {json}"; - } - output = await this.Context.EvaluateAsync(expression, output, cancellationToken); - } - } - return output!; - } - - /// - /// Handles s - /// - /// The to handle the for - /// The to handle - /// A new awaitable - protected virtual async Task OnNextActionAsync(IActionProcessor processor, IV1WorkflowActivityIntegrationEvent e) - { - switch (e) - { - case V1WorkflowActivitySkippedIntegrationEvent: - case V1WorkflowActivityCompletedIntegrationEvent: - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - var output = null as object; - switch (this.State.ActionMode) - { - case ActionExecutionMode.Parallel: - var activities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, this.CancellationTokenSource.Token)) - .Where(a => a.Type == V1WorkflowActivityType.Action) - .ToList(); - if (activities.All(a => a.Status >= V1WorkflowActivityStatus.Faulted)) - { - output = await this.AggregateActionOutputsAsync(this.CancellationTokenSource.Token); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), this.CancellationTokenSource.Token); - } - break; - case ActionExecutionMode.Sequential: - output = await this.AggregateActionOutputsAsync(this.CancellationTokenSource.Token); - if (processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Action, out var currentActionName) - && this.State.TryGetNextAction(currentActionName, out ActionDefinition nextAction)) - { - var input = await this.Context.FilterInputAsync(nextAction, output, this.CancellationTokenSource.Token); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Action, nextAction.Name! } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Action, input, metadata, this.Activity, this.CancellationTokenSource.Token); - } - else - { - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), this.CancellationTokenSource.Token); - } - break; - default: - throw new NotSupportedException($"The specified {nameof(ActionExecutionMode)} '{this.State.ActionMode}' is not supported"); - } - } - break; - } - } - - /// - /// Handles an 's - /// - /// The that has thrown the specified - /// The thrown - /// A new awaitable - protected virtual async Task OnActionFaultedAsync(ActionProcessor processor, Exception ex) - { - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - foreach (var childProcessor in this.Processors) - { - if (childProcessor == processor) - continue; - await childProcessor.TerminateAsync(this.CancellationTokenSource.Token); - this.Processors.TryRemove(processor); - childProcessor.Dispose(); - } - await base.OnErrorAsync(ex, this.CancellationTokenSource.Token); - } - } - - /// - /// Handles the completion of the specified - /// - /// The that has completed - /// A new awaitable - protected virtual async Task OnActionCompletedAsync(ActionProcessor processor) - { - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - var activities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, this.CancellationTokenSource.Token)) - .Where(a => a.Type == V1WorkflowActivityType.Action); - if (activities.All(a => a.Status >= V1WorkflowActivityStatus.Faulted)) - { - await base.OnCompletedAsync(this.CancellationTokenSource.Token); - return; - } - foreach (var activity in activities - .Where(p => p.Status == V1WorkflowActivityStatus.Pending)) - { - var nextProcessor = this.CreateProcessorFor(activity); - await nextProcessor.ProcessAsync(this.CancellationTokenSource.Token); - } - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/ParallelStateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/ParallelStateProcessor.cs deleted file mode 100644 index d0321e802..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/ParallelStateProcessor.cs +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process s - /// - public class ParallelStateProcessor - : StateProcessor - { - - /// - public ParallelStateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, ParallelStateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - - } - - /// - /// Gets a boolean indicating whether or not child s have been executed according to the configured - /// - protected bool BranchesExecuted { get; private set; } - - /// - /// Gets a boolean indicating whether or not child s have been completed according to the configured - /// - protected bool BranchesCompleted { get; private set; } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var cancellationToken = this.CancellationTokenSource.Token; - var processor = (BranchProcessor)base.CreateProcessorFor(activity); - processor.OfType().SubscribeAsync - ( - async e => await this.OnBranchExecutedAsync(processor, e, cancellationToken), - async ex => await this.OnErrorAsync(ex, cancellationToken), - async () => await this.OnBranchCompletedAsync(processor, cancellationToken) - ); - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - { - foreach (var branch in this.State.Branches) - { - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name }, - { V1WorkflowActivityMetadata.Branch, branch.Name } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Branch, this.Activity.Input!.ToObject()!, metadata, this.Activity, cancellationToken); - } - } - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - foreach (var processor in this.Processors) - { - await processor.ProcessAsync(cancellationToken); - } - } - - /// - /// Handles the 's execution - /// - /// The that has produced the to process - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnBranchExecutedAsync(BranchProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) - { - if (this.BranchesExecuted) - return; - var output = null as object; - var childActivities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Branch) - .ToList(); - var executed = false; - switch (this.State.CompletionType) - { - case ParallelCompletionType.AtLeastN: - var executedActivities = childActivities - .Where(p => p.Status >= V1WorkflowActivityStatus.Faulted) - .ToList(); - if (executedActivities.Count() >= this.State.N) - { - executed = true; - output = new(); - foreach (var executedActivity in executedActivities) - { - output = output.Merge(executedActivity.Output!.ToObject()!); - } - } - break; - case ParallelCompletionType.AllOf: - if (childActivities.All(p => p.Status >= V1WorkflowActivityStatus.Faulted)) - { - executed = true; - output = new(); - foreach (var executedActivity in childActivities) - { - output = output.Merge(executedActivity.Output!.ToObject()!); - } - } - break; - default: - throw new NotSupportedException($"The specified {nameof(ParallelCompletionType)} '{this.State.CompletionType}' is not supported"); - } - if (!executed) - return; - using (await this.Lock.LockAsync(cancellationToken)) - { - if (this.BranchesExecuted) - return; - else - this.BranchesExecuted = true; - } - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - } - - /// - /// Handles the completion of the specified - /// - /// The to handle the completion of - /// A - /// A new awaitable - protected virtual async Task OnBranchCompletedAsync(BranchProcessor processor, CancellationToken cancellationToken) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - if (this.BranchesCompleted) - return; - using (await this.Lock.LockAsync(cancellationToken)) - { - var childActivities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) - .Where(a => a.Type == V1WorkflowActivityType.Branch) - .ToList(); - var completed = false; - switch (this.State.CompletionType) - { - case ParallelCompletionType.AllOf: - completed = childActivities.All(p => p.Status >= V1WorkflowActivityStatus.Faulted); - break; - case ParallelCompletionType.AtLeastN: - if (childActivities.Where(p => p.Status >= V1WorkflowActivityStatus.Faulted).Count() >= this.State.N) - completed = true; - break; - default: - throw new NotSupportedException($"The specified {nameof(ParallelCompletionType)} '{this.State.CompletionType}' is not supported"); - } - if (!completed) - return; - if (completed && !this.BranchesCompleted) - this.BranchesCompleted = true; - else - return; - } - await this.OnCompletedAsync(cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/ProduceEventProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/ProduceEventProcessor.cs deleted file mode 100644 index c59216d8d..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/ProduceEventProcessor.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Net.Mime; - -namespace Synapse.Worker.Services.Processors -{ - ///

- /// Represents a used to process s to produce - /// - public class ProduceEventProcessor - : WorkflowActivityProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to publish and subscribe to integration events - /// The service used to access the current - /// The to process - /// The that defines the event to produce - public ProduceEventProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IIntegrationEventBus integrationEventBus, IOptions options, V1WorkflowActivity activity, EventDefinition eventDefinition) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.IntegrationEventBus = integrationEventBus; - this.EventDefinition = eventDefinition; - } - - /// - /// Gets the service used to publish and subscribe to integration events - /// - protected IIntegrationEventBus IntegrationEventBus { get; } - - /// - /// Gets the that defines the event to produce - /// - public EventDefinition EventDefinition { get; } - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - try - { - var e = new V1Event() - { - Id = Guid.NewGuid().ToString(), - Type = this.EventDefinition.Type, - Source = new(this.EventDefinition.Source!), - DataContentType = MediaTypeNames.Application.Json, - Data = this.Activity.Input, - ExtensionAttributes = new() - }; - if (this.EventDefinition.Correlations != null) - { - foreach (var correlation in this.EventDefinition.Correlations) - { - var value = correlation.ContextAttributeValue; - if (string.IsNullOrWhiteSpace(value)) - { - if (e.TryGetAttribute(correlation.ContextAttributeName, out value)) - await this.Context.Workflow.SetCorrelationMappingAsync(correlation.ContextAttributeName, value, cancellationToken); - } - else - { - if (value.IsRuntimeExpression()) - value = (await this.Context.EvaluateAsync(value, await this.Context.Workflow.GetActivityStateDataAsync(this.Activity, cancellationToken), cancellationToken)!)!.ToString(); - await this.Context.Workflow.SetCorrelationMappingAsync(correlation.ContextAttributeName, value!, cancellationToken); - } - e.ExtensionAttributes.Add(correlation.ContextAttributeName, Dynamic.FromObject(value)); - } - } - - await this.Context.PublishEventAsync(e, cancellationToken); - - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/RuntimeExpressionFunctionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/RuntimeExpressionFunctionProcessor.cs deleted file mode 100644 index 1e2e71973..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/RuntimeExpressionFunctionProcessor.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the used to process runtime expression-based s - /// - public class RuntimeExpressionFunctionProcessor - : FunctionProcessor - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - /// The to process - /// The to process - public RuntimeExpressionFunctionProcessor(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, - IWorkflowActivityProcessorFactory activityProcessorFactory, IOptions options, V1WorkflowActivity activity, - ActionDefinition action, FunctionDefinition function) - : base(serviceProvider, loggerFactory, context, activityProcessorFactory, options, activity, action, function) - { - - } - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - await base.ProcessAsync(cancellationToken); - if (this.Activity.Status == V1WorkflowActivityStatus.Skipped) - return; - var output = await this.Context.EvaluateAsync(this.Function.Operation, this.Activity.Input.ToObject(), cancellationToken); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/SleepStateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/SleepStateProcessor.cs deleted file mode 100644 index 27747d256..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/SleepStateProcessor.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process s - /// - public class SleepStateProcessor - : StateProcessor - { - - /// - public SleepStateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, SleepStateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - - } - - /// - /// Gets the sleep duration - /// - protected TimeSpan Duration { get; private set; } - - /// - /// Gets the used to delay the 's execution - /// - protected Timer Timer { get; set; } = null!; - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Suspended) - this.Duration = this.State.Duration.Subtract(this.Context.Workflow.Instance.Sessions.Last(s => !s.IsActive).EndedAt!.Value.Subtract(this.Activity.StartedAt!.Value)); - else - this.Duration = this.State.Duration; - return Task.CompletedTask; - } - - /// - protected override Task ProcessAsync(CancellationToken cancellationToken) - { - var duration = this.Duration; //todo: substract activity's total execution time - this.Timer = new(async (state) => await this.OnDelayElapsedAsync(cancellationToken), duration, duration, Timeout.InfiniteTimeSpan); - return Task.CompletedTask; - } - - /// - /// Handles the event fired when the 's has elapsed - /// - /// A - /// - protected virtual async Task OnDelayElapsedAsync(CancellationToken cancellationToken) - { - await this.Timer.DisposeAsync(); - this.Timer = null!; - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - this.Timer?.Dispose(); - base.Dispose(disposing); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/StartProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/StartProcessor.cs deleted file mode 100644 index 2c02a358a..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/StartProcessor.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* -* Copyright © 2022-Present The Synapse Authors -*

-* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -*

-* http://www.apache.org/licenses/LICENSE-2.0 -*

-* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -*/ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents an used to process s - /// - public class StartProcessor - : WorkflowActivityProcessor - { - - /// - public StartProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, StartDefinition start) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.Start = start; - } - - /// - /// Gets the to process - /// - public StartDefinition? Start { get; } - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - //TODO - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/StateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/StateProcessor.cs deleted file mode 100644 index cb51d4b08..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/StateProcessor.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the base class for all s that process s - /// - public abstract class StateProcessor - : WorkflowActivityProcessor, IStateProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - /// The to process - protected StateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, IOptions options, V1WorkflowActivity activity, StateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.State = state; - } - - /// - /// Gets the to process - /// - public virtual StateDefinition State { get; } - - /// - protected override async Task OnNextAsync(IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken) - { - if (e is V1WorkflowActivityCompletedIntegrationEvent completedEvent) - await base.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, await this.Context.FilterOutputAsync(this.State, completedEvent.Output!.ToObject()!, cancellationToken)), cancellationToken); - else - await base.OnNextAsync(e, cancellationToken); - } - - } - - /// - /// Represents the base class for all s that process s - /// - /// The type of to process - public abstract class StateProcessor - : StateProcessor - where TState : StateDefinition - { - - /// - protected StateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, IOptions options, V1WorkflowActivity activity, TState state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - } - - /// - /// Gets the to process - /// - public virtual new TState State => (TState)base.State; - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/SubflowProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/SubflowProcessor.cs deleted file mode 100644 index 766f74367..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/SubflowProcessor.cs +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Worker.Services.Processors -{ - ///

- /// Represents an used to process subflows - /// - public class SubflowProcessor - : ActionProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to publish and subscribe to integration events - /// The service used to access the current - /// The to process - /// The the to process belongs to - /// The to process - public SubflowProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IIntegrationEventBus integrationEventBus, IOptions options, V1WorkflowActivity activity, StateDefinition state, ActionDefinition action) - : base(loggerFactory, context, activityProcessorFactory, options, activity, action) - { - this.IntegrationEventBus = integrationEventBus; - this.State = state; - } - - /// - /// Gets the service used to publish and subscribe to integration events - /// - protected IIntegrationEventBus IntegrationEventBus { get; } - - /// - /// Gets the subscription - /// - protected IDisposable Subscription { get; private set; } = null!; - - /// - /// Gets the the to process belongs to - /// - protected StateDefinition State { get; } - - /// - /// Gets the to process - /// - protected SubflowReference Subflow => this.Action.Subflow!; - - /// - /// Gets the used to monitor the time limit before which the current should go into - /// - protected Timer IdleTimer { get; private set; } = null!; - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - if (!this.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Subflow, out var workflowInstanceId)) - { - workflowInstanceId = await this.Context.Workflow.StartSubflowAsync($"{this.Subflow.WorkflowId}:{this.Subflow.Version}", this.Activity.Input, cancellationToken); - this.Activity.Metadata.Add(V1WorkflowActivityMetadata.Subflow, workflowInstanceId); - await this.Context.Workflow.SetActivityMetadataAsync(this.Activity, cancellationToken); - } - switch (this.Subflow.InvocationMode) - { - case InvocationMode.Asynchronous: - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input), this.CancellationTokenSource.Token); - await this.OnCompletedAsync(this.CancellationTokenSource.Token); - break; - case InvocationMode.Synchronous: - var eventDefinition = new EventDefinition() - { - Kind = EventKind.Consumed, - Name = "onSubflowExecuted", - Source = CloudEvents.Source.ToString(), - Type = CloudEvents.TypeOf(typeof(V1WorkflowInstanceExecutedIntegrationEvent), typeof(V1WorkflowInstance)), - Correlations = new() - { - new() { ContextAttributeName = nameof(CloudEvent.Subject).ToLower(), ContextAttributeValue = workflowInstanceId } - } - }; - var e = await this.Context.Workflow.ConsumeOrBeginCorrelateEventAsync(eventDefinition, cancellationToken); - if (e != null) - { - await this.OnEventAsync(e); - return; - } - this.Subscription = this.IntegrationEventBus.InboundStream - .Where(e => e.Subject == workflowInstanceId && e.Source?.ToString() == eventDefinition.Source && e.Type == eventDefinition.Type) - .SubscribeAsync(this.OnEventAsync); - if (this.Options.Correlation.Timeout.HasValue) - this.IdleTimer = new Timer(async state => await this.OnIdleTimerExpiredAsync(state, cancellationToken), null, this.Options.Correlation.Timeout.Value, this.Options.Correlation.Timeout.Value); - break; - default: - throw new NotSupportedException($"The specified {nameof(InvocationMode)} '{this.Subflow.InvocationMode}' is not supported"); - } - } - - /// - /// Switches to passive correlation mode - /// - /// A - /// A new awaitable - protected virtual async Task SwitchToPassiveModeAsync(CancellationToken cancellationToken) - { - using (await this.Lock.LockAsync(cancellationToken)) - { - //todo: passive correlation mode: await this.Context.WaitForEventsAsync(new[] { this.EventDefinition }, V1TriggerCorrelationMode.Exclusive, V1TriggerConditionType.AllOf, cancellationToken); - } - } - - /// - /// Handles the expiration of the iddle - /// - /// The 's state - /// A - /// A new awaitable - protected virtual async Task OnIdleTimerExpiredAsync(object? state, CancellationToken cancellationToken) - { - this.IdleTimer.Dispose(); - this.IdleTimer = null!; - if (this.Options.Correlation.Mode == RuntimeCorrelationMode.Dual) - { - await this.SwitchToPassiveModeAsync(cancellationToken); - } - else - { - //TODO - //await this.OnTimeoutAsync(); - } - } - - /// - /// Handles an incoming - /// - /// The to handle - /// A new awaitable - protected virtual async Task OnEventAsync(CloudEvent e) - { - using (await this.Lock.LockAsync(this.CancellationTokenSource.Token)) - { - if (this.IdleTimer != null) - { - this.IdleTimer.Dispose(); - this.IdleTimer = null!; - } - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, e.Data), this.CancellationTokenSource.Token); - await this.OnCompletedAsync(this.CancellationTokenSource.Token); - } - } - - /// - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - this.IdleTimer?.Dispose(); - this.Subscription?.Dispose(); - this.Subscription?.Dispose(); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/SwitchStateProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/SwitchStateProcessor.cs deleted file mode 100644 index 1cda9260f..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/SwitchStateProcessor.cs +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents a used to process s - /// - public class SwitchStateProcessor - : StateProcessor - { - - /// - /// Gets the name of the default - /// - public const string DefaultConditionName = "default"; - - /// - public SwitchStateProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, SwitchStateDefinition state) - : base(loggerFactory, context, activityProcessorFactory, options, activity, state) - { - - } - - /// - protected override IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - var cancellationToken = this.CancellationTokenSource.Token; - var processor = (ConsumeEventProcessor)base.CreateProcessorFor(activity); - processor.OfType() - .SubscribeAsync - ( - async e => await this.OnConsumeEventResultAsync(processor, e, cancellationToken), - async ex => await this.OnErrorAsync(ex, cancellationToken) - ); - return processor; - } - - /// - protected override async Task InitializeAsync(CancellationToken cancellationToken) - { - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - { - switch (this.State.SwitchType) - { - case SwitchStateType.Data: - //Do nothing - break; - case SwitchStateType.Event: - foreach (var eventConditionDefinition in this.State.EventConditions!) - { - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, this.State.Name! }, - { V1WorkflowActivityMetadata.Event, eventConditionDefinition.Event } - }; - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.ConsumeEvent, this.Activity.Input!.ToObject()!, metadata, this.Activity, cancellationToken); - } - break; - default: - throw new NotSupportedException($"The specified switch state '{this.State.SwitchType}' is not supported"); - } - } - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.Activity, cancellationToken)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateProcessorFor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - switch (this.State.SwitchType) - { - case SwitchStateType.Data: - bool caseMatched = false; - foreach (var caseDefinition in State.DataConditions!) - { - if (await this.Context.EvaluateConditionAsync(caseDefinition.Condition, this.Activity.Input!.ToObject()!, cancellationToken)) - { - await this.OnNextAsync(caseDefinition.Name!, cancellationToken); - caseMatched = true; - } - } - if (!caseMatched) - await this.OnNextAsync(DefaultConditionName, cancellationToken); - break; - case SwitchStateType.Event: - foreach (var processor in this.Processors) - { - await processor.ProcessAsync(cancellationToken); - } - break; - default: - throw new NotSupportedException($"The specified switch state '{this.State.SwitchType}' is not supported"); - } - } - - /// - /// Produces the 's - /// - /// The matching 's name - /// A - /// A new awaitable - protected virtual async ValueTask OnNextAsync(string caseName, CancellationToken cancellationToken) - { - this.Activity.Metadata[V1WorkflowActivityMetadata.Case] = caseName; - await this.Context.Workflow.SetActivityMetadataAsync(this.Activity, cancellationToken); - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - /// - /// Handles the specified - /// - /// The that has produced the specified - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnConsumeEventResultAsync(ConsumeEventProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - foreach (var childProcessor in this.Processors) - { - await childProcessor.TerminateAsync(cancellationToken); - this.Processors.TryRemove(childProcessor); - childProcessor.Dispose(); - } - if (!this.State.TryGetEventCase(processor.EventDefinition.Name, out var eventCase)) - throw new NullReferenceException($"Failed to find an event case definition that matches the event with the specified name '{processor.EventDefinition.Name}'"); - await this.OnNextAsync(eventCase.Name!, cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/Processors/TransitionProcessor.cs b/src/apps/Synapse.Worker/Services/Processors/TransitionProcessor.cs deleted file mode 100644 index f6a62bc6e..000000000 --- a/src/apps/Synapse.Worker/Services/Processors/TransitionProcessor.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Worker.Services.Processors -{ - - ///

- /// Represents the base class for all s used to process s - /// - public class TransitionProcessor - : WorkflowActivityProcessor, ITransitionProcessor - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - /// The to process the transition of - /// The to process - public TransitionProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, - IOptions options, V1WorkflowActivity activity, StateDefinition state, TransitionDefinition transition) - : base(loggerFactory, context, activityProcessorFactory, options, activity) - { - this.State = state; - this.Transition = transition; - } - - /// - /// Gets the to process the transition of - /// - public StateDefinition State { get; } - - /// - /// Gets the to process - /// - public TransitionDefinition Transition { get; } - - /// - protected override Task InitializeAsync(CancellationToken cancellationToken) - { - //TODO - return Task.CompletedTask; - } - - /// - protected override async Task ProcessAsync(CancellationToken cancellationToken) - { - //TODO - await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, this.Activity.Input!.ToObject()!), cancellationToken); - await this.OnCompletedAsync(cancellationToken); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/WorkflowActivityProcessor.cs b/src/apps/Synapse.Worker/Services/WorkflowActivityProcessor.cs deleted file mode 100644 index 6da683b5f..000000000 --- a/src/apps/Synapse.Worker/Services/WorkflowActivityProcessor.cs +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ConcurrentCollections; -using System.Reactive.Subjects; - -namespace Synapse.Worker.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public abstract class WorkflowActivityProcessor - : IWorkflowActivityProcessor - { - - private bool _Disposed; - - /// - /// Initializes a new - /// - /// The service used to create s - /// The current - /// The service used to create s - /// The service used to access the current - /// The to process - protected WorkflowActivityProcessor(ILoggerFactory loggerFactory, IWorkflowRuntimeContext context, IWorkflowActivityProcessorFactory activityProcessorFactory, IOptions options, V1WorkflowActivity activity) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Context = context; - this.ActivityProcessorFactory = activityProcessorFactory; - this.Options = options.Value; - this.Activity = activity; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the current - /// - protected IWorkflowRuntimeContext Context { get; } - - /// - /// Gets the service used to create s - /// - protected IWorkflowActivityProcessorFactory ActivityProcessorFactory { get; } - - /// - /// Gets the current - /// - protected ApplicationOptions Options { get; } - - /// - public V1WorkflowActivity Activity { get; } - - /// - /// Gets the used to observe the 's execution - /// - protected Subject Subject { get; } = new(); - - /// - /// Gets a containing all child s - /// - protected ConcurrentHashSet Processors { get; } = new ConcurrentHashSet(); - - /// - /// Gets the 's - /// - protected CancellationTokenSource CancellationTokenSource { get; private set; } = null!; - - /// - /// Gets the object used to asynchronously lock the - /// - protected AsyncLock Lock { get; } = new AsyncLock(); - - Task IWorkflowActivityProcessor.ProcessAsync(CancellationToken cancellationToken) - { - this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - Task.Factory.StartNew(async () => - { - try - { - this.Logger.LogInformation("Initializing activity '{activityId}' (type: '{activityType}')...", this.Activity.Id, this.Activity.Type); - if (this.Activity.Status == V1WorkflowActivityStatus.Pending) - await this.Context.Workflow.InitializeActivityAsync(this.Activity, cancellationToken); - await this.InitializeAsync(this.CancellationTokenSource.Token); - this.Logger.LogInformation("Activity '{activityId}' (type: '{activityType}') initialized", this.Activity.Id, this.Activity.Type); - this.Logger.LogInformation("Starting/resuming activity '{activityId}' (type: '{activityType}')...", this.Activity.Id, this.Activity.Type); - await this.Context.Workflow.StartOrResumeActivityAsync(this.Activity, this.CancellationTokenSource.Token); - await this.ProcessAsync(this.CancellationTokenSource.Token); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - }); - return Task.CompletedTask; - } - - /// - /// Initializes the - /// - /// A - /// A new awaitable - protected abstract Task InitializeAsync(CancellationToken cancellationToken); - - /// - /// Executes the - /// - /// A - /// A new awaitable - protected abstract Task ProcessAsync(CancellationToken cancellationToken); - - async Task IWorkflowActivityProcessor.SuspendAsync(CancellationToken cancellationToken) - { - this.Logger.LogInformation("Suspending activity '{activityId}' (type: '{activityType}')...", this.Activity.Id, this.Activity.Type); - var tasks = new List(this.Processors.Count); - foreach (var processor in this.Processors) - { - tasks.Add(processor.SuspendAsync(cancellationToken)); - } - try { await Task.WhenAll(tasks); } catch { } - await this.Context.Workflow.SuspendActivityAsync(this.Activity, this.CancellationTokenSource.Token); - await this.SuspendAsync(cancellationToken); - this.Logger.LogInformation("Activity '{activityId}' (type: '{activityType}') suspended", this.Activity.Id, this.Activity.Type); - } - - /// - /// Suspends the 's processing - /// - /// A - /// A new awaitable - protected virtual Task SuspendAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - async Task IWorkflowActivityProcessor.TerminateAsync(CancellationToken cancellationToken) - { - this.Logger.LogInformation("Terminating activity '{activityId}' (type: '{activityType}')...", this.Activity.Id, this.Activity.Type); - var tasks = new List(this.Processors.Count); - foreach (var processor in this.Processors) - { - tasks.Add(processor.TerminateAsync(cancellationToken)); - } - try { await Task.WhenAll(tasks); } catch { } - await this.Context.Workflow.CancelActivityAsync(this.Activity, this.CancellationTokenSource.Token); - await this.TerminateAsync(cancellationToken); - this.CancellationTokenSource?.Cancel(); - this.Logger.LogInformation("Activity '{activityId}' (type: '{activityType}') terminated", this.Activity.Id, this.Activity.Type); - } - - /// - /// Terminates the 's execution - /// - /// A - /// A new awaitable - protected virtual Task TerminateAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - /// - public IDisposable Subscribe(IObserver observer) - { - return this.Subject.Subscribe(observer); - } - - /// - /// Creates a new child for the specified - /// - /// The to create a child for - protected virtual IWorkflowActivityProcessor CreateProcessorFor(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - IWorkflowActivityProcessor processor = this.ActivityProcessorFactory.Create(activity); - this.Processors.Add(processor); - return processor; - } - - /// - /// Handles the 's - /// - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnNextAsync(IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken) - { - try - { - if (e is V1WorkflowActivityCompletedIntegrationEvent) - this.Logger.LogInformation("Activity '{activityId}' (type: '{activityType}') completed", this.Activity.Id, this.Activity.Type); - await this.Context.Workflow.On(this.Activity, e, cancellationToken); - this.Subject.OnNext(e); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - } - - /// - /// Handles the specified that occured during the 's processing - /// - /// The to handle - /// A - /// A new awaitable - protected virtual async Task OnErrorAsync(Exception ex, CancellationToken cancellationToken) - { - try - { - this.Logger.LogWarning("An error occured while executing the activity '{activityId}' (type: '{activityType}')/r/nDetails:/r/n{ex}", this.Activity.Id, this.Activity.Type, ex.ToString()); - await this.Context.Workflow.FaultActivityAsync(this.Activity, ex, cancellationToken); - this.Subject.OnError(ex); - } - catch (Exception cex) - { - this.Logger.LogError("A critical exception occured while faulting the execution of the activity '{activityId}'(type: '{activityType}'):/r/n{ex}", this.Activity.Id, this.Activity.Type, cex.ToString()); - } - } - - /// - /// Handles the 's completion - /// - /// A - /// A new awaitable - protected virtual async Task OnCompletedAsync(CancellationToken cancellationToken) - { - try - { - this.Processors.ToList().ForEach(p => p.Dispose()); - this.Processors.Clear(); - this.Subject.OnCompleted(); - } - catch (Exception ex) - { - await this.OnErrorAsync(ex, cancellationToken); - } - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.CancellationTokenSource?.Dispose(); - this.Processors?.ToList().ForEach(p => p.Dispose()); - this.Processors?.Clear(); - this.Subject?.Dispose(); - } - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/WorkflowActivityProcessorFactory.cs b/src/apps/Synapse.Worker/Services/WorkflowActivityProcessorFactory.cs deleted file mode 100644 index 36b2172a8..000000000 --- a/src/apps/Synapse.Worker/Services/WorkflowActivityProcessorFactory.cs +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection; -using Synapse.Worker.Services.Processors; - -namespace Synapse.Worker.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class WorkflowActivityProcessorFactory - : IWorkflowActivityProcessorFactory - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to perform logging - /// The current - public WorkflowActivityProcessorFactory(IServiceProvider serviceProvider, ILogger logger, IWorkflowRuntimeContext context) - { - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.Context = context; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the current - /// - protected IWorkflowRuntimeContext Context { get; } - - /// - public virtual IWorkflowActivityProcessor Create(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.State, out var stateName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.State}'"); - if (!this.Context.Workflow.Definition.TryGetState(stateName, out var state)) - throw new NullReferenceException($"Failed to find the workflow state with the specified name '{stateName}'"); - try - { - return activity.Type switch - { - V1WorkflowActivityType.Action => this.CreateActionActivityProcessor(state, activity), - V1WorkflowActivityType.Branch => this.CreateBranchActivityProcessor(state, activity), - V1WorkflowActivityType.ConsumeEvent => this.CreateConsumeEventActivityProcessor(activity), - V1WorkflowActivityType.End => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, state.End ?? new()), - V1WorkflowActivityType.Error => throw new NotImplementedException(),//todo - V1WorkflowActivityType.EventTrigger => this.CreateEventStateTriggerActivityProcessor(state, activity), - V1WorkflowActivityType.Iteration => this.CreateIterationActivityProcessor(state, activity), - V1WorkflowActivityType.ProduceEvent => this.CreateProduceEventActivityProcessor(activity), - V1WorkflowActivityType.Start => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, this.Context.Workflow.Definition.Start ?? new()), - V1WorkflowActivityType.State => this.CreateStateActivityProcessor(state, activity), - V1WorkflowActivityType.SubFlow => this.CreateSubflowActivityProcessor(state, activity), - V1WorkflowActivityType.Transition => this.CreateTransitionActivityProcessor(state, activity), - _ => throw new NotSupportedException($"The specified {typeof(V1WorkflowActivityType).Name} '{activity.Type}' is not supported"), - }; - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while creating a new processor for the activity with id '{activityId}': {ex}", activity.Id, ex.ToString()); - throw; - } - } - - /// - /// Creates a new - /// - /// The to create a new for - /// The that describe the to process - /// A new - protected virtual IStateProcessor CreateStateActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - return state switch - { - //CallbackStateDefinition callbackState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), //todo - EventStateDefinition eventState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), - ForEachStateDefinition forEachState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), - InjectStateDefinition injectState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), - OperationStateDefinition operationState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), - ParallelStateDefinition parallelState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), - SleepStateDefinition delayState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), - SwitchStateDefinition switchState => ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity), - _ => throw new NotSupportedException($"The specified {nameof(StateDefinition)} type '{state.GetType().Name}' is not supported"), - }; - } - - /// - /// Creates a new for an of type - /// - /// The that defines the action to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateActionActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - if (!state.TryGetAction(activity.Metadata, out var action)) - throw new NullReferenceException($"Failed to find an action that matches the metadata specified by the activity with id '{activity.Id}'"); - return action.Type switch - { - ActionType.Function => this.CreateFunctionActivityProcessor(state, activity), - ActionType.Subflow => this.CreateSubflowActivityProcessor(state, activity), - ActionType.Trigger => this.CreateAsyncFunctionActivityProcessor(state, activity), - _ => throw new NotSupportedException($"The specified {typeof(ActionType).Name} '{action.Type}' is not supported"), - }; - } - - /// - /// Creates a new for an of type - /// - /// The that defines the action to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateFunctionActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - if (!state.TryGetAction(activity.Metadata, out var action)) - throw new NullReferenceException($"Failed to find an action that matches the metadata specified by the activity with id '{activity.Id}'"); - if (!this.Context.Workflow.Definition.TryGetFunction(action.Function!.RefName, out var function)) - throw new NullReferenceException($"Failed to find a function with the specified name '{action.Function.RefName}' in the workflow with name '{this.Context.Workflow.Definition.Id}' and version '{this.Context.Workflow.Definition.Version}'"); - return function.Type switch - { - FunctionType.AsyncApi => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, action, function), - FunctionType.Expression => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, action, function), - FunctionType.GraphQL => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, action, function), - FunctionType.OData => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, action, function), - //FunctionType.OpenApi => //todo: move 'rest' code here, and implement basic rest type, for when the Serverless Workflow spec supports it - FunctionType.Rest => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, action, function), - FunctionType.Rpc => ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, action, function), - _ => throw new NotSupportedException($"The specified {nameof(FunctionType)} '{function.Type}' is not supported"), - }; - } - - /// - /// Creates a new for an of type - /// - /// The that defines the action to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateAsyncFunctionActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - if (!state.TryGetAction(activity.Metadata, out var action)) - throw new NullReferenceException($"Failed to find an action that matches the metadata specified by the activity with id '{activity.Id}'"); - if (!this.Context.Workflow.Definition.TryGetEvent(action.Event!.ProduceEvent, out var triggerEvent)) - throw new NullReferenceException($"Failed to find a produced event with the specified name '{action.Event!.ProduceEvent}' in the workflow with name '{this.Context.Workflow.Definition.Id}' and version '{this.Context.Workflow.Definition.Version}'"); - if (triggerEvent.Kind != EventKind.Produced) - throw new Exception($"The event '{action.Event!.ResultEvent}' is referenced as a produced event, but is defined as a consumed one"); - if (!this.Context.Workflow.Definition.TryGetEvent(action.Event!.ResultEvent, out var resultEvent)) - throw new NullReferenceException($"Failed to find a consumed event with the specified name '{action.Event!.ResultEvent}' in the workflow with name '{this.Context.Workflow.Definition.Id}' and version '{this.Context.Workflow.Definition.Version}'"); - if (resultEvent.Kind != EventKind.Consumed) - throw new Exception($"The event '{action.Event!.ResultEvent}' is referenced as a consumed event, but is defined as a produced one"); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity, action, triggerEvent, resultEvent); - } - - /// - /// Creates a new for an of type - /// - /// The that defines the action to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateSubflowActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - if (!state.TryGetAction(activity.Metadata, out var action)) - throw new NullReferenceException($"Failed to find an action that matches the metadata specified by the activity with id '{activity.Id}'"); - if (action.Type != ActionType.Subflow - || action.Subflow == null) - throw new InvalidCastException($"The action with name '{action.Name}' is not of type '{ActionType.Subflow}'"); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, state, activity, action); - } - - /// - /// Creates a new for an of type - /// - /// The that defines the action to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateEventStateTriggerActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - if (state is not EventStateDefinition eventState) - throw new ArgumentException($"The specified state definition with name '{state.Name}' is not the definition of an event state"); - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Trigger, out var rawTriggerId)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Trigger}'"); - if (!int.TryParse(rawTriggerId, out var triggerId)) - throw new ArgumentException($"The '{V1WorkflowActivityMetadata.Trigger}' metadata field of activity '{activity.Id}' is not a valid integer"); - if (!eventState.TryGetTrigger(triggerId, out var trigger)) - throw new NullReferenceException($"Failed to find a trigger at the specified index '{triggerId}' in the event state with name '{eventState.Name}'"); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, eventState, trigger); - } - - /// - /// Creates a new for an of type - /// - /// The that defines the to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateBranchActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - if (state is not ParallelStateDefinition parallelState) - throw new ArgumentException($"The specified state definition with name '{state.Name}' is not the definition of a parallel state"); - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Branch, out var branchName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Branch}'"); - if (!parallelState.TryGetBranch(branchName, out var branch)) - throw new NullReferenceException($"Failed to find a branch with the specified name '{branchName}' in the parallel state with name '{parallelState.Name}'"); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, parallelState, branch); - } - - /// - /// Creates a new for an of type - /// - /// The that defines the action to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateIterationActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - if (state is not ForEachStateDefinition foreachState) - throw new ArgumentException($"The specified state definition with name '{state.Name}' is not the definition of a foreach state"); - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Iteration, out var rawIterationIndex)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Iteration}'"); - if (!int.TryParse(rawIterationIndex, out var iterationIndex)) - throw new ArgumentException($"The '{V1WorkflowActivityMetadata.Iteration}' metadata field of activity '{activity.Id}' is not a valid integer"); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, foreachState, iterationIndex); - } - - /// - /// Creates a new for an of type - /// - /// The that defines the to process - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateTransitionActivityProcessor(StateDefinition state, V1WorkflowActivity activity) - { - activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.NextState, out var transitionTo); - TransitionDefinition? transition; - if (state is SwitchStateDefinition @switch) - { - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Case, out var caseName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Case}'"); - if (!@switch.TryGetCase(caseName, out SwitchCaseDefinition dataCondition)) - throw new NullReferenceException($"Failed to find a condition with the specified name '{caseName}'"); - transition = dataCondition.Transition; - if (transition == null) - { - if (string.IsNullOrWhiteSpace(transitionTo)) - transitionTo = dataCondition.TransitionToStateName!; - transition = new() { NextState = transitionTo }; - } - } - else - { - transition = state.Transition; - if (transition == null) - { - if (string.IsNullOrWhiteSpace(transitionTo)) - transitionTo = state.TransitionToStateName!; - transition = new() { NextState = transitionTo }; - } - - } - return ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, state, transition); - } - - /// - /// Creates a new for an of type - /// - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateProduceEventActivityProcessor(V1WorkflowActivity activity) - { - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Event, out var eventName)) - throw new NullReferenceException($"Failed to find the required '{V1WorkflowActivityMetadata.Event}' metadata"); - if (!this.Context.Workflow.Definition.TryGetEvent(eventName, out var e)) - throw new NullReferenceException($"Failed to find an action that matches the metadata specified by the activity with id '{activity.Id}'"); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, e); - } - - /// - /// Creates a new for an of type - /// - /// The that describe the to process - /// A new - protected virtual IWorkflowActivityProcessor CreateConsumeEventActivityProcessor(V1WorkflowActivity activity) - { - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Event, out var eventName)) - throw new NullReferenceException($"Failed to find the required '{V1WorkflowActivityMetadata.Event}' metadata"); - if (!this.Context.Workflow.Definition.TryGetEvent(eventName, out var e)) - throw new NullReferenceException($"Failed to find an action that matches the metadata specified by the activity with id '{activity.Id}'"); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, activity, e); - } - - IWorkflowActivityProcessor IWorkflowActivityProcessorFactory.Create(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - return this.Create(activity); - } - - IWorkflowActivityProcessor IWorkflowActivityProcessorFactory.Create(TActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - return (IWorkflowActivityProcessor)this.Create(activity); - } - - TProcessor IWorkflowActivityProcessorFactory.Create(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - return (TProcessor)this.Create(activity); - } - - TProcessor IWorkflowActivityProcessorFactory.Create(TActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - return (TProcessor)this.Create(activity); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/WorkflowFacade.cs b/src/apps/Synapse.Worker/Services/WorkflowFacade.cs deleted file mode 100644 index 64222e00d..000000000 --- a/src/apps/Synapse.Worker/Services/WorkflowFacade.cs +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Apis.Runtime; - -namespace Synapse.Worker.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class WorkflowFacade - : IWorkflowFacade - { - - /// - /// Initializes a new - /// - /// The current workflow's - /// The current - /// The service used to interact with the Synapse Runtime API - public WorkflowFacade(WorkflowDefinition definition, V1WorkflowInstance instance, ISynapseRuntimeApi synapseRuntimeApi) - { - this.Definition = definition; - this.Instance = instance; - this.SynapseRuntimeApi = synapseRuntimeApi; - } - - /// - public WorkflowDefinition Definition { get; } - - /// - public V1WorkflowInstance Instance { get; private set; } - - /// - /// Gets the service used to interact with the Synapse Runtime API - /// - protected ISynapseRuntimeApi SynapseRuntimeApi { get; } - - /// - public virtual async Task StartAsync(CancellationToken cancellationToken) - { - this.Instance = await this.SynapseRuntimeApi.StartAsync(this.Instance.Id, cancellationToken); - } - - /// - public virtual async Task ConsumeOrBeginCorrelateEventAsync(EventDefinition eventDefinition, CancellationToken cancellationToken = default) - { - if (eventDefinition == null) - throw new ArgumentNullException(nameof(eventDefinition)); - var e = await this.SynapseRuntimeApi.ConsumeOrBeginCorrelateEventAsync(new() { WorkflowInstanceId = this.Instance.Id, EventDefinition = eventDefinition }, cancellationToken); - if (e == null) - return null; - else - return e.ToCloudEvent(); - } - - /// - public virtual async Task SetCorrelationMappingAsync(string key, string value, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(key)) - throw new ArgumentNullException(nameof(key)); - if (string.IsNullOrWhiteSpace(value)) - throw new ArgumentNullException(nameof(value)); - await this.SynapseRuntimeApi.SetCorrelationMappingAsync(new() { Id = this.Instance.Id, Key = key, Value = value }, cancellationToken); - } - - /// - public virtual async Task TryCorrelateAsync(V1Event e, IEnumerable? mappingKeys, CancellationToken cancellationToken = default) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - if (mappingKeys == null) - mappingKeys = Array.Empty(); - return await this.SynapseRuntimeApi.TryCorrelateAsync(new() { WorkflowInstanceId = this.Instance.Id, Event = e, MappingKeys = mappingKeys }, cancellationToken); - } - - /// - public virtual async Task> GetActivitiesAsync(CancellationToken cancellationToken = default) - { - return await this.SynapseRuntimeApi.GetActivitiesAsync(this.Instance.Id, cancellationToken); - } - - /// - public virtual async Task> GetOperativeActivitiesAsync(CancellationToken cancellationToken) - { - return await this.SynapseRuntimeApi.GetOperativeActivitiesAsync(this.Instance.Id, cancellationToken); - } - - /// - public virtual async Task> GetActivitiesAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - return await this.SynapseRuntimeApi.GetActivitiesAsync(this.Instance.Id, activity.Id, cancellationToken); - } - - /// - public virtual async Task> GetOperativeActivitiesAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - return await this.SynapseRuntimeApi.GetOperativeActivitiesAsync(this.Instance.Id, activity.Id, cancellationToken); - } - - /// - public virtual async Task CreateActivityAsync(V1WorkflowActivityType type, object? input, IDictionary? metadata, V1WorkflowActivity? parent, CancellationToken cancellationToken) - { - var inputParam = input as Dynamic; - if (inputParam == null && input != null) - inputParam = Dynamic.FromObject(input); - var metadataParam = metadata == null ? null : new NameValueCollection(metadata); - return await this.SynapseRuntimeApi.CreateActivityAsync(new() { WorkflowInstanceId = this.Instance.Id, Type = type, Input = inputParam, Metadata = metadataParam, ParentId = parent?.Id }, cancellationToken); - } - - /// - public virtual async Task InitializeActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - //await this.SynapseRuntimeApi.InitializeActivityAsync(activity.Id, cancellationToken); - await Task.CompletedTask; - } - - /// - public virtual async Task StartOrResumeActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - await this.SynapseRuntimeApi.StartActivityAsync(activity.Id, cancellationToken); - } - - /// - public virtual async Task SuspendActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - await this.SynapseRuntimeApi.SuspendActivityAsync(activity.Id, cancellationToken); - } - - /// - public virtual async Task SkipActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - await this.SynapseRuntimeApi.SkipActivityAsync(activity.Id, cancellationToken); - } - - /// - public virtual async Task SetActivityMetadataAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - await this.SynapseRuntimeApi.SetActivityMetadataAsync(new() { Id = activity.Id, Metadata = activity.Metadata }, cancellationToken); - } - - /// - public virtual async Task FaultActivityAsync(V1WorkflowActivity activity, Exception ex, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - if (ex == null) - throw new ArgumentNullException(nameof(ex)); - await this.FaultActivityAsync(activity, new Integration.Models.Error() { Code = ex.GetType().Name.Replace("exception", string.Empty, StringComparison.OrdinalIgnoreCase), Message = ex.Message }, cancellationToken); - } - - /// - public virtual async Task FaultActivityAsync(V1WorkflowActivity activity, Integration.Models.Error error, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - if (error == null) - throw new ArgumentNullException(nameof(error)); - await this.SynapseRuntimeApi.FaultActivityAsync(new() { Id = activity.Id, Error = error }, cancellationToken); ; - } - - /// - public virtual async Task CompensateActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - await this.SynapseRuntimeApi.CompensateActivityAsync(new() { Id = activity.Id }, cancellationToken); - } - - /// - public virtual async Task MarkActivityAsCompensatedAsync(string activityId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(activityId)) - throw new ArgumentNullException(nameof(activityId)); - await this.SynapseRuntimeApi.MarkActivityAsCompensatedAsync(new() { Id = activityId }, cancellationToken); - } - - /// - public virtual async Task CancelActivityAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - await this.SynapseRuntimeApi.CancelActivityAsync(activity.Id, cancellationToken); - } - - /// - public virtual async Task SetActivityOutputAsync(V1WorkflowActivity activity, object? output, CancellationToken cancellationToken = default) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - var outputParam = output as Dynamic; - if (outputParam == null && output != null) - outputParam = Dynamic.FromObject(output); - await this.SynapseRuntimeApi.SetActivityOutputAsync(new() { Id = activity.Id, Output = outputParam }, cancellationToken); - } - - /// - public virtual async Task GetActivityStateDataAsync(V1WorkflowActivity activity, CancellationToken cancellationToken = default) - { - var result = await this.SynapseRuntimeApi.GetActivityStateDataAsync(activity.Id, cancellationToken); - return result.ToObject()!; - } - - /// - public virtual async Task StartSubflowAsync(string workflowId, object? input, CancellationToken cancellationToken = default) - { - var inputData = input as Dynamic; - if (inputData == null && input != null) - inputData = Dynamic.FromObject(input); - var workflowInstance = await this.SynapseRuntimeApi.StartSubflowAsync(new() { WorkflowId = workflowId, InputData = inputData, ActivationType = V1WorkflowInstanceActivationType.Subflow, ParentId = this.Instance.Id, AutoStart = true }, cancellationToken); - return workflowInstance.Id; - } - - /// - public virtual async Task SuspendAsync(CancellationToken cancellationToken = default) - { - this.Instance = await this.SynapseRuntimeApi.SuspendAsync(this.Instance.Id, cancellationToken); - } - - /// - public virtual async Task CancelAsync(CancellationToken cancellationToken = default) - { - this.Instance = await this.SynapseRuntimeApi.CancelAsync(this.Instance.Id, cancellationToken); - } - - /// - public virtual async Task TransitionToAsync(StateDefinition state, CancellationToken cancellationToken = default) - { - //await this.Synapse.TransitionWorkflowInstanceToAsync(this.Instance.Id, state.Name, cancellationToken); //todo - await Task.CompletedTask; - } - - /// - public virtual async Task FaultAsync(Exception ex, CancellationToken cancellationToken) - { - this.Instance = await this.SynapseRuntimeApi.FaultAsync(new() { Id = this.Instance.Id, Error = new() { Code = ex.GetType().Name.Replace("exception", string.Empty, StringComparison.InvariantCultureIgnoreCase), Message = ex.Message } }, cancellationToken); - } - - /// - public virtual async Task SetOutputAsync(object? output, CancellationToken cancellationToken = default) - { - var outputData = output as Dynamic; - if (outputData == null && output != null) - outputData = Dynamic.FromObject(output); - this.Instance = await this.SynapseRuntimeApi.SetOutputAsync(new() { Id = this.Instance.Id, Output = outputData }, cancellationToken); - } - - /// - public virtual async Task On(V1WorkflowActivity activity, IV1WorkflowActivityIntegrationEvent e, CancellationToken cancellationToken = default) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - switch (e) - { - case V1WorkflowActivityStartedIntegrationEvent: - case V1WorkflowActivityResumedIntegrationEvent: - await this.StartOrResumeActivityAsync(activity, cancellationToken); - break; - case V1WorkflowActivitySuspendedIntegrationEvent: - await this.SuspendActivityAsync(activity, cancellationToken); - break; - case V1WorkflowActivitySkippedIntegrationEvent: - await this.SkipActivityAsync(activity, cancellationToken); - break; - case V1WorkflowActivityFaultedIntegrationEvent faultedEvent: - await this.FaultActivityAsync(activity, faultedEvent.Error, cancellationToken); - break; - case V1WorkflowActivityCancelledIntegrationEvent: - await this.CancelActivityAsync(activity, cancellationToken); - break; - case V1WorkflowActivityCompletedIntegrationEvent completedEvent: - await this.SetActivityOutputAsync(activity, completedEvent.Output, cancellationToken); - break; - default: - throw new NotSupportedException($"The specified workflow activity integration event type '{e.GetType().Name}' is not supported in this context"); - } - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/WorkflowRuntime.cs b/src/apps/Synapse.Worker/Services/WorkflowRuntime.cs deleted file mode 100644 index b92016b64..000000000 --- a/src/apps/Synapse.Worker/Services/WorkflowRuntime.cs +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ConcurrentCollections; -using Microsoft.Extensions.Hosting; -using Synapse.Apis.Runtime; - -namespace Synapse.Worker.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class WorkflowRuntime - : BackgroundService, IWorkflowRuntime - { - - private IDisposable? _ServerSignalStreamSubscription; - private IDisposable? _OutboundEventStreamSubscription; - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to handle the lifetime of the 's host application - /// The service used to publish and subscribe to integration events - /// The service used to create s - /// The service used to interact with the Synapse Runtime API - /// The current - public WorkflowRuntime(ILogger logger, IHostApplicationLifetime hostApplicationLifetime, IIntegrationEventBus integrationEventBus, - IWorkflowActivityProcessorFactory activityProcessorFactory, ISynapseRuntimeApi synapseRuntimeApi, IWorkflowRuntimeContext context) - { - this.Logger = logger; - this.HostApplicationLifetime = hostApplicationLifetime; - this.IntegrationEventBus = integrationEventBus; - this.ActivityProcessorFactory = activityProcessorFactory; - this.RuntimeApi = synapseRuntimeApi; - this.Context = context; - this.CancellationTokenSource = null!; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to handle the lifetime of the 's host application - /// - protected IHostApplicationLifetime HostApplicationLifetime { get; } - - /// - /// Gets the service used to create s - /// - protected IWorkflowActivityProcessorFactory ActivityProcessorFactory { get; } - - /// - /// Gets the current - /// - protected IWorkflowRuntimeContext Context { get; } - - /// - /// Gets the service used to interact with the Synapse Runtime API - /// - protected ISynapseRuntimeApi RuntimeApi { get; } - - /// - /// Gets the 's - /// - protected CancellationTokenSource CancellationTokenSource { get; private set; } - - /// - /// Gets the 's - /// - protected CancellationToken CancellationToken => this.CancellationTokenSource.Token; - - /// - /// Gets a containing all child s - /// - protected ConcurrentHashSet Processors { get; } = new ConcurrentHashSet(); - - /// - /// Gets the service used to publish and subscribe to integration events - /// - protected IIntegrationEventBus IntegrationEventBus { get; } - - /// - /// Gets an that represents the inbound, server stream - /// - protected IObservable ServerStream { get; private set; } = null!; - - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); - try - { - await this.Context.InitializeAsync(this.CancellationToken); - this.ServerStream = this.RuntimeApi.Connect(this.Context.Workflow.Instance.Id).ToObservable(); - this._ServerSignalStreamSubscription = this.ServerStream.SubscribeAsync(this.OnServerSignalAsync, this.OnServerConnectionErrorAsync, this.OnDisconnectedFromServerAsync); - this._OutboundEventStreamSubscription = this.IntegrationEventBus.OutboundStream.SubscribeAsync(this.OnPublishEventAsync); - switch (this.Context.Workflow.Instance.Status) - { - case V1WorkflowInstanceStatus.Pending: - case V1WorkflowInstanceStatus.Scheduled: - case V1WorkflowInstanceStatus.Starting: - await this.StartAsync(); - break; - case V1WorkflowInstanceStatus.Resuming: - await this.ResumeAsync(); - break; - default: - throw new InvalidOperationException($"The workflow instance '{this.Context.Workflow.Instance.Id}' is in an unexpected state '{this.Context.Workflow.Instance.Status}'"); - } - } - catch (Exception ex) - { - this.Logger.LogError("A critical exception occured while executing the workflow instance with id '{instanceId}': {ex}", EnvironmentVariables.Runtime.WorkflowInstanceId.Value, ex.ToString()); - try - { - await this.Context.Workflow.FaultAsync(ex, this.CancellationToken); - } - catch (Exception iex) - { - this.Logger.LogError("A critical exception occured while faulting the execution of workflow instance with id '{instanceId}': {ex}", EnvironmentVariables.Runtime.WorkflowInstanceId.Value, iex.ToString()); - throw; - } - this.HostApplicationLifetime.StopApplication(); - throw; - } - } - - /// - /// Starts the 's execution - /// - /// A new awaitable - protected virtual async Task StartAsync() - { - this.Logger.LogInformation("Starting the workflow's execution..."); - await this.Context.Workflow.StartAsync(this.CancellationToken); - if (!this.Context.Workflow.Definition.TryGetStartState(out StateDefinition startState)) - throw new InvalidOperationException($"Failed to resolved the startup state for workflow '{this.Context.Workflow.Definition.Id}'"); - await this.Context.Workflow.TransitionToAsync(startState, this.CancellationToken); - var metadata = new Dictionary() { { V1WorkflowActivityMetadata.State, startState.Name } }; - var activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.State, await this.Context.FilterInputAsync(startState, this.Context.Workflow.Instance.Input, this.CancellationToken), metadata, null, this.CancellationToken); - var processor = this.CreateActivityProcessor(activity); - await processor.ProcessAsync(this.CancellationToken); - } - - /// - /// Resumes the 's execution - /// - /// A new awaitable - protected virtual async Task ResumeAsync() - { - this.Logger.LogInformation("Resuming the workflow's execution..."); - await this.Context.Workflow.StartAsync(this.CancellationToken); - var activities = await this.Context.Workflow.GetOperativeActivitiesAsync(this.CancellationToken); - if (!activities.Any()) - { - activities = await this.Context.Workflow.GetActivitiesAsync(this.CancellationToken); - var lastTopLevelActivity = activities - .OrderBy(a => a.CreatedAt) - .LastOrDefault(a => a.Type == V1WorkflowActivityType.State || a.Type == V1WorkflowActivityType.Transition || a.Type == V1WorkflowActivityType.End); - if (lastTopLevelActivity == null) - { - await this.StartAsync(); - return; - } - switch (lastTopLevelActivity.Type) - { - case V1WorkflowActivityType.Start: - await this.ResumeFromStartAsync(lastTopLevelActivity); - break; - case V1WorkflowActivityType.State: - await this.ResumeFromStateAsync(lastTopLevelActivity); - break; - case V1WorkflowActivityType.Transition: - await this.ResumeFromTransitionAsync(lastTopLevelActivity); - break; - case V1WorkflowActivityType.End: - await this.ResumeFromEndAsync(lastTopLevelActivity); - break; - default: - throw new NotSupportedException($"The specified {nameof(V1WorkflowActivityType)} '{nameof(lastTopLevelActivity.Type)}' is not supported in this context"); - } - } - foreach (var activity in activities) - { - var processor = this.CreateActivityProcessor(activity); - await processor.ProcessAsync(this.CancellationToken); - } - } - - /// - /// Resumes the execution of the workflow from the - /// - /// The to resume from - /// A new awaitable - protected virtual Task ResumeFromStartAsync(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - //todo: implement - throw new NotImplementedException(); - } - - /// - /// Resumes the execution of the workflow from the specified - /// - /// The to resume from - /// A new awaitable - protected virtual async Task ResumeFromStateAsync(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.State, out var stateName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.State}'"); - if (!this.Context.Workflow.Definition.TryGetState(stateName, out var state)) - throw new NullReferenceException($"Failed to find the workflow state with the specified name '{stateName}'"); - V1WorkflowActivity nextActivity; - var metadata = new Dictionary() { { V1WorkflowActivityMetadata.State, state.Name } }; - if (state.Transition != null - || !string.IsNullOrWhiteSpace(state.TransitionToStateName)) - nextActivity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Transition, activity.Output!.ToObject()!, metadata, null, this.CancellationToken); - else if (state.End != null - || state.IsEnd) - nextActivity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.End, activity.Output!.ToObject()!, metadata, null, this.CancellationToken); - else - throw new InvalidOperationException($"The state '{state.Name}' must declare a transition definition or an end definition for it is part of the main execution logic of the workflow '{this.Context.Workflow.Definition.Id}'"); - var processor = this.CreateActivityProcessor(nextActivity); - await processor.ProcessAsync(this.CancellationToken); - } - - /// - /// Resumes the execution of the workflow from the specified - /// - /// The to resume from - /// A new awaitable - protected virtual async Task ResumeFromTransitionAsync(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.State, out var stateName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.State}'"); - if (!this.Context.Workflow.Definition.TryGetState(stateName, out var state)) - throw new NullReferenceException($"Failed to find the workflow state with the specified name '{stateName}'"); - TransitionDefinition transition; - if (state is SwitchStateDefinition @switch) - { - if (!activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Case, out var caseName)) - throw new ArgumentException($"The specified activity '{activity.Id}' is missing the required metadata field '{V1WorkflowActivityMetadata.Case}'"); - if (!@switch.TryGetCase(caseName, out SwitchCaseDefinition dataCondition)) - throw new NullReferenceException($"Failed to find a condition with the specified name '{caseName}'"); - transition = dataCondition.Transition!; - if (transition == null) - transition = new() { NextState = dataCondition.TransitionToStateName! }; - } - else - { - transition = state.Transition!; - if (transition == null) - transition = new() { NextState = state.TransitionToStateName! }; - } - if (!this.Context.Workflow.Definition.TryGetState(transition.NextState, out StateDefinition nextState)) - throw new NullReferenceException($"Failed to find a state with name '{transition.NextState}' in workflow '{this.Context.Workflow.Definition.Id} {this.Context.Workflow.Definition.Version}'"); - await this.Context.Workflow.TransitionToAsync(nextState, this.CancellationToken); - var metadata = new Dictionary() { { V1WorkflowActivityMetadata.State, nextState.Name } }; - var nextActivity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.State, activity.Output, metadata, null, this.CancellationToken); - var processor = this.CreateActivityProcessor(nextActivity); - await processor.ProcessAsync(this.CancellationToken); - } - - /// - /// Resumes the execution of the workflow from the - /// - /// The to resume from - /// A new awaitable - protected virtual async Task ResumeFromEndAsync(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - await this.Context.Workflow.SetOutputAsync(activity.Output, this.CancellationToken); - } - - /// - /// Suspends the 's execution - /// - /// A new awaitable - protected virtual async Task SuspendAsync() - { - this.Logger.LogInformation("Suspending execution..."); - foreach (var processor in this.Processors.ToList()) - { - await processor.SuspendAsync(this.CancellationToken); - } - await this.Context.Workflow.SuspendAsync(this.CancellationToken); - this.Logger.LogInformation("Execution has been successfully suspended. The application will now shutdown...."); - this.HostApplicationLifetime.StopApplication(); - } - - /// - /// Cancels the 's execution - /// - /// A new awaitable - protected virtual async Task CancelAsync() - { - this.Logger.LogInformation("Cancelling execution..."); - foreach (var processor in this.Processors.ToList()) - { - await processor.TerminateAsync(this.CancellationToken); - } - await this.Context.Workflow.CancelAsync(this.CancellationToken); - this.Logger.LogInformation("Execution has been successfully cancelled. The application will now shutdown...."); - this.HostApplicationLifetime.StopApplication(); - } - - /// - /// Creates a new child for the specified - /// - /// The to create a child for - protected virtual IWorkflowActivityProcessor CreateActivityProcessor(V1WorkflowActivity activity) - { - if (activity == null) - throw new ArgumentNullException(nameof(activity)); - IWorkflowActivityProcessor processor = this.ActivityProcessorFactory.Create(activity); - switch (processor) - { - case IStateProcessor stateProcessor: - processor.OfType().SubscribeAsync - ( - async e => await this.OnStateCompletedAsync(stateProcessor, e), - async ex => await this.OnStateProcessingErrorAsync(stateProcessor, ex), - async () => await this.OnActivityProcessingCompletedAsync(stateProcessor) - ); - break; - case ITransitionProcessor transitionProcessor: - processor.OfType().SubscribeAsync - ( - async e => await this.OnTransitionCompletedAsync(transitionProcessor, e), - async ex => await this.OnActivityProcessingErrorAsync(transitionProcessor, ex), - async () => await this.OnActivityProcessingCompletedAsync(transitionProcessor) - ); - break; - case IEndProcessor endProcessor: - processor.OfType().SubscribeAsync - ( - async e => await this.OnEndCompletedAsync(endProcessor, e), - async ex => await this.OnActivityProcessingErrorAsync(endProcessor, ex), - async () => await this.OnCompletedAsync(endProcessor) - ); - break; - } - this.Processors.Add(processor); - return processor; - } - - /// - /// Handles the specified - /// - /// The to handle - /// A new awaitable - protected virtual async Task OnServerSignalAsync(V1RuntimeSignal signal) - { - if (signal == null) - return; - try - { - switch (signal.Type) - { - case V1RuntimeSignalType.Correlate: - var correlationContext = signal.Data!.ToObject(); - foreach (var e in correlationContext.PendingEvents) - { - this.IntegrationEventBus.InboundStream.OnNext(e.ToCloudEvent()); - } - break; - case V1RuntimeSignalType.Suspend: - await this.SuspendAsync(); - break; - case V1RuntimeSignalType.Cancel: - await this.CancelAsync(); - break; - default: - this.Logger.LogWarning("The specified server signal type '{signal.Type}' is not supported", signal.Type); - break; - } - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while handling a server runtime signal: {ex}", ex.ToString()); - await this.Context.Workflow.FaultAsync(ex, this.CancellationToken); - this.HostApplicationLifetime.StopApplication(); - } - } - - /// - /// Handles the specified that occured during communication with the server - /// - /// The to handle - /// A new awaitable - protected virtual async Task OnServerConnectionErrorAsync(Exception ex) - { - this.Logger.LogCritical("The connection to the remote server has been lost"); - this.HostApplicationLifetime.StopApplication(); - await Task.CompletedTask; - } - - /// - /// Handles the 's disconnection from the server - /// - /// A new awaitable - protected virtual async Task OnDisconnectedFromServerAsync() - { - this.Logger.LogCritical("The connection to the remote server has been lost"); - this.HostApplicationLifetime.StopApplication(); - await Task.CompletedTask; - } - - /// - /// Publishes the specified - /// - /// The to publish - /// A new awaitable - protected virtual async Task OnPublishEventAsync(CloudEvent e) - { - try - { - //todo: publish the event to the server - await Task.CompletedTask; - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while publishing the specified event: {ex}", ex.ToString()); - } - } - - /// - /// Handles the completion of a state - /// - /// The that has finished processing the state - /// The that describes the processed state's output - /// A new awaitable - protected virtual async Task OnStateCompletedAsync(IStateProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e) - { - var metadata = new Dictionary() { { V1WorkflowActivityMetadata.State, processor.State.Name } }; - if (processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.CompensationSource, out var compensationSource)) - metadata.Add(V1WorkflowActivityMetadata.CompensationSource, compensationSource); - if (processor.State is SwitchStateDefinition switchState) - { - if (!processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Case, out var caseName)) - throw new InvalidOperationException($"Failed to retrieve the required switch state metadata with key '{V1WorkflowActivityMetadata.Case}'"); - if (!switchState.TryGetCase(caseName, out SwitchCaseDefinition switchCase)) - throw new InvalidOperationException($"Failed to find a case with name '{caseName}' in the state '{processor.State.Name}' of workflow '{this.Context.Workflow.Definition.Id}'"); - metadata.Add(V1WorkflowActivityMetadata.Case, caseName); - V1WorkflowActivity activity = switchCase.OutcomeType switch - { - SwitchCaseOutcomeType.End => await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.End, e.Output, metadata, null, this.CancellationToken), - SwitchCaseOutcomeType.Transition => await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Transition, e.Output, metadata, null, this.CancellationToken), - _ => throw new NotSupportedException($"The specified condition type '{switchCase.OutcomeType}' is not supported in this context") - }; -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateActivityProcessor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - else - { - if (processor.State.Transition != null - || !string.IsNullOrWhiteSpace(processor.State.TransitionToStateName)) - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Transition, e.Output!.ToObject()!, metadata, null, this.CancellationToken); - else if (processor.State.End != null - || processor.State.IsEnd) - await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.End, e.Output!.ToObject()!, metadata, null, this.CancellationToken); - else - throw new InvalidOperationException($"The state '{processor.State.Name}' must declare a transition definition or an end definition for it is part of the main execution logic of the workflow '{this.Context.Workflow.Definition.Id}'"); - foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.CancellationToken)) - { -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateActivityProcessor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - } - } - - /// - /// Handles an that has occured during the processing of a - /// - /// The that has thrown the to handle - /// The to handle - /// A new awaitable - protected virtual async Task OnStateProcessingErrorAsync(IStateProcessor processor, Exception ex) - { - if (string.IsNullOrWhiteSpace(processor.State.CompensatedBy)) - { - await this.OnActivityProcessingErrorAsync(processor, ex); - return; - } - await this.Context.Workflow.CompensateActivityAsync(processor.Activity, this.CancellationToken); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, processor.State.Name }, - { V1WorkflowActivityMetadata.NextState, processor.State.CompensatedBy }, - { V1WorkflowActivityMetadata.CompensationSource, processor.Activity.Id } - }; - var activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Transition, await this.Context.Workflow.GetActivityStateDataAsync(processor.Activity, this.CancellationToken), metadata, null, this.CancellationToken); -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateActivityProcessor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - await this.OnActivityProcessingCompletedAsync(processor); - } - - /// - /// Handles the next - /// - /// The that has produced an - /// The to process - /// A new awaitable - protected virtual async Task OnTransitionCompletedAsync(ITransitionProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e) - { - if (!this.Context.Workflow.Definition.TryGetState(processor.Transition.NextState, out StateDefinition nextState)) - throw new NullReferenceException($"Failed to find a state with name '{processor.Transition.NextState}' in workflow '{this.Context.Workflow.Definition.Id} {this.Context.Workflow.Definition.Version}'"); - await this.Context.Workflow.TransitionToAsync(nextState, this.CancellationToken); - var metadata = new Dictionary() - { - { V1WorkflowActivityMetadata.State, nextState.Name } - }; - if (processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.CompensationSource, out var compensationSource)) - metadata.Add(V1WorkflowActivityMetadata.CompensationSource, compensationSource); - var activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.State, e.Output, metadata, null, this.CancellationToken); -#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - this.CreateActivityProcessor(activity); -#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed - } - - /// - /// Handles the completion of the specified - /// - /// The that has produced the - /// The to handle - /// A new awaitable - protected virtual async Task OnEndCompletedAsync(IEndProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e) - { - if (processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.CompensationSource, out var compensationSource)) - await this.Context.Workflow.MarkActivityAsCompensatedAsync(compensationSource, this.CancellationToken); - await this.Context.Workflow.SetOutputAsync(e.Output, this.CancellationToken); - } - - /// - /// Handles an that has occured during the processing of a - /// - /// The that has thrown the to handle - /// The to handle - /// A new awaitable - protected virtual async Task OnActivityProcessingErrorAsync(IWorkflowActivityProcessor processor, Exception ex) - { - try - { - this.Logger.LogWarning("An error occured while executing the workflow instance: {ex}", ex.ToString()); - await this.Context.Workflow.FaultAsync(ex, this.CancellationToken); - this.HostApplicationLifetime.StopApplication(); - } - catch (Exception cex) - { - this.Logger.LogError("A critical exception occured while faulting the execution of the workflow instance: {ex}", cex.ToString()); - throw; - } - } - - /// - /// Handles the completion of the specified - /// - /// The to handle the completion of - /// A new awaitable - protected virtual async Task OnActivityProcessingCompletedAsync(IWorkflowActivityProcessor processor) - { - this.Processors.TryRemove(processor); - processor.Dispose(); - foreach (IWorkflowActivityProcessor childProcessor in this.Processors) - { - await childProcessor.ProcessAsync(this.CancellationToken); - } - } - - /// - /// Handles the completion of the specified - /// - /// The to handle the completion of - /// A new awaitable - protected virtual async Task OnCompletedAsync(IEndProcessor processor) - { - await this.OnActivityProcessingCompletedAsync(processor); - this.Logger.LogInformation("Workflow executed"); - this.HostApplicationLifetime.StopApplication(); - } - - /// -#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize - public override void Dispose() -#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize - { - this._ServerSignalStreamSubscription?.Dispose(); - this._OutboundEventStreamSubscription?.Dispose(); - base.Dispose(); - } - - } - -} diff --git a/src/apps/Synapse.Worker/Services/WorkflowRuntimeContext.cs b/src/apps/Synapse.Worker/Services/WorkflowRuntimeContext.cs deleted file mode 100644 index 9f1c37027..000000000 --- a/src/apps/Synapse.Worker/Services/WorkflowRuntimeContext.cs +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Microsoft.Extensions.DependencyInjection; -using Neuroglia.Data.Expressions; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using ServerlessWorkflow.Sdk.Services.IO; -using Synapse.Apis.Management; -using Synapse.Apis.Runtime; - -namespace Synapse.Worker.Services -{ - - ///

- /// Represents the default implementation of the - /// - public class WorkflowRuntimeContext - : IWorkflowRuntimeContext - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to perform logging - /// The service used to create provide s - /// The service used to resolve external definitions referenced by s - /// The service used to manage secrets - /// The service used to interact with the Synapse Public API - /// The service used to interact with the Synapse Runtime API - public WorkflowRuntimeContext(IServiceProvider serviceProvider, ILogger logger, IWorkflowExternalDefinitionResolver workflowExternalDefinitionResolver, - IExpressionEvaluatorProvider expressionEvaluatorProvider, ISecretManager secretManager, ISynapseManagementApi managementApi, ISynapseRuntimeApi runtimeApi) - { - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.WorkflowExternalDefinitionResolver = workflowExternalDefinitionResolver; - this.ExpressionEvaluatorProvider = expressionEvaluatorProvider; - this.SecretManager = secretManager; - this.ManagementApi = managementApi; - this.RuntimeApi = runtimeApi; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to resolve external definitions referenced by s - /// - protected IWorkflowExternalDefinitionResolver WorkflowExternalDefinitionResolver { get; } - - /// - /// Gets the service used to create provide s - /// - protected IExpressionEvaluatorProvider ExpressionEvaluatorProvider { get; } - - /// - /// Gets the used to evaluate the current workflow's runtime expressions - /// - protected IExpressionEvaluator ExpressionEvaluator { get; private set; } = null!; - - /// - /// Gets the service used to manage secrets - /// - protected ISecretManager SecretManager { get; } - - /// - /// Gets the service used to interact with the Synapse Public API - /// - protected ISynapseManagementApi ManagementApi { get; } - - /// - /// Gets the service used to interact with the Synapse Runtime API - /// - protected ISynapseRuntimeApi RuntimeApi { get; } - - /// - public IWorkflowFacade Workflow { get; private set; } = null!; - - /// - public IDictionary Data { get; } = new Dictionary(); - - /// - public virtual async Task InitializeAsync(CancellationToken cancellationToken) - { - try - { - var workflowInstanceId = EnvironmentVariables.Runtime.WorkflowInstanceId.Value; - this.Logger.LogInformation("Initializing the runtime context for workflow instance with id '{workflowInstanceId}'...", workflowInstanceId); - var workflowInstance = await this.ManagementApi.GetWorkflowInstanceByIdAsync(workflowInstanceId, cancellationToken); - if (workflowInstance == null) - throw new NullReferenceException($"Failed to find a workflow instance with the specified id '{workflowInstanceId}'"); - this.Logger.LogInformation("Retrieving definition of workflow with id '{workflowInstance.WorkflowId}'...", workflowInstance.WorkflowId); - var workflow = await this.ManagementApi.GetWorkflowByIdAsync(workflowInstance.WorkflowId, cancellationToken); - if (workflow == null) - throw new NullReferenceException($"Failed to find a workflow with the specified id '{workflowInstance.WorkflowId}'"); - var workflowDefinition = await this.WorkflowExternalDefinitionResolver.LoadExternalDefinitionsAsync(workflow.Definition, new(), cancellationToken); - this.ExpressionEvaluator = this.ExpressionEvaluatorProvider.GetEvaluator(workflow.Definition.ExpressionLanguage)!; - if (this.ExpressionEvaluator == null) - throw new NullReferenceException($"Failed to find an expression evaluator for language '{workflow.Definition.ExpressionLanguage}'"); - this.Workflow = ActivatorUtilities.CreateInstance(this.ServiceProvider, workflowInstance, workflowDefinition); - this.Logger.LogInformation("Runtime context initialized"); - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while initializing the workflow runtime context: {ex}", ex.ToString()); - throw; - } - } - - /// - public virtual async Task EvaluateAsync(string runtimeExpression, object? data, AuthorizationInfo? authorization, CancellationToken cancellationToken) - { - runtimeExpression = runtimeExpression.Trim(); - if (runtimeExpression.StartsWith("${")) runtimeExpression = runtimeExpression[2..^1].Trim(); - var args = await this.BuildRuntimExpressionArgumentsAsync(cancellationToken); - if (authorization != null) args.Add("AUTHZ", authorization); - foreach (Match functionMatch in Regex.Matches(runtimeExpression, @"(fn:[\w\-_]*)")) - { - var functionName = functionMatch.Value.Trim(); - functionName = functionName[3..]; - if (!this.Workflow.Definition.TryGetFunction(functionName, out var function)) throw new NullReferenceException($"Failed to find a function with the specified name '{functionName}' in the workflow '{this.Workflow.Definition}'"); - if (function.Type != FunctionType.Expression) throw new InvalidOperationException($"The function with name '{function.Name}' is of type '{EnumHelper.Stringify(function.Type)}' and cannot be called in an expression"); - var value = this.ExpressionEvaluator.Evaluate(function.Operation, data!, args); - var serializedValue = null as string; - if (value != null) serializedValue = JsonConvert.SerializeObject(value, Formatting.None, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore }); - runtimeExpression = runtimeExpression.Replace(functionMatch.Value, serializedValue); - } - return this.ExpressionEvaluator.Evaluate(runtimeExpression, data!, args); - } - - /// - public virtual Task EvaluateAsync(string runtimeExpression, object? data, CancellationToken cancellationToken) - { - return this.EvaluateAsync(runtimeExpression, data, null, cancellationToken); - } - - /// - public virtual async Task GetSecretAsync(string secret, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(secret)) throw new ArgumentNullException(nameof(secret)); - var secrets = await this.SecretManager.GetSecretsAsync(cancellationToken); - if (!secrets.TryGetValue(secret, out var secretValue)) throw new NullReferenceException($"Failed to find the specified secret '{secret}'"); - return secretValue switch - { - T t => t, - DynamicObject dyn => dyn.ToObject(), - JObject jobj => jobj.ToObject()!, - _ => throw new InvalidCastException(), - }; - } - - /// - public virtual async Task PublishEventAsync(V1Event e, CancellationToken cancellationToken = default) - { - if (e == null) throw new ArgumentNullException(nameof(e)); - await this.RuntimeApi.PublishEventAsync(new() { Event = e }, cancellationToken); - } - - /// - /// Builds the runtime expression arguments - /// - /// A - /// A new that represents the runtime expression arguments - protected virtual async Task> BuildRuntimExpressionArgumentsAsync(CancellationToken cancellationToken) - { - var args = new Dictionary - { - { "WORKFLOW", await this.BuildRuntimeExpressionWorkflowArgumentAsync(cancellationToken) }, - { "CONST", await this.BuildRuntimeExpressionConstantsArgumentAsync(cancellationToken) }, - { "SECRETS", await this.BuildRuntimeExpressionSecretsArgumentAsync(cancellationToken) } - }; - return args; - } - - /// - /// Builds the runtime expression '$WORKFLOW' argument object - /// - /// A - /// The runtime expression '$WORKFLOW' argument object - protected virtual async Task BuildRuntimeExpressionWorkflowArgumentAsync(CancellationToken cancellationToken) - { - return await Task.FromResult(new - { - workflow = new - { - id = this.Workflow.Definition.GetUniqueIdentifier(), - instanceId = this.Workflow.Instance.Id - } - }); - } - - /// - /// Builds the runtime expression '$CONST' argument object - /// - /// A - /// The runtime expression '$CONST' argument object - protected virtual async Task BuildRuntimeExpressionConstantsArgumentAsync(CancellationToken cancellationToken) - { - var constants = this.Workflow.Definition.Constants?.ToObject(); - if (constants == null) - constants = new(); - return await Task.FromResult(constants); - } - - /// - /// Builds the runtime expression '$SECRETS' argument object - /// - /// A - /// The runtime expression '$SECRETS' argument object - protected virtual async Task BuildRuntimeExpressionSecretsArgumentAsync(CancellationToken cancellationToken) - { - if (await this.SecretManager.GetSecretsAsync(cancellationToken) is not object secrets) - secrets = new(); - return secrets; - } - - } - -} diff --git a/src/apps/Synapse.Worker/Synapse.Worker.csproj b/src/apps/Synapse.Worker/Synapse.Worker.csproj deleted file mode 100644 index 321e08e1a..000000000 --- a/src/apps/Synapse.Worker/Synapse.Worker.csproj +++ /dev/null @@ -1,57 +0,0 @@ - - - net6.0 - enable - enable - Exe - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse synctl app cli - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Properties - embedded - Linux - ..\..\.. - ghcr.io/serverlessworkflow/synapse-worker - win10-x64;linux-x64;osx-x64 - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/apps/Synapse.Worker/appsettings.json b/src/apps/Synapse.Worker/appsettings.json deleted file mode 100644 index 371281d90..000000000 --- a/src/apps/Synapse.Worker/appsettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "Endpoints": { - "Http": { - "Scheme": "http", - "Port": "42286" - }, - "Grpc": { - "Scheme": "http", - "Port": "41387" - } - } -} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/CliConstants.cs b/src/cli/Synapse.Cli/CliConstants.cs new file mode 100644 index 000000000..61cb1357e --- /dev/null +++ b/src/cli/Synapse.Cli/CliConstants.cs @@ -0,0 +1,27 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli; + +/// +/// Exposes constants and statics used by the CLI +/// +internal static class CliConstants +{ + + /// + /// Gets the name of the CLI configuration file + /// + public const string ConfigurationFileName = "config.yaml"; + +} diff --git a/src/cli/Synapse.Cli/Commands/Command.cs b/src/cli/Synapse.Cli/Commands/Command.cs new file mode 100644 index 000000000..cc694b589 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Command.cs @@ -0,0 +1,77 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Options; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the base class for all implementations +/// +public abstract class Command + : System.CommandLine.Command +{ + + /// + /// Initializes a new + /// + /// The current + /// The service used to create s + /// The service used to interact with the remote Synapse API + /// The 's name + /// The 's description + protected Command(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, string name, string description) + : base(name, description) + { + this.ServiceProvider = serviceProvider; + this.Logger = loggerFactory.CreateLogger(this.GetType()); + this.Api = api; + } + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider { get; } + + /// + /// Gets the service used to perform logging + /// + protected ILogger Logger { get; } + + /// + /// Gets the service used to interact with the remote Synapse API + /// + protected ISynapseApiClient Api { get; } + + /// + /// Ensures that the CLI application has been properly configured + /// + protected virtual void EnsureConfigured() + { + var applicationOptions = this.ServiceProvider.GetRequiredService>().Value; + if (applicationOptions.Api.Configurations.Count < 1) throw new Exception(@"Error: The server is not configured. Please ensure that your server configuration is correctly set. + +To configure the server, follow these steps: + +1. Verify that your configuration file is present and correctly formatted. +2. Set the server configuration using the appropriate command, for example: + synctl config set-api your-api-name --server=https://your-server-address --token=your_access_token + +For more information on configuring the server, visit: https://your-docs-url.com/configuration + +Hint: You can check your current configuration with: + synctl config get-apis +"); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Config/DeleteApiCommand.cs b/src/cli/Synapse.Cli/Commands/Config/DeleteApiCommand.cs new file mode 100644 index 000000000..1ceb4f36c --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Config/DeleteApiCommand.cs @@ -0,0 +1,69 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Options; +using Synapse.Cli.Configuration; +using Synapse.Cli.Services; + +namespace Synapse.Cli.Commands.Config; + +/// +/// Represents the used to configure the CLI to delete the specified API configuration +/// +internal class DeleteApiCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "delete-api"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Deletes the API configuration with the specified name."; + + /// + public DeleteApiCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IOptionsManager optionsManager, IOptionsMonitor applicationOptions) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.OptionsManager = optionsManager; + this.ApplicationOptions = applicationOptions; + this.Add(new Argument("name") { Description = "The name of the API configuration to delete." }); + this.Handler = CommandHandler.Create(HandleAsync); + } + + /// + /// Gets the service used to manage the application's options + /// + protected IOptionsManager OptionsManager { get; } + + /// + /// Gets the current + /// + protected IOptionsMonitor ApplicationOptions { get; } + + /// + /// Handles the + /// + /// The name of the API configuration to use + /// A new awaitable + public async Task HandleAsync(string name) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + if (this.ApplicationOptions.CurrentValue.Api.Current == name) throw new NotSupportedException($"Failed to delete the API configuration with name '{name}' because it is the API currently in use."); + if (!this.ApplicationOptions.CurrentValue.Api.Configurations.Remove(name)) throw new NullReferenceException($"Failed to find a configured API with name '{name}'."); + await this.OptionsManager.UpdateOptionsAsync(this.ApplicationOptions.CurrentValue); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Config/GetApisCommand.cs b/src/cli/Synapse.Cli/Commands/Config/GetApisCommand.cs new file mode 100644 index 000000000..510048091 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Config/GetApisCommand.cs @@ -0,0 +1,85 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Options; +using Synapse.Cli.Configuration; +using Synapse.Cli.Services; +using Synapse.Resources; + +namespace Synapse.Cli.Commands.Config; + +/// +/// Represents the used to configure the API used by the Synapse CLI +/// +internal class GetApisCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "get-apis"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Retrieves all configured APIs"; + + /// + public GetApisCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IOptionsManager optionsManager, IOptionsMonitor applicationOptions) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.OptionsManager = optionsManager; + this.ApplicationOptions = applicationOptions; + this.Handler = CommandHandler.Create(HandleAsync); + } + + /// + /// Gets the service used to manage the application's options + /// + protected IOptionsManager OptionsManager { get; } + + /// + /// Gets the current + /// + protected IOptionsMonitor ApplicationOptions { get; } + + /// + /// Handles the + /// + /// A new awaitable + public async Task HandleAsync() + { + var table = new Table(); + table.Border(TableBorder.None); + table.AddColumn("CURRENT"); + table.AddColumn("NAME"); + foreach (var apiConfig in this.ApplicationOptions.CurrentValue.Api.Configurations) + { + table.AddRow + ( + this.ApplicationOptions.CurrentValue.Api.Current == apiConfig.Key || this.ApplicationOptions.CurrentValue.Api.Configurations.Count == 1 ? "*" : string.Empty, + apiConfig.Key + ); + } + AnsiConsole.Write(table); + await Task.CompletedTask; + } + + static class CommandOptions + { + + public static Option Server => new(["-s", "--server"], "The address of the API server to use"); + + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Config/SetApiCommand.cs b/src/cli/Synapse.Cli/Commands/Config/SetApiCommand.cs new file mode 100644 index 000000000..31c058d08 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Config/SetApiCommand.cs @@ -0,0 +1,90 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Options; +using Synapse.Cli.Configuration; +using Synapse.Cli.Services; + +namespace Synapse.Cli.Commands.Config; + +/// +/// Represents the used to configure the API used by the Synapse CLI +/// +internal class SetApiCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "set-api"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Configures the API used by the Synapse CLI"; + + /// + public SetApiCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IOptionsManager optionsManager, IOptionsMonitor applicationOptions) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.OptionsManager = optionsManager; + this.ApplicationOptions = applicationOptions; + this.Add(new Argument("name") { Description = "The name of the API configuration to create or update." }); + this.Add(CommandOptions.Server); + this.Add(CommandOptions.Token); + this.Handler = CommandHandler.Create(HandleAsync); + } + + /// + /// Gets the service used to manage the application's options + /// + protected IOptionsManager OptionsManager { get; } + + /// + /// Gets the current + /// + protected IOptionsMonitor ApplicationOptions { get; } + + /// + /// Handles the + /// + /// The name of the API configuration to update + /// The uri of the API server to use + /// The token used to authenticate on the API server + /// A new awaitable + public async Task HandleAsync(string name, Uri server, string token) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentNullException.ThrowIfNull(server); + if (!this.ApplicationOptions.CurrentValue.Api.Configurations.TryGetValue(name, out var apiConfig) || apiConfig == null) apiConfig = new ApiConfiguration() + { + Server = server, + Token = token + }; + apiConfig.Server = server; + apiConfig.Token = token; + this.ApplicationOptions.CurrentValue.Api.Configurations[name] = apiConfig; + if (this.ApplicationOptions.CurrentValue.Api.Configurations.Count == 1) this.ApplicationOptions.CurrentValue.Api.Current = name; + await this.OptionsManager.UpdateOptionsAsync(this.ApplicationOptions.CurrentValue); + } + + static class CommandOptions + { + + public static Option Server => new(["-s", "--server"], "The address of the API server to use"); + + public static Option Token => new(["-t", "--token"], "The token used to authenticate on the API server"); + + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Config/UseApiCommand.cs b/src/cli/Synapse.Cli/Commands/Config/UseApiCommand.cs new file mode 100644 index 000000000..3561df93d --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Config/UseApiCommand.cs @@ -0,0 +1,69 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Options; +using Synapse.Cli.Configuration; +using Synapse.Cli.Services; + +namespace Synapse.Cli.Commands.Config; + +/// +/// Represents the used to configure the CLI to use the specified API configuration +/// +internal class UseApiCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "use-api"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Configures the API used by the Synapse CLI"; + + /// + public UseApiCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IOptionsManager optionsManager, IOptionsMonitor applicationOptions) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.OptionsManager = optionsManager; + this.ApplicationOptions = applicationOptions; + this.Add(new Argument("name") { Description = "The name of the API configuration to use." }); + this.Handler = CommandHandler.Create(HandleAsync); + } + + /// + /// Gets the service used to manage the application's options + /// + protected IOptionsManager OptionsManager { get; } + + /// + /// Gets the current + /// + protected IOptionsMonitor ApplicationOptions { get; } + + /// + /// Handles the + /// + /// The name of the API configuration to use + /// A new awaitable + public async Task HandleAsync(string name) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + if (!this.ApplicationOptions.CurrentValue.Api.Configurations.TryGetValue(name, out var apiConfig) || apiConfig == null) throw new NullReferenceException($"Failed to find a configured API with name '{name}'."); + this.ApplicationOptions.CurrentValue.Api.Current = name; + await this.OptionsManager.UpdateOptionsAsync(this.ApplicationOptions.CurrentValue); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/ConfigCommand.cs b/src/cli/Synapse.Cli/Commands/ConfigCommand.cs new file mode 100644 index 000000000..ce0487f5f --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/ConfigCommand.cs @@ -0,0 +1,44 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Commands.Config; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to configure the Synapse CLI +/// +public class ConfigCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "config"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Configures the Synapse CLI"; + + /// + public ConfigCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} diff --git a/src/cli/Synapse.Cli/Commands/CorrelationCommand.cs b/src/cli/Synapse.Cli/Commands/CorrelationCommand.cs new file mode 100644 index 000000000..71a32deb3 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/CorrelationCommand.cs @@ -0,0 +1,45 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Commands.Correlations; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to manage s +/// +public class CorrelationCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "correlation"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Manages correlations"; + + /// + public CorrelationCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("correlations"); + this.AddAlias("corel"); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Correlations/CreateCorrelationCommand.cs b/src/cli/Synapse.Cli/Commands/Correlations/CreateCorrelationCommand.cs new file mode 100644 index 000000000..0879cf7a4 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Correlations/CreateCorrelationCommand.cs @@ -0,0 +1,81 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Correlations; + +/// +/// Represents the used to create a new +/// +internal class CreateCorrelationCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "create"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Creates a new correlation."; + + /// + public CreateCorrelationCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IYamlSerializer yamlSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.YamlSerializer = yamlSerializer; + this.Add(CommandOptions.File); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize objects to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } + + /// + /// Handles the + /// + /// The file that defines the correlation to create + /// A new awaitable + public async Task HandleAsync(string file) + { + ArgumentException.ThrowIfNullOrWhiteSpace(file); + this.EnsureConfigured(); + var yaml = await File.ReadAllTextAsync(file); + var correlation = this.YamlSerializer.Deserialize(yaml) ?? throw new NullReferenceException("Failed to read a correlation resource from the specified file."); + correlation = await this.Api.Correlations.CreateAsync(correlation); + Console.WriteLine($"correlation/{correlation.GetName()} created"); + } + + static class CommandOptions + { + + public static Option File + { + get + { + var option = new Option("--file") + { + Description = "The file that contains the definition of the correlation to create." + }; + option.AddAlias("-f"); + return option; + } + } + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Correlations/DeleteCorrelationCommand.cs b/src/cli/Synapse.Cli/Commands/Correlations/DeleteCorrelationCommand.cs new file mode 100644 index 000000000..1238ba5a7 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Correlations/DeleteCorrelationCommand.cs @@ -0,0 +1,73 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.Correlations; + +/// +/// Represents the used to delete a single +/// +internal class DeleteCorrelationCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "delete"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Deletes the specified correlation"; + + /// + public DeleteCorrelationCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("del"); + this.Add(new Argument("name") { Description = "The name of the correlation to delete" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Confirm); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The name of the correlation to delete + /// The namespace of the correlation to delete + /// A boolean indicating whether or not to ask for the user's confirmation + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, bool y) + { + this.EnsureConfigured(); + if (!y) + { + Console.Write($"Are you sure you wish to delete the correlation '{name}'? Press 'y' to confirm, or any other key to cancel: "); + var inputKey = Console.ReadKey(); + Console.WriteLine(); + if (inputKey.Key != ConsoleKey.Y) return; + } + await this.Api.Correlations.DeleteAsync(name, @namespace); + Console.WriteLine($"correlation/{name} deleted"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the correlation to delete belongs to"); + + public static Option Confirm => new(["-y", "--yes"], () => false, "Delete the correlation without prompting confirmation"); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Correlations/ListCorrelationsCommand.cs b/src/cli/Synapse.Cli/Commands/Correlations/ListCorrelationsCommand.cs new file mode 100644 index 000000000..4aeaeb844 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Correlations/ListCorrelationsCommand.cs @@ -0,0 +1,84 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using moment.net; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Correlations; + +/// +/// Represents the used to lists +/// +internal class ListServiceAccountsCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "list"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Lists correlations"; + + /// + public ListServiceAccountsCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("ls"); + this.Add(CommandOptions.Namespace); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace the workflow to list belong to + /// A new awaitable + public async Task HandleAsync(string @namespace) + { + this.EnsureConfigured(); + var table = new Table(); + var isEmpty = true; + table.Border(TableBorder.None); + table.AddColumn("NAME"); + table.AddColumn("CREATED AT", column => + { + column.Alignment = Justify.Center; + }); + await foreach (var correlation in await this.Api.Correlations.ListAsync(@namespace)) + { + isEmpty = false; + table.AddRow + ( + correlation.GetName(), + correlation.Metadata.CreationTimestamp?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-" + ); + } + if (isEmpty) + { + AnsiConsole.WriteLine($"No resource found"); + return; + } + AnsiConsole.Write(table); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => string.Empty, "The namespace the correlation to list belong to."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/CorrelatorCommand.cs b/src/cli/Synapse.Cli/Commands/CorrelatorCommand.cs new file mode 100644 index 000000000..9d5fc9ff7 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/CorrelatorCommand.cs @@ -0,0 +1,44 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Commands.Correlators; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to manage s +/// +public class CorrelatorCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "correlator"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Manages correlators"; + + /// + public CorrelatorCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("correlators"); + this.AddAlias("cor"); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Correlators/GetCorrelatorCommand.cs b/src/cli/Synapse.Cli/Commands/Correlators/GetCorrelatorCommand.cs new file mode 100644 index 000000000..61bc7319f --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Correlators/GetCorrelatorCommand.cs @@ -0,0 +1,84 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.Correlators; + +/// +/// Represents the used to get a specific +/// +internal class GetCorrelatorCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "get"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Get the specified correlator"; + + /// + public GetCorrelatorCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer, IYamlSerializer yamlSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.YamlSerializer = yamlSerializer; + this.AddAlias("get"); + this.Add(new Argument("name") { Description = "The name of the correlator to get" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Output); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Gets the service used to serialize/deserialize to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } + + /// + /// Handles the + /// + /// The namespace of the correlator to get + /// The name of the correlator to get + /// The desired output format + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string output) + { + this.EnsureConfigured(); + var correlator = await this.Api.Correlators.GetAsync(name, @namespace); + string outputText = output.ToLowerInvariant() switch + { + "json" => this.JsonSerializer.SerializeToText(correlator), + "yaml" => this.YamlSerializer.SerializeToText(correlator), + _ => throw new NotSupportedException($"The specified output format '{output}' is not supported"), + }; + AnsiConsole.Markup($"[gray]{outputText.EscapeMarkup()}[/]"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the correlator to get belongs to."); + + public static Option Output => new(["-o", "--output"], () => "yaml", "The output format."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Correlators/ListCorrelatorsCommand.cs b/src/cli/Synapse.Cli/Commands/Correlators/ListCorrelatorsCommand.cs new file mode 100644 index 000000000..017926f85 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Correlators/ListCorrelatorsCommand.cs @@ -0,0 +1,97 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using moment.net; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Correlators; + +/// +/// Represents the used to lists +/// +internal class ListCorrelatorsCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "list"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Lists correlators"; + + /// + public ListCorrelatorsCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("ls"); + this.Add(CommandOptions.Namespace); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace the correlators to list belong to + /// A new awaitable + public async Task HandleAsync(string @namespace) + { + this.EnsureConfigured(); + var table = new Table(); + var isEmpty = true; + table.Border(TableBorder.None); + table.AddColumn("NAME", column => + { + column.NoWrap = true; + }); + table.AddColumn("NAMESPACE", column => + { + column.NoWrap = true; + }); + table.AddColumn("STATUS", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("CREATED AT", column => + { + column.Alignment = Justify.Center; + }); + await foreach (var correlator in await this.Api.Correlators.ListAsync(@namespace)) + { + isEmpty = false; + table.AddRow + ( + correlator.GetName(), + correlator.GetNamespace()!, + (correlator.Status?.Phase ?? CorrelatorStatusPhase.Stopped).ToUpperInvariant(), + correlator.Metadata.CreationTimestamp?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-" + ); + } + if(isEmpty) + { + AnsiConsole.WriteLine(string.IsNullOrWhiteSpace(@namespace) ? "No resource found" : $"No resource found in {@namespace} namespace"); + return; + } + AnsiConsole.Write(table); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => string.Empty, "The namespace the correlators to list belong to."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/NamespaceCommand.cs b/src/cli/Synapse.Cli/Commands/NamespaceCommand.cs new file mode 100644 index 000000000..205e0bd24 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/NamespaceCommand.cs @@ -0,0 +1,46 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Synapse.Cli.Commands.Namespaces; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to manage s +/// +public class NamespaceCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "namespace"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Manages namespaces"; + + /// + public NamespaceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("namespaces"); + this.AddAlias("ns"); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Namespaces/CreateNamespaceCommand.cs b/src/cli/Synapse.Cli/Commands/Namespaces/CreateNamespaceCommand.cs new file mode 100644 index 000000000..c25718a9c --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Namespaces/CreateNamespaceCommand.cs @@ -0,0 +1,61 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Namespaces; + +/// +/// Represents the used to create a new +/// +internal class CreateNamespaceCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "create"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Creates a new namespace."; + + /// + public CreateNamespaceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.Add(new Argument("name") { Description = "The name of the namespace to create." }); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The name of the namespace to create + /// A new awaitable + public async Task HandleAsync(string name) + { + this.EnsureConfigured(); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + await this.Api.Namespaces.CreateAsync(new() + { + Metadata = new() + { + Name = name + } + }); + Console.WriteLine($"namespace/{name} created"); + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Namespaces/DeleteNamespaceCommand.cs b/src/cli/Synapse.Cli/Commands/Namespaces/DeleteNamespaceCommand.cs new file mode 100644 index 000000000..aca32dca3 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Namespaces/DeleteNamespaceCommand.cs @@ -0,0 +1,71 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Namespaces; + +/// +/// Represents the used to delete a single +/// +internal class DeleteNamespaceCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "delete"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Deletes the specified namespace"; + + /// + public DeleteNamespaceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("del"); + this.Add(new Argument("name") { Description = "The name of the namespace to delete" }); + this.Add(CommandOptions.Confirm); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The name of the namespace to delete + /// A boolean indicating whether or not to ask for the user's confirmation + /// A new awaitable + public async Task HandleAsync(string name, bool y) + { + this.EnsureConfigured(); + if (!y) + { + Console.Write($"Are you sure you wish to delete the namespace '{name}'? Press 'y' to confirm, or any other key to cancel: "); + var inputKey = Console.ReadKey(); + if (inputKey.Key != ConsoleKey.Y) return; + } + await this.Api.Namespaces.DeleteAsync(name); + Console.Write($"namespace/{name} deleted"); + } + + static class CommandOptions + { + + public static Option Confirm => new(["-y", "--yes"], () => false, "Delete the @namespace(s) without prompting confirmation"); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Namespaces/ListNamespacesCommand.cs b/src/cli/Synapse.Cli/Commands/Namespaces/ListNamespacesCommand.cs new file mode 100644 index 000000000..a8a574e43 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Namespaces/ListNamespacesCommand.cs @@ -0,0 +1,75 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using moment.net; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Namespaces; + +/// +/// Represents the used to lists +/// +internal class ListNamespacesCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "list"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Lists namespaces"; + + /// + public ListNamespacesCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("ls"); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// A new awaitable + public async Task HandleAsync() + { + this.EnsureConfigured(); + var table = new Table(); + var isEmpty = true; + table.Border(TableBorder.None); + table.AddColumn("NAME"); + table.AddColumn("CREATED AT", column => + { + column.Alignment = Justify.Center; + }); + await foreach (var @namespace in await this.Api.Namespaces.ListAsync()) + { + isEmpty = false; + table.AddRow + ( + @namespace.GetName(), + @namespace.Metadata.CreationTimestamp?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-" + ); + } + if (isEmpty) + { + AnsiConsole.WriteLine($"No resource found"); + return; + } + AnsiConsole.Write(table); + } + +} diff --git a/src/cli/Synapse.Cli/Commands/OperatorCommand.cs b/src/cli/Synapse.Cli/Commands/OperatorCommand.cs new file mode 100644 index 000000000..fea85a214 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/OperatorCommand.cs @@ -0,0 +1,44 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Commands.Operators; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to manage s +/// +public class OperatorCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "operator"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Manages operators"; + + /// + public OperatorCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("operators"); + this.AddAlias("op"); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/Operators/GetOperatorCommand.cs b/src/cli/Synapse.Cli/Commands/Operators/GetOperatorCommand.cs new file mode 100644 index 000000000..69f8e8d7c --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Operators/GetOperatorCommand.cs @@ -0,0 +1,84 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.Operators; + +/// +/// Represents the used to get a specific +/// +internal class GetOperatorCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "get"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Get the specified operator"; + + /// + public GetOperatorCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer, IYamlSerializer yamlSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.YamlSerializer = yamlSerializer; + this.AddAlias("get"); + this.Add(new Argument("name") { Description = "The name of the operator to get" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Output); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Gets the service used to serialize/deserialize to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } + + /// + /// Handles the + /// + /// The namespace of the operator to get + /// The name of the operator to get + /// The desired output format + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string output) + { + this.EnsureConfigured(); + var @operator = await this.Api.Operators.GetAsync(name, @namespace); + string outputText = output.ToLowerInvariant() switch + { + "json" => this.JsonSerializer.SerializeToText(@operator), + "yaml" => this.YamlSerializer.SerializeToText(@operator), + _ => throw new NotSupportedException($"The specified output format '{output}' is not supported"), + }; + AnsiConsole.Markup($"[gray]{outputText.EscapeMarkup()}[/]"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the operator to get belongs to."); + + public static Option Output => new(["-o", "--output"], () => "yaml", "The output format."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Operators/ListOperatorsCommand.cs b/src/cli/Synapse.Cli/Commands/Operators/ListOperatorsCommand.cs new file mode 100644 index 000000000..a31dbf0c2 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Operators/ListOperatorsCommand.cs @@ -0,0 +1,97 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using moment.net; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Operators; + +/// +/// Represents the used to lists +/// +internal class ListOperatorsCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "list"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Lists operators"; + + /// + public ListOperatorsCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("ls"); + this.Add(CommandOptions.Namespace); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace the operators to list belong to + /// A new awaitable + public async Task HandleAsync(string @namespace) + { + this.EnsureConfigured(); + var table = new Table(); + var isEmpty = true; + table.Border(TableBorder.None); + table.AddColumn("NAME", column => + { + column.NoWrap = true; + }); + table.AddColumn("NAMESPACE", column => + { + column.NoWrap = true; + }); + table.AddColumn("STATUS", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("CREATED AT", column => + { + column.Alignment = Justify.Center; + }); + await foreach (var @operator in await this.Api.Operators.ListAsync(@namespace)) + { + isEmpty = false; + table.AddRow + ( + @operator.GetName(), + @operator.GetNamespace()!, + (@operator.Status?.Phase ?? OperatorStatusPhase.Stopped).ToUpperInvariant(), + @operator.Metadata.CreationTimestamp?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-" + ); + } + if(isEmpty) + { + AnsiConsole.WriteLine(string.IsNullOrWhiteSpace(@namespace) ? "No resource found" : $"No resource found in {@namespace} namespace"); + return; + } + AnsiConsole.Write(table); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => string.Empty, "The namespace the operators to list belong to."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/ServiceAccountCommand.cs b/src/cli/Synapse.Cli/Commands/ServiceAccountCommand.cs new file mode 100644 index 000000000..5dde9ebc0 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/ServiceAccountCommand.cs @@ -0,0 +1,45 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Commands.ServiceAccounts; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to manage s +/// +public class ServiceAccountCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "service-account"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Manages service accounts"; + + /// + public ServiceAccountCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("service-accounts"); + this.AddAlias("sa"); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/ServiceAccounts/CreateServiceAccountCommand.cs b/src/cli/Synapse.Cli/Commands/ServiceAccounts/CreateServiceAccountCommand.cs new file mode 100644 index 000000000..aaa5fdf05 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/ServiceAccounts/CreateServiceAccountCommand.cs @@ -0,0 +1,103 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Synapse.Resources; +using System.Security.Cryptography; + +namespace Synapse.Cli.Commands.ServiceAccounts; + +/// +/// Represents the used to create a new +/// +internal class CreateServiceAccountCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "create"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Creates a new service account."; + + /// + public CreateServiceAccountCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.Add(new Argument("name") { Description = "The name of the service account to create." }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Key); + this.Add(CommandOptions.Claims); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize objects to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Handles the + /// + /// The name of the service account to create + /// The namespace the service account to create belongs to + /// + /// The JSON of a type/value mapping of the claims associated to the service account to create + /// A new awaitable The JSON of the claims associated to the service account to create + public async Task HandleAsync(string name, string @namespace, string? key, string claims) + { + ArgumentException.ThrowIfNullOrWhiteSpace(name); + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + ArgumentException.ThrowIfNullOrWhiteSpace(claims); + this.EnsureConfigured(); + if (string.IsNullOrWhiteSpace(key)) + { + var keyBytes = new byte[64]; + using var rng = RandomNumberGenerator.Create(); + rng.GetBytes(keyBytes); + key = Convert.ToBase64String(keyBytes); + } + var claimMap = this.JsonSerializer.Deserialize>(claims); + var serviceAccount = new ServiceAccount() + { + Metadata = new() + { + Namespace = @namespace, + Name = name + }, + Spec = new() + { + Key = key, + Claims = claimMap ?? new Dictionary() + } + }; + serviceAccount = await this.Api.ServiceAccounts.CreateAsync(serviceAccount); + Console.WriteLine($"service-account/{serviceAccount.GetName()} created"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], "The namespace the service account to create belongs to."); + + public static Option Key => new(["-k", "--key"], () => string.Empty, "The symmetric key used by the related service for authentication purpose. If not set, one will be automatically generated."); + + public static Option Claims => new(["-c", "--claims"], "The JSON of the claims associated to the service account to create"); + + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/ServiceAccounts/DeleteServiceAccountCommand.cs b/src/cli/Synapse.Cli/Commands/ServiceAccounts/DeleteServiceAccountCommand.cs new file mode 100644 index 000000000..4e4e7a4df --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/ServiceAccounts/DeleteServiceAccountCommand.cs @@ -0,0 +1,73 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.ServiceAccounts; + +/// +/// Represents the used to delete a single +/// +internal class DeleteServiceAccountCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "delete"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Deletes the specified service account"; + + /// + public DeleteServiceAccountCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("del"); + this.Add(new Argument("name") { Description = "The name of the service account to delete" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Confirm); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The name of the service account to delete + /// The namespace of the service account to delete + /// A boolean indicating whether or not to ask for the user's confirmation + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, bool y) + { + this.EnsureConfigured(); + if (!y) + { + Console.Write($"Are you sure you wish to delete the service account '{name}'? Press 'y' to confirm, or any other key to cancel: "); + var inputKey = Console.ReadKey(); + Console.WriteLine(); + if (inputKey.Key != ConsoleKey.Y) return; + } + await this.Api.ServiceAccounts.DeleteAsync(name, @namespace); + Console.WriteLine($"service-account/{name} deleted"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the service account to delete belongs to"); + + public static Option Confirm => new(["-y", "--yes"], () => false, "Delete the service account without prompting confirmation"); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/ServiceAccounts/ListServiceAccountsCommand.cs b/src/cli/Synapse.Cli/Commands/ServiceAccounts/ListServiceAccountsCommand.cs new file mode 100644 index 000000000..43ab79dd3 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/ServiceAccounts/ListServiceAccountsCommand.cs @@ -0,0 +1,84 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using moment.net; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.ServiceAccounts; + +/// +/// Represents the used to lists +/// +internal class ListServiceAccountsCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "list"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Lists service accounts"; + + /// + public ListServiceAccountsCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("ls"); + this.Add(CommandOptions.Namespace); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace the service account to list belong to + /// A new awaitable + public async Task HandleAsync(string @namespace) + { + this.EnsureConfigured(); + var table = new Table(); + var isEmpty = true; + table.Border(TableBorder.None); + table.AddColumn("NAME"); + table.AddColumn("CREATED AT", column => + { + column.Alignment = Justify.Center; + }); + await foreach (var serviceAccount in await this.Api.ServiceAccounts.ListAsync(@namespace)) + { + isEmpty = false; + table.AddRow + ( + serviceAccount.GetName(), + serviceAccount.Metadata.CreationTimestamp?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-" + ); + } + if (isEmpty) + { + AnsiConsole.WriteLine($"No resource found"); + return; + } + AnsiConsole.Write(table); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => string.Empty, "The namespace the service account to list belong to."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/WorkflowCommand.cs b/src/cli/Synapse.Cli/Commands/WorkflowCommand.cs new file mode 100644 index 000000000..8b8ed6d30 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/WorkflowCommand.cs @@ -0,0 +1,47 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Commands.Workflows; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to manage s +/// +public class WorkflowCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "workflow"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Manages workflows"; + + /// + public WorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("workflows"); + this.AddAlias("wf"); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} diff --git a/src/cli/Synapse.Cli/Commands/WorkflowInstanceCommand.cs b/src/cli/Synapse.Cli/Commands/WorkflowInstanceCommand.cs new file mode 100644 index 000000000..1bef04a6a --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/WorkflowInstanceCommand.cs @@ -0,0 +1,50 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Commands.WorkflowInstances; + +namespace Synapse.Cli.Commands; + +/// +/// Represents the used to manage s +/// +public class WorkflowInstanceCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "workflow-instance"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Manages workflow instances"; + + /// + public WorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("workflow-instances"); + this.AddAlias("wfi"); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + //this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + //this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + //this.AddCommand(ActivatorUtilities.CreateInstance(this.ServiceProvider)); + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Commands/WorkflowInstances/DeleteWorkflowInstanceCommand.cs b/src/cli/Synapse.Cli/Commands/WorkflowInstances/DeleteWorkflowInstanceCommand.cs new file mode 100644 index 000000000..e425ae5ac --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/WorkflowInstances/DeleteWorkflowInstanceCommand.cs @@ -0,0 +1,73 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.WorkflowInstances; + +/// +/// Represents the used to delete a single +/// +internal class DeleteWorkflowInstanceCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "delete"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Deletes the specified workflow instance"; + + /// + public DeleteWorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("del"); + this.Add(new Argument("name") { Description = "The name of the workflow instance to delete" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Confirm); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace of the workflow to delete + /// The name of the workflow to delete + /// A boolean indicating whether or not to ask for the user's confirmation + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, bool y) + { + this.EnsureConfigured(); + if (!y) + { + Console.Write($"Are you sure you wish to delete the workflow instance '{name}.{@namespace}'? Press 'y' to confirm, or any other key to cancel: "); + var inputKey = Console.ReadKey(); + Console.WriteLine(); + if (inputKey.Key != ConsoleKey.Y) return; + } + await this.Api.WorkflowInstances.DeleteAsync(name, @namespace); + Console.WriteLine($"workflow-instance/{name} deleted"); + } + + static class CommandOptions + { + + public static Option Namespace => new([ "-n", "--namespace" ], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the workflow instance to delete belongs to."); + + public static Option Confirm => new(["-y", "--yes"], () => false, "Delete the workflow instance without prompting confirmation."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceCommand.cs b/src/cli/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceCommand.cs new file mode 100644 index 000000000..dcc8ecf25 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceCommand.cs @@ -0,0 +1,84 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.WorkflowInstances; + +/// +/// Represents the used to get a specific +/// +internal class GetWorkflowInstanceCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "get"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Get the specified workflow instance"; + + /// + public GetWorkflowInstanceCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer, IYamlSerializer yamlSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.YamlSerializer = yamlSerializer; + this.AddAlias("get"); + this.Add(new Argument("name") { Description = "The name of the workflow instance to get" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Output); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Gets the service used to serialize/deserialize to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } + + /// + /// Handles the + /// + /// The namespace of the workflow instance to get + /// The name of the workflow instance to get + /// The desired output format + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string output) + { + this.EnsureConfigured(); + var workflowInstance = await this.Api.WorkflowInstances.GetAsync(name, @namespace); + string outputText = output.ToLowerInvariant() switch + { + "json" => this.JsonSerializer.SerializeToText(workflowInstance), + "yaml" => this.YamlSerializer.SerializeToText(workflowInstance), + _ => throw new NotSupportedException($"The specified output format '{output}' is not supported"), + }; + AnsiConsole.Markup($"[gray]{outputText.EscapeMarkup()}[/]"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the workflow instance to get belongs to."); + + public static Option Output => new(["-o", "--output"], () => "yaml", "The output format."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceOutputCommand.cs b/src/cli/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceOutputCommand.cs new file mode 100644 index 000000000..27ae89515 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/WorkflowInstances/GetWorkflowInstanceOutputCommand.cs @@ -0,0 +1,86 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.WorkflowInstances; + +/// +/// Represents the used to get the output of a specific +/// +internal class GetWorkflowInstanceOutputCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "get-output"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Get the output of the specified workflow instance"; + + /// + public GetWorkflowInstanceOutputCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer, IYamlSerializer yamlSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.YamlSerializer = yamlSerializer; + this.AddAlias("get-output"); + this.Add(new Argument("name") { Description = "The name of the workflow instance to get the output of" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Output); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Gets the service used to serialize/deserialize to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } + + /// + /// Handles the + /// + /// The namespace of the workflow instance to get + /// The name of the workflow instance to get + /// The desired output format + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string output) + { + this.EnsureConfigured(); + var workflowInstance = await this.Api.WorkflowInstances.GetAsync(name, @namespace); + if (string.IsNullOrWhiteSpace(workflowInstance.Status?.OutputReference)) throw new NullReferenceException($"The workflow instance '{name}.{@namespace}' did not complete."); + var outputDocument = await this.Api.Documents.GetAsync(workflowInstance.Status.OutputReference); + string outputText = output.ToLowerInvariant() switch + { + "json" => this.JsonSerializer.SerializeToText(outputDocument.Content), + "yaml" => this.YamlSerializer.SerializeToText(outputDocument.Content), + _ => throw new NotSupportedException($"The specified output format '{output}' is not supported"), + }; + AnsiConsole.Markup($"[gray]{outputText.EscapeMarkup()}[/]"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the workflow instance to get the output of belongs to."); + + public static Option Output => new(["-o", "--output"], () => "yaml", "The output format."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/WorkflowInstances/ListWorkflowInstancesCommand.cs b/src/cli/Synapse.Cli/Commands/WorkflowInstances/ListWorkflowInstancesCommand.cs new file mode 100644 index 000000000..5c2de3365 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/WorkflowInstances/ListWorkflowInstancesCommand.cs @@ -0,0 +1,125 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using moment.net; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.WorkflowInstances; + +/// +/// Represents the used to lists +/// +internal class ListWorkflowInstancesCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "list"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Lists workflows"; + + /// + public ListWorkflowInstancesCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("ls"); + this.Add(CommandOptions.Namespace); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace the workflow to list belong to + /// A new awaitable + public async Task HandleAsync(string @namespace) + { + this.EnsureConfigured(); + var table = new Table(); + var isEmpty = true; + table.Border(TableBorder.None); + table.AddColumn("NAME", column => + { + column.NoWrap = true; + }); + table.AddColumn("NAMESPACE", column => + { + column.Alignment = Justify.Center; + column.NoWrap = true; + }); + table.AddColumn("DEFINITION", column => + { + column.Alignment = Justify.Center; + column.NoWrap = true; + }); + table.AddColumn("STATUS", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("CREATED AT", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("STARTED AT", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("ENDED AT", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("DURATION", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("OPERATOR", column => + { + column.NoWrap = true; + column.Alignment = Justify.Center; + }); + await foreach (var workflow in await this.Api.WorkflowInstances.ListAsync(@namespace)) + { + isEmpty = false; + table.AddRow + ( + workflow.GetName(), + workflow.GetNamespace()!, + workflow.Spec.Definition.ToString(), + (workflow.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending).ToUpperInvariant(), + workflow.Metadata.CreationTimestamp?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-", + workflow.Status?.StartedAt?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-", + workflow.Status?.EndedAt?.ToOffset(DateTimeOffset.Now.Offset).DateTime.FromNow() ?? "-", + workflow.Status?.StartedAt.HasValue == true && workflow.Status?.EndedAt.HasValue == true ? workflow.Status.EndedAt.Value.Subtract(workflow.Status.StartedAt.Value).ToString("hh\\:mm\\:ss\\.fff") : "-", + workflow.Metadata.Labels?.TryGetValue(SynapseDefaults.Resources.Labels.Operator, out var operatorName) == true && !string.IsNullOrWhiteSpace(operatorName) ? operatorName : "-" + ); + } + if(isEmpty) + { + AnsiConsole.WriteLine(string.IsNullOrWhiteSpace(@namespace) ? "No resource found" : $"No resource found in {@namespace} namespace"); + return; + } + AnsiConsole.Write(table); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => string.Empty, "The namespace the workflow instances to list belong to."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/WorkflowInstances/MonitorWorkflowInstancesCommand.cs b/src/cli/Synapse.Cli/Commands/WorkflowInstances/MonitorWorkflowInstancesCommand.cs new file mode 100644 index 000000000..f034007a9 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/WorkflowInstances/MonitorWorkflowInstancesCommand.cs @@ -0,0 +1,89 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse.Cli.Commands.WorkflowInstances; + +/// +/// Represents the used to monitor a s +/// +internal class MonitorWorkflowInstancesCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "monitor"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Monitors a specific workflow instance"; + + /// + public MonitorWorkflowInstancesCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer, IYamlSerializer yamlSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.YamlSerializer = yamlSerializer; + this.AddAlias("mon"); + this.Add(new Argument("name") { Description = "The name of the workflow instance to monitor" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Output); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Gets the service used to serialize/deserialize to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } + + /// + /// Handles the + /// + /// The name of the workflow instance to monitor + /// The namespace the workflow instance to monitor belong to + /// The desired output format + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string output) + { + this.EnsureConfigured(); + var enumerable = await this.Api.WorkflowInstances.MonitorAsync(name, @namespace); + await foreach (var e in enumerable) + { + string outputText = output.ToLowerInvariant() switch + { + "json" => this.JsonSerializer.SerializeToText(e), + "yaml" => this.YamlSerializer.SerializeToText(e), + _ => throw new NotSupportedException($"The specified output format '{output}' is not supported"), + }; + AnsiConsole.Markup($"[gray]{outputText.EscapeMarkup()}[/]"); + } + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => string.Empty, "The namespace the workflow instances to list belong to."); + + public static Option Output => new(["-o", "--output"], () => "yaml", "The output format."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Workflows/CreateWorkflowCommand.cs b/src/cli/Synapse.Cli/Commands/Workflows/CreateWorkflowCommand.cs new file mode 100644 index 000000000..08bf0cc28 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Workflows/CreateWorkflowCommand.cs @@ -0,0 +1,110 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data; +using Neuroglia.Data.Infrastructure.ResourceOriented; +using ServerlessWorkflow.Sdk.Models; + +namespace Synapse.Cli.Commands.Workflows; + +/// +/// Represents the used to create a new +/// +internal class CreateWorkflowCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "create"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Creates a new workflow."; + + /// + public CreateWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IWorkflowDefinitionReader workflowDefinitionReader) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.WorkflowDefinitionReader = workflowDefinitionReader; + this.Add(CommandOptions.File); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to read s + /// + protected IWorkflowDefinitionReader WorkflowDefinitionReader { get; } + + /// + /// Handles the + /// + /// The path to the file that contains the definition of the workflow to create + /// A new awaitable + public async Task HandleAsync(string file) + { + this.EnsureConfigured(); + Stream? stream; + if (!string.IsNullOrWhiteSpace(file)) stream = File.OpenRead(file); + else throw new InvalidOperationException("You must specify exactly one of the following options: --file"); + var workflowDefinition = await this.WorkflowDefinitionReader.ReadAsync(stream); + Workflow? workflow = null; + try { workflow = await this.Api.Workflows.GetAsync(workflowDefinition.Document.Name, workflowDefinition.Document.Namespace); } + catch { } + if (workflow == null) + { + workflow = new() + { + Metadata = new() + { + Namespace = workflowDefinition.Document.Namespace, + Name = workflowDefinition.Document.Name + }, + Spec = new() + { + Versions = [workflowDefinition] + } + }; + workflow = await this.Api.Workflows.CreateAsync(workflow); + } + else + { + var originalWorkflow = workflow.Clone()!; + workflow.Spec.Versions.Add(workflowDefinition); + var patch = JsonPatchUtility.CreateJsonPatchFromDiff(originalWorkflow, workflow); + workflow = await this.Api.Workflows.PatchAsync(workflowDefinition.Document.Name, workflowDefinition.Document.Namespace, new(PatchType.JsonPatch, patch)); + } + Console.WriteLine($"workflow/{workflow.GetName()}:{workflowDefinition.Document.Version} created"); + await stream.DisposeAsync(); + } + + static class CommandOptions + { + + public static Option File + { + get + { + var option = new Option("--file") + { + Description = "The file that contains the definition of the workflow to create." + }; + option.AddAlias("-f"); + return option; + } + } + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Workflows/DeleteWorkflowCommand.cs b/src/cli/Synapse.Cli/Commands/Workflows/DeleteWorkflowCommand.cs new file mode 100644 index 000000000..56138f45f --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Workflows/DeleteWorkflowCommand.cs @@ -0,0 +1,94 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Workflows; + +/// +/// Represents the used to delete a single +/// +internal class DeleteWorkflowCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "delete"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Deletes the specified workflow"; + + /// + public DeleteWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("del"); + this.Add(new Argument("name") { Description = "The name of the workflow to delete" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Version); + this.Add(CommandOptions.Confirm); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace of the workflow to delete + /// The name of the workflow to delete + /// The version of the workflow to delete + /// A boolean indicating whether or not to ask for the user's confirmation + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string version, bool y) + { + this.EnsureConfigured(); + if (!y) + { + if (string.IsNullOrWhiteSpace(version)) Console.Write($"Are you sure you wish to delete all version of the workflow '{name}.{@namespace}'? Press 'y' to confirm, or any other key to cancel: "); + else Console.Write($"Are you sure you wish to delete the workflow '{name}.{@namespace}:{version}'? Press 'y' to confirm, or any other key to cancel: "); + var inputKey = Console.ReadKey(); + Console.WriteLine(); + if (inputKey.Key != ConsoleKey.Y) return; + } + if (string.IsNullOrWhiteSpace(version)) + { + await this.Api.Workflows.DeleteAsync(name, @namespace); + Console.WriteLine($"workflow/{name} deleted"); + } + else + { + var workflow = await this.Api.Workflows.GetAsync(name, @namespace) ?? throw new NullReferenceException($"Failed to find the specified workflow '{name}.{@namespace}'"); + var definition = workflow.Spec.Versions.FirstOrDefault(v => v.Document.Version == version) ?? throw new NullReferenceException($"Failed to find the specified workflow version '{name}.{@namespace}:{version}'"); + var originalWorkflow = workflow.Clone()!; + workflow.Spec.Versions.Remove(definition); + var patch = JsonPatchUtility.CreateJsonPatchFromDiff(originalWorkflow, workflow); + await this.Api.Workflows.PatchAsync(name, @namespace, new(PatchType.JsonPatch, patch)); + Console.WriteLine($"workflow/{name}:{version} deleted"); + } + } + + static class CommandOptions + { + + public static Option Namespace => new([ "-n", "--namespace" ], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the workflow to delete belongs to"); + + public static Option Version => new(["-v", "--version"], () => string.Empty, "The version of the workflow to delete. Note that failing to specify the version will delete all version of the specified workflow"); + + public static Option Confirm => new(["-y", "--yes"], () => false, "Delete the workflow(s) without prompting confirmation"); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Workflows/GetWorkflowCommand.cs b/src/cli/Synapse.Cli/Commands/Workflows/GetWorkflowCommand.cs new file mode 100644 index 000000000..102bc1b4a --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Workflows/GetWorkflowCommand.cs @@ -0,0 +1,97 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Commands.Workflows; + +/// +/// Represents the used to get a specific +/// +internal class GetWorkflowCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "get"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Get the specified workflow"; + + /// + public GetWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer, IYamlSerializer yamlSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.YamlSerializer = yamlSerializer; + this.AddAlias("get"); + this.Add(new Argument("name") { Description = "The name of the workflow to get" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Version); + this.Add(CommandOptions.Format); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Gets the service used to serialize/deserialize to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } + + /// + /// Handles the + /// + /// The namespace of the workflow to get + /// The name of the workflow to get + /// The version of the workflow to get + /// The desired output format + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string version, string output) + { + this.EnsureConfigured(); + object outputData; + if (string.IsNullOrWhiteSpace(version)) + { + outputData = await this.Api.Workflows.GetAsync(name, @namespace); + } + else + { + var workflow = await this.Api.Workflows.GetAsync(name, @namespace) ?? throw new NullReferenceException($"Failed to find the specified workflow '{name}.{@namespace}'"); + outputData = workflow.Spec.Versions.FirstOrDefault(v => v.Document.Version == version) ?? throw new NullReferenceException($"Failed to find the specified workflow version '{name}.{@namespace}:{version}'"); + } + var outputText = output.ToLowerInvariant() switch + { + "json" => this.JsonSerializer.SerializeToText(outputData), + "yaml" => this.YamlSerializer.SerializeToText(outputData), + _ => throw new NotSupportedException($"The specified output format '{output}' is not supported") + }; + AnsiConsole.Markup($"[gray]{outputText.EscapeMarkup()}[/]"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the workflow to get belongs to."); + + public static Option Version => new(["-v", "--version"], () => string.Empty, "The version of the workflow to get. Setting the version allows the retrieval of a specific version instead."); + + public static Option Format => new(["-o", "--output"], () => "yaml", "The output format."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Workflows/ListWorkflowsCommand.cs b/src/cli/Synapse.Cli/Commands/Workflows/ListWorkflowsCommand.cs new file mode 100644 index 000000000..533438080 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Workflows/ListWorkflowsCommand.cs @@ -0,0 +1,104 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Workflows; + +/// +/// Represents the used to lists +/// +internal class ListWorkflowsCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "list"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Lists workflows"; + + /// + public ListWorkflowsCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.AddAlias("ls"); + this.Add(CommandOptions.Namespace); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Handles the + /// + /// The namespace the workflow to list belong to + /// A new awaitable + public async Task HandleAsync(string @namespace) + { + this.EnsureConfigured(); + var table = new Table(); + var isEmpty = true; + table.Border(TableBorder.None); + table.AddColumn("NAME"); + table.AddColumn("NAMESPACE"); + table.AddColumn("LATEST", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("VERSIONS", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("SCHEDULE", column => + { + column.Alignment = Justify.Center; + }); + table.AddColumn("OPERATOR", column => + { + column.Alignment = Justify.Center; + }); + await foreach (var workflow in await this.Api.Workflows.ListAsync(@namespace)) + { + isEmpty = false; + table.AddRow + ( + workflow.GetName(), + workflow.GetNamespace()!, + workflow.Spec.Versions.GetLatest().Document.Version, + workflow.Spec.Versions.Count.ToString(), + workflow.Spec.Versions.GetLatest().Schedule == null + ? "-" + : workflow.Spec.Versions.GetLatest().Schedule?.After?.ToString() + ?? workflow.Spec.Versions.GetLatest().Schedule?.Cron + ?? "events", + workflow.Metadata.Labels?.TryGetValue(SynapseDefaults.Resources.Labels.Operator, out var @operator) == true ? @operator : "-" + ); + } + if (isEmpty) + { + AnsiConsole.WriteLine(string.IsNullOrWhiteSpace(@namespace) ? "No resource found" : $"No resource found in {@namespace} namespace"); + return; + } + AnsiConsole.Write(table); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => string.Empty, "The namespace the workflow to list belong to."); + + } + +} diff --git a/src/cli/Synapse.Cli/Commands/Workflows/RunWorkflowCommand.cs b/src/cli/Synapse.Cli/Commands/Workflows/RunWorkflowCommand.cs new file mode 100644 index 000000000..4b89bfb63 --- /dev/null +++ b/src/cli/Synapse.Cli/Commands/Workflows/RunWorkflowCommand.cs @@ -0,0 +1,103 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia; +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Cli.Commands.Workflows; + +/// +/// Represents the used to run a workflow +/// +internal class RunWorkflowCommand + : Command +{ + + /// + /// Gets the 's name + /// + public const string CommandName = "run"; + /// + /// Gets the 's description + /// + public const string CommandDescription = "Runs a workflow"; + + /// + /// Initializes a new + /// + /// The current + /// The service used to create s + /// The service used to interact with the remote Synapse API + /// The service used to serialize/deserialize to/from JSON + public RunWorkflowCommand(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, ISynapseApiClient api, IJsonSerializer jsonSerializer) + : base(serviceProvider, loggerFactory, api, CommandName, CommandDescription) + { + this.JsonSerializer = jsonSerializer; + this.Add(new Argument("name") { Description = "The name of the workflow to run" }); + this.Add(CommandOptions.Namespace); + this.Add(CommandOptions.Version); + this.Add(CommandOptions.Input); + this.Handler = CommandHandler.Create(this.HandleAsync); + } + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } + + /// + /// Handles the + /// + /// The name of the workflow to run + /// The namespace the workflow to run belongs to. Defaults to 'default'. + /// The version of the workflow to run + /// The input data JSON + /// A new awaitable + public async Task HandleAsync(string name, string @namespace, string version, string input) + { + this.EnsureConfigured(); + var inputData = new EquatableDictionary { }; + if (!string.IsNullOrWhiteSpace(input)) inputData = this.JsonSerializer.Deserialize>(input); + var instance = await this.Api.WorkflowInstances.CreateAsync(new() + { + Metadata = new() + { + Namespace = @namespace, + Name = $"{name}-" + }, + Spec = new() + { + Definition = new() + { + Namespace = @namespace, + Name = name, + Version = version + }, + Input = inputData + } + }); + Console.WriteLine($"workflow-instance/{instance.GetName()} created"); + } + + static class CommandOptions + { + + public static Option Namespace => new(["-n", "--namespace"], () => Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, "The namespace the workflow to run belongs to. Defaults to 'default'."); + + public static Option Version => new(["-v", "--version"], "The version of the workflow to run."); + + public static Option Input => new(["-i", "--input"], "The workflow's input data JSON."); + + } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Configuration/ApiConfiguration.cs b/src/cli/Synapse.Cli/Configuration/ApiConfiguration.cs new file mode 100644 index 000000000..0890ad0e7 --- /dev/null +++ b/src/cli/Synapse.Cli/Configuration/ApiConfiguration.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Configuration; + +/// +/// Represents the named options used to configure a Synapse API to connect to using the Synapse CLI +/// +[DataContract] +public class ApiConfiguration +{ + + /// + /// Gets/sets the uri that references the API server to connect to + /// + [DataMember(Name = "server", Order = 1), JsonPropertyOrder(1), JsonPropertyName("server"), YamlMember(Alias = "server", Order = 1)] + public required virtual Uri Server { get; set; } + + /// + /// Gets/sets the token used to authenticate on the API server + /// + [DataMember(Name = "token", Order = 2), JsonPropertyOrder(2), JsonPropertyName("token"), YamlMember(Alias = "token", Order = 2)] + public required virtual string Token { get; set; } + +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Configuration/ApplicationApiOptions.cs b/src/cli/Synapse.Cli/Configuration/ApplicationApiOptions.cs new file mode 100644 index 000000000..8ff4e75ce --- /dev/null +++ b/src/cli/Synapse.Cli/Configuration/ApplicationApiOptions.cs @@ -0,0 +1,36 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Configuration; + +/// +/// Represents the object used to configure the Synapse API used by the CLI +/// +[DataContract] +public record ApplicationApiOptions +{ + + /// + /// Gets/sets a name/value mapping of all configured APIs + /// + [Required, MinLength(1)] + [DataMember(Name = "configurations", Order = 1), JsonPropertyOrder(1), JsonPropertyName("configurations"), YamlMember(Alias = "configurations", Order = 1)] + public virtual IDictionary Configurations { get; set; } = new Dictionary(); + + /// + /// Gets/sets the name of the API currently used by the Synapse CLI + /// + [DataMember(Name = "current", Order = 2), JsonPropertyOrder(2), JsonPropertyName("current"), YamlMember(Alias = "current", Order = 2)] + public string? Current { get; set; } + +} diff --git a/src/cli/Synapse.Cli/Configuration/ApplicationOptions.cs b/src/cli/Synapse.Cli/Configuration/ApplicationOptions.cs new file mode 100644 index 000000000..9d0467904 --- /dev/null +++ b/src/cli/Synapse.Cli/Configuration/ApplicationOptions.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Cli.Configuration; + +/// +/// Represents the options used to configure the Synapse CLI application +/// +[DataContract] +public record ApplicationOptions +{ + + /// + /// Gets/sets the name of the API currently used by the Synapse CLI + /// + [DataMember(Name = "api", Order = 1), JsonPropertyOrder(1), JsonPropertyName("api"), YamlMember(Alias = "api", Order = 1)] + public ApplicationApiOptions Api { get; set; } = new(); + +} diff --git a/src/cli/Synapse.Cli/Extensions/IServiceCollectionExtensions.cs b/src/cli/Synapse.Cli/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 000000000..8d46805c6 --- /dev/null +++ b/src/cli/Synapse.Cli/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,44 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia; + +namespace Synapse.Cli; + +/// +/// Defines extensions for s +/// +public static class IServiceCollectionExtensions +{ + + /// + /// Adds and configures all s loaded from the specified assemblies + /// + /// The to configure + /// The configured + public static IServiceCollection AddCliCommands(this IServiceCollection services) + { + foreach (var commandType in TypeCacheUtil.FindFilteredTypes + ( + "synctl-cmd", + t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType && typeof(Command).IsAssignableFrom(t) && t.IsPublic, + typeof(Commands.Command).Assembly) + ) + { + services.AddScoped(typeof(Command), commandType); + } + return services; + } + +} + diff --git a/src/cli/Synapse.Cli/Program.cs b/src/cli/Synapse.Cli/Program.cs new file mode 100644 index 000000000..763fa967c --- /dev/null +++ b/src/cli/Synapse.Cli/Program.cs @@ -0,0 +1,63 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +var parser = BuildCommandLineParser(); +await parser.InvokeAsync(args); + +static Parser BuildCommandLineParser() +{ + var configuration = new ConfigurationBuilder() + .AddYamlFile(CliConstants.ConfigurationFileName, true, true) + .Build(); + var services = new ServiceCollection(); + ConfigureServices(services, configuration); + var serviceProvider = services.BuildServiceProvider(); + var scope = serviceProvider.CreateScope(); + var rootCommand = new RootCommand(); + foreach (var command in scope.ServiceProvider.GetServices()) rootCommand.AddCommand(command); + return new CommandLineBuilder(rootCommand) + .UseDefaults() + .UseExceptionHandler((ex, context) => + { + AnsiConsole.Markup($"[red]{ex.Message.EscapeMarkup()}[/]"); + var inner = ex.InnerException; + while (inner != null) + { + AnsiConsole.Markup($"[red]{inner.Message.EscapeMarkup()}[/]"); + inner = inner.InnerException; + } + }) + .Build(); +} + +static void ConfigureServices(IServiceCollection services, IConfiguration configuration) +{ + var applicationOptions = new ApplicationOptions(); + configuration.Bind(applicationOptions); + services.AddSingleton(configuration); + services.Configure(configuration); + services.AddLogging(); + services.Configure(options => + { + options.WriteIndented = true; + }); + services.AddSynapseHttpApiClient(http => + { + if (string.IsNullOrWhiteSpace(applicationOptions.Api.Current)) return; + var apiConfiguration = applicationOptions.Api.Configurations[applicationOptions.Api.Current]; + http.BaseAddress = apiConfiguration.Server; + http.TokenFactory = provider => Task.FromResult(apiConfiguration.Token)!; + }); + services.AddCliCommands(); + services.AddSingleton(); +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Properties/launchSettings.json b/src/cli/Synapse.Cli/Properties/launchSettings.json new file mode 100644 index 000000000..75da2d6c3 --- /dev/null +++ b/src/cli/Synapse.Cli/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Synapse.Cli": { + "commandName": "Project", + "commandLineArgs": "workflow create --file fake.yaml" + } + } +} \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Sample/workflow-fake-2.yaml b/src/cli/Synapse.Cli/Sample/workflow-fake-2.yaml new file mode 100644 index 000000000..aba12175b --- /dev/null +++ b/src/cli/Synapse.Cli/Sample/workflow-fake-2.yaml @@ -0,0 +1,165 @@ +document: + dsl: '0.10' + namespace: default + name: fake-name + version: '0.2.0' + title: Fake Title + summary: Fake MD summary + tags: + fakeTagName: fakeTagValue +use: + authentications: + fakeBasic: + basic: + username: fake-user + password: fake-password + fakeBearer: + bearer: + token: fake-token + fakeOAuth2: + oauth2: + authority: https://fake-authority.com/ + grant: client_credentials + client: + id: fake-client-id + secret: fake-client-secret + extensions: + fakeLoggingExtension: + extend: all + when: fake-expression + before: + - fake-http-call: + call: http + with: + method: post + uri: https://fake.log.collector.com + body: + message: ${ "Executing task '\($task.reference)'..." } + after: + - fake-http-call: + call: http + with: + method: post + uri: https://fake.log.collector.com + body: + message: ${ "Executed task '\($task.reference)'..." } + functions: + fakeFunction1: + call: http + with: + method: post + uri: https://test.com + fakeFunction2: + run: + shell: + command: echo "Hello, World!" + secrets: + - fake-secret +do: +- todo-1: + call: http + with: + method: get + uri: https://unit-tests.serverlessworkflow.io +- todo-2: + emit: + event: + with: + type: io.serverlessworkflow.unit-tests.fake.event.type.v1 +- todo-3: + for: + each: color + in: .colors + at: index + do: + - fake-http-call: + set: + processed: .processed + [$color] +- todo-4: + listen: + to: + any: + - with: + foo: bar + - with: + foo: bar + bar: baz +- todo-5: + raise: + error: + type: fake-error-type + title: fake-error-title + status: "400" +- todo-6: + run: + container: + image: fake-image:latest + command: fake command --arg1 arg1 + environment: + ASPNET_ENVIRONMENT: Development +- todo-7: + run: + shell: + command: fake command --arg1 arg1 + arguments: + - --arg2 arg2 + environment: + ASPNET_ENVIRONMENT: Development +- todo-8: + run: + script: + language: js + code: console.log("Hello, World!") +- todo-9: + run: + workflow: + namespace: default + name: fake-workflow + version: '1.0.0' + input: + foo: bar +- todo-10: + set: + foo: bar + bar: + baz: foo +- todo-11: + switch: + - case-1: + when: fake-condition + then: continue + - case-2: + when: another-fake-condition + then: exit + - default: + then: end +- todo-12: + try: + - setFoo: + set: + foo: bar + catch: {} +- todo-13: + wait: + minutes: 5 +- todo-14: + do: + - todo-14-1: + call: http + with: + method: get + uri: https://unit-tests.serverlessworkflow.io + - todo-14-2: + emit: + event: + with: + type: io.serverlessworkflow.unit-tests.fake.event.type.v1 + - todo-14-3: + for: + each: color + in: .colors + at: index + do: + - setProcessed: + set: + processed: .processed + [$color] \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Sample/workflow-fake.yaml b/src/cli/Synapse.Cli/Sample/workflow-fake.yaml new file mode 100644 index 000000000..68b2255ee --- /dev/null +++ b/src/cli/Synapse.Cli/Sample/workflow-fake.yaml @@ -0,0 +1,165 @@ +document: + dsl: '0.10' + namespace: default + name: fake-name + version: '0.1.0' + title: Fake Title + summary: Fake MD summary + tags: + fakeTagName: fakeTagValue +use: + authentications: + fakeBasic: + basic: + username: fake-user + password: fake-password + fakeBearer: + bearer: + token: fake-token + fakeOAuth2: + oauth2: + authority: https://fake-authority.com/ + grant: client_credentials + client: + id: fake-client-id + secret: fake-client-secret + extensions: + fakeLoggingExtension: + extend: all + when: fake-expression + before: + - fake-http-call: + call: http + with: + method: post + uri: https://fake.log.collector.com + body: + message: ${ "Executing task '\($task.reference)'..." } + after: + - fake-http-call: + call: http + with: + method: post + uri: https://fake.log.collector.com + body: + message: ${ "Executed task '\($task.reference)'..." } + functions: + fakeFunction1: + call: http + with: + method: post + uri: https://test.com + fakeFunction2: + run: + shell: + command: echo "Hello, World!" + secrets: + - fake-secret +do: +- todo-1: + call: http + with: + method: get + uri: https://unit-tests.serverlessworkflow.io +- todo-2: + emit: + event: + with: + type: io.serverlessworkflow.unit-tests.fake.event.type.v1 +- todo-3: + for: + each: color + in: .colors + at: index + do: + - fake-http-call: + set: + processed: .processed + [$color] +- todo-4: + listen: + to: + any: + - with: + foo: bar + - with: + foo: bar + bar: baz +- todo-5: + raise: + error: + type: fake-error-type + title: fake-error-title + status: "400" +- todo-6: + run: + container: + image: fake-image:latest + command: fake command --arg1 arg1 + environment: + ASPNET_ENVIRONMENT: Development +- todo-7: + run: + shell: + command: fake command --arg1 arg1 + arguments: + - --arg2 arg2 + environment: + ASPNET_ENVIRONMENT: Development +- todo-8: + run: + script: + language: js + code: console.log("Hello, World!") +- todo-9: + run: + workflow: + namespace: default + name: fake-workflow + version: '1.0.0' + input: + foo: bar +- todo-10: + set: + foo: bar + bar: + baz: foo +- todo-11: + switch: + - case-1: + when: fake-condition + then: continue + - case-2: + when: another-fake-condition + then: exit + - default: + then: end +- todo-12: + try: + - setFoo: + set: + foo: bar + catch: {} +- todo-13: + wait: + minutes: 5 +- todo-14: + do: + - todo-14-1: + call: http + with: + method: get + uri: https://unit-tests.serverlessworkflow.io + - todo-14-2: + emit: + event: + with: + type: io.serverlessworkflow.unit-tests.fake.event.type.v1 + - todo-14-3: + for: + each: color + in: .colors + at: index + do: + - setProcessed: + set: + processed: .processed + [$color] \ No newline at end of file diff --git a/src/cli/Synapse.Cli/Services/IOptionsManager.cs b/src/cli/Synapse.Cli/Services/IOptionsManager.cs new file mode 100644 index 000000000..4462838e2 --- /dev/null +++ b/src/cli/Synapse.Cli/Services/IOptionsManager.cs @@ -0,0 +1,54 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Cli.Configuration; + +namespace Synapse.Cli.Services; + +/// +/// Defines the service used to manage the application's options +/// +public interface IOptionsManager +{ + + /// + /// Updates the application's options + /// + /// The updated to persist + /// A + /// A new awaitable + Task UpdateOptionsAsync(ApplicationOptions options, CancellationToken cancellationToken = default); + +} + +/// +/// Represents the default implementation of the interface +/// +/// The service used to serialize/deserialize data to/from YAML +public class OptionsManager(IYamlSerializer serializer) + : IOptionsManager +{ + + /// + /// Gets the service used to serialize/deserialize data to/from YAML + /// + protected IYamlSerializer Serializer { get; } = serializer; + + /// + public virtual async Task UpdateOptionsAsync(ApplicationOptions options, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(options); + var yaml = this.Serializer.SerializeToText(options); + await File.WriteAllTextAsync(CliConstants.ConfigurationFileName, yaml, cancellationToken); + } +} diff --git a/src/cli/Synapse.Cli/Synapse.Cli.csproj b/src/cli/Synapse.Cli/Synapse.Cli.csproj new file mode 100644 index 000000000..b017ebf20 --- /dev/null +++ b/src/cli/Synapse.Cli/Synapse.Cli.csproj @@ -0,0 +1,50 @@ + + + + net8.0 + enable + enable + Exe + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse cli synctl + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + synctl + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git a/src/cli/Synapse.Cli/Usings.cs b/src/cli/Synapse.Cli/Usings.cs new file mode 100644 index 000000000..d3e8ba565 --- /dev/null +++ b/src/cli/Synapse.Cli/Usings.cs @@ -0,0 +1,34 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; +global using Neuroglia.Serialization; +global using ServerlessWorkflow.Sdk.IO; +global using Spectre.Console; +global using Synapse.Api.Client; +global using Synapse.Api.Client.Services; +global using Synapse.Cli; +global using Synapse.Cli.Configuration; +global using Synapse.Cli.Services; +global using Synapse.Resources; +global using System.CommandLine; +global using System.CommandLine.Builder; +global using System.CommandLine.NamingConventionBinder; +global using System.CommandLine.Parsing; +global using System.ComponentModel.DataAnnotations; +global using System.Runtime.Serialization; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using YamlDotNet.Serialization; \ No newline at end of file diff --git a/src/cli/Synapse.Cli/config.yaml b/src/cli/Synapse.Cli/config.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/Synapse.Application/Commands/AuthenticationDefinitionCollections/v1/V1CreateFunctionDefinitionCollectionCommand.cs b/src/core/Synapse.Application/Commands/AuthenticationDefinitionCollections/v1/V1CreateFunctionDefinitionCollectionCommand.cs deleted file mode 100644 index e35fa99e0..000000000 --- a/src/core/Synapse.Application/Commands/AuthenticationDefinitionCollections/v1/V1CreateFunctionDefinitionCollectionCommand.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Application.Queries.AuthenticationDefinitionCollections; - -namespace Synapse.Application.Commands.AuthenticationDefinitionCollections -{ - - ///

- /// Represents the used to create a new - /// - [DataTransferObjectType(typeof(Integration.Commands.AuthenticationDefinitionCollections.V1CreateAuthenticationDefinitionCollectionCommand))] - public class V1CreateAuthenticationDefinitionCollectionCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateAuthenticationDefinitionCollectionCommand() - { - - } - - /// - /// Initializes a new - /// - /// The name of the to create - /// The version of the to create - /// The description of the to create - /// An containing the s the to create is made out of - public V1CreateAuthenticationDefinitionCollectionCommand(string name, string version, string? description, IEnumerable authentications) - { - this.Name = name; - this.Version = version; - this.Description = description; - this.Authentications = authentications.ToList().AsReadOnly(); - } - - /// - /// Gets the name of the to create - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the version of the to create - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the description of the to create - /// - public virtual string? Description { get; protected set; } - - /// - /// Gets an containing the s the to create is made out of - /// - public virtual IReadOnlyCollection Authentications { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateAuthenticationDefinitionCollectionCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1CreateAuthenticationDefinitionCollectionCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository authenticationCollections) - : base(loggerFactory, mediator, mapper) - { - this.AuthenticationCollections = authenticationCollections; - } - - /// - /// Gets the used to manage s - /// - protected IRepository AuthenticationCollections { get; } - - /// - public virtual async Task> HandleAsync(V1CreateAuthenticationDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - SemVersion version = null!; - try - { - var collectionDto = await this.Mediator.ExecuteAndUnwrapAsync(V1GetAuthenticationDefinitionCollectionByIdQuery.Parse(command.Name.ToLowerInvariant().Slugify("-"))); - version = SemVersion.Parse(collectionDto.Version, SemVersionStyles.Any); - } - catch {} - if (version == null) - { - version = SemVersion.Parse(command.Version, SemVersionStyles.Any); - } - else - { - if (SemVersion.ComparePrecedence(SemVersion.Parse(command.Version, SemVersionStyles.Any), version) <= 0) - version = version.WithPatch(version.Patch + 1); - } - var collection = await this.AuthenticationCollections.AddAsync(new V1AuthenticationDefinitionCollection(command.Name, version.ToString(), command.Description, command.Authentications.ToArray()), cancellationToken); - await this.AuthenticationCollections.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(collection)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/CommandHandlerBase.cs b/src/core/Synapse.Application/Commands/CommandHandlerBase.cs deleted file mode 100644 index c68e9a1c1..000000000 --- a/src/core/Synapse.Application/Commands/CommandHandlerBase.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands -{ - - ///

- /// Represents the base class for all implementations - /// - public abstract class CommandHandlerBase - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - protected CommandHandlerBase(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Mediator = mediator; - this.Mapper = mapper; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Correlations/v1/V1CorrelateEventCommand.cs b/src/core/Synapse.Application/Commands/Correlations/v1/V1CorrelateEventCommand.cs deleted file mode 100644 index 449f81533..000000000 --- a/src/core/Synapse.Application/Commands/Correlations/v1/V1CorrelateEventCommand.cs +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Application.Commands.WorkflowInstances; -using Synapse.Application.Queries.Correlations; - -namespace Synapse.Application.Commands.Correlations -{ - - ///

- /// Represents the used to correlate a - /// - [DataTransferObjectType(typeof(Integration.Commands.Correlations.V1CorrelateEventCommand))] - public class V1CorrelateEventCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CorrelateEventCommand() - { - - } - - /// - /// Initializes a new - /// - /// The to correlate - public V1CorrelateEventCommand(V1Event e) - { - this.Event = e; - } - - /// - /// Gets the to correlate - /// - public virtual V1Event Event { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1CorrelateEventCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The used to manage s - public V1CorrelateEventCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository correlations, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.Correlations = correlations; - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Correlations { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task HandleAsync(V1CorrelateEventCommand command, CancellationToken cancellationToken = default) - { - this.Logger.LogInformation("Processing event with id '{eventId}', type '{eventType}' and source '{eventSource}'...", command.Event.Id, command.Event.Type, command.Event.Source); - var correlations = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetEventCorrelationsQuery(command.Event), cancellationToken); - await foreach (var correlation in correlations) - { - this.Logger.LogInformation("Processing correlation with id '{correlationId}'...", correlation.Id); - var matchingCondition = correlation.GetMatchingConditionFor(command.Event)!; - var matchingFilter = matchingCondition.GetMatchingFilterFor(command.Event)!; - var matchingContexts = correlation.Contexts - .Where(c => c.CorrelatesTo(command.Event)) - .ToList(); - switch (correlation.Lifetime) - { - case V1CorrelationLifetime.Singleton: - var matchingContext = matchingContexts.FirstOrDefault(); - if (matchingContext == null) - { - this.Logger.LogInformation("Failed to find a matching correlation context"); - if (correlation.Contexts.Any()) - throw new Exception("Failed to correlate event"); //should not happen - this.Logger.LogInformation("Creating a new correlation context..."); - matchingContext = V1CorrelationContext.CreateFor(command.Event, matchingFilter.CorrelationMappings.Keys); - correlation.AddContext(matchingContext); - await this.Correlations.UpdateAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - this.Logger.LogInformation("Correlation context with id '{contextId}' successfully created", matchingContext.Id); - this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", matchingContext.Id); - } - await this.CorrelateAsync(correlation, matchingContext, command.Event, matchingFilter, cancellationToken); - break; - case V1CorrelationLifetime.Transient: - matchingContext = matchingContexts.FirstOrDefault(); - if(matchingContext == null) - { - this.Logger.LogInformation("Failed to find a matching correlation context"); - this.Logger.LogInformation("Creating a new correlation context..."); - matchingContext = V1CorrelationContext.CreateFor(command.Event, matchingFilter.CorrelationMappings.Keys); - correlation.AddContext(matchingContext); - await this.Correlations.UpdateAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - this.Logger.LogInformation("Correlation context with id '{contextId}' successfully created", matchingContext.Id); - this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", matchingContext.Id); - matchingContexts.Add(matchingContext); - } - this.Logger.LogInformation("Found {matchingContextCount} matching correlation contexts", matchingContexts.Count()); - foreach (var context in matchingContexts) - { - await this.CorrelateAsync(correlation, context, command.Event, matchingFilter, cancellationToken); - } - break; - default: - throw new NotSupportedException($"The specified {nameof(V1CorrelationLifetime)} '{correlation.Lifetime}' is not supported"); - } - } - return this.Ok(); - } - - /// - /// Performs a correlation on a , in a given and using the specified - /// - /// The to perform - /// The in which to perform the - /// The to correlate - /// The used to correlate the - /// A - /// A new awaitable - protected virtual async Task CorrelateAsync(V1Correlation correlation, V1CorrelationContext correlationContext, V1Event e, V1EventFilter filter, CancellationToken cancellationToken = default) - { - this.Logger.LogInformation("Correlating event to context with id '{contextId}'...", correlationContext.Id); - correlation.Correlate(e, filter.CorrelationMappings.Keys); - correlation = await this.Correlations.UpdateAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", correlationContext.Id); - this.Logger.LogInformation("Attempting to complete the correlation with id '{correlationId}' in context with id '{contextId}'...", correlation.Id, correlationContext.Id); - if (!correlation.TryComplete(correlationContext)) - { - this.Logger.LogInformation("Correlations conditions are not met in the specified correlation context"); - return; - } - this.Logger.LogInformation("Correlation with id '{correlationId}' has been completed in context with id '{contextId}. Computing outcome...", correlation.Id, correlationContext.Id); - switch (correlation.Outcome.Type) - { - case V1CorrelationOutcomeType.Start: - await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateWorkflowInstanceCommand(correlation.Outcome.Target, V1WorkflowInstanceActivationType.Trigger, new(), correlationContext, true, null), cancellationToken); - break; - case V1CorrelationOutcomeType.Correlate: - await this.Mediator.ExecuteAndUnwrapAsync(new V1CorrelateWorkflowInstanceCommand(correlation.Outcome.Target, correlationContext), cancellationToken); - break; - default: - throw new NotSupportedException($"The specified {nameof(V1CorrelationOutcomeType)} '{correlation.Outcome.Type}' is not supported"); - } - correlation.ReleaseContext(correlationContext); - correlation = await this.Correlations.UpdateAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - if(correlation.Lifetime == V1CorrelationLifetime.Singleton) - { - this.Logger.LogInformation("The correlation with id '{correlationId}' is a singleton and its context has been released. Disposing of it...", correlation.Id); - await this.Mediator.ExecuteAndUnwrapAsync(new V1DeleteCorrelationCommand(correlation.Id), cancellationToken); - this.Logger.LogInformation("The correlation with id '{correlationId}' has been successfully disposed of", correlation.Id); - } - this.Logger.LogInformation("Correlation outcome successfully computed"); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Correlations/v1/V1CreateCorrelationCommand.cs b/src/core/Synapse.Application/Commands/Correlations/v1/V1CreateCorrelationCommand.cs deleted file mode 100644 index bbf453431..000000000 --- a/src/core/Synapse.Application/Commands/Correlations/v1/V1CreateCorrelationCommand.cs +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Application.Commands.WorkflowInstances; -using System.ComponentModel.DataAnnotations; - -namespace Synapse.Application.Commands.Correlations -{ - - ///

- /// Represents the used to create a new - /// - [DataTransferObjectType(typeof(Integration.Commands.Correlations.V1CreateCorrelationCommand))] - public class V1CreateCorrelationCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateCorrelationCommand() - { - this.Conditions = null!; - this.Outcome = null!; - } - - /// - /// Initializes a new - /// - /// The activation type of the to create - /// The lifetime of the to create - /// The type of evaluation the should use - /// An containing all s the to create is made out of - /// The of the to create - /// The initial of the to create - public V1CreateCorrelationCommand(V1CorrelationActivationType activationType, V1CorrelationLifetime lifetime, V1CorrelationConditionType conditionType, - IEnumerable conditions, V1CorrelationOutcome outcome, V1CorrelationContext? context) - { - this.ActivationType = activationType; - this.Lifetime = lifetime; - this.ConditionType = conditionType; - this.Conditions = conditions.ToList(); - this.Outcome = outcome; - this.Context = context; - } - - /// - /// Gets the activation type of the to create - /// - public virtual V1CorrelationActivationType ActivationType { get; protected set; } - - /// - /// Gets the lifetime of the to create - /// - [Required] - public virtual V1CorrelationLifetime Lifetime { get; protected set; } - - /// - /// Gets the type of evaluation the should use - /// - [Required] - public virtual V1CorrelationConditionType ConditionType { get; protected set; } - - /// - /// Gets an containing all s the to create is made out of - /// - [MinLength(1)] - public virtual ICollection Conditions { get; protected set; } - - /// - /// Gets the of the to create - /// - [Required] - public virtual V1CorrelationOutcome Outcome { get; protected set; } - - /// - /// Gets the initial of the to create - /// - public virtual V1CorrelationContext? Context { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateCorrelationCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1CreateCorrelationCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository correlations) - : base(loggerFactory, mediator, mapper) - { - this.Correlations = correlations; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Correlations { get; } - - /// - public virtual async Task> HandleAsync(V1CreateCorrelationCommand command, CancellationToken cancellationToken = default) - { - var correlation = new V1Correlation(command.ActivationType, command.Lifetime, command.ConditionType, command.Conditions, command.Outcome, command.Context); - correlation = await this.Correlations.AddAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(correlation)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelatedEventCommand.cs b/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelatedEventCommand.cs deleted file mode 100644 index 4f6a8b666..000000000 --- a/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelatedEventCommand.cs +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.Correlations -{ - ///

- /// Represents the used to delete a correlated - /// - [DataTransferObjectType(typeof(Integration.Commands.Correlations.V1DeleteCorrelatedEventCommand))] - public class V1DeleteCorrelatedEventCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1DeleteCorrelatedEventCommand() { } - - /// - /// Initializes a new - /// - /// The id of the the to delete belongs to - /// The id of the the to delete belongs to - /// The id of the to delete - public V1DeleteCorrelatedEventCommand(string correlationId, string contextId,string eventId) - { - this.CorrelationId = correlationId; - this.ContextId = contextId; - this.EventId = eventId; - } - - /// - /// Gets the id of the the to delete belongs to - /// - public virtual string CorrelationId { get; protected set; } = null!; - - /// - /// Gets the id of the the to delete belongs to - /// - public virtual string ContextId { get; protected set; } = null!; - - /// - /// Gets the id of the to delete - /// - public virtual string EventId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1DeleteCorrelatedEventCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1DeleteCorrelatedEventCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository correlations) - : base(loggerFactory, mediator, mapper) - { - this.Correlations = correlations; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Correlations { get; } - - /// - public virtual async Task HandleAsync(V1DeleteCorrelatedEventCommand command, CancellationToken cancellationToken = default) - { - var correlation = await this.Correlations.FindAsync(command.CorrelationId, cancellationToken); - if (correlation == null) throw DomainException.NullReference(typeof(V1Correlation), command.CorrelationId); - var context = correlation.Contexts?.FirstOrDefault(c => c.Id.Equals(command.ContextId, StringComparison.InvariantCultureIgnoreCase)); - if (context == null) throw DomainException.NullReference(typeof(V1CorrelationContext), command.ContextId); - var evt = context.PendingEvents?.FirstOrDefault(e => e.Id.Equals(command.EventId, StringComparison.InvariantCultureIgnoreCase)); - if (evt == null) throw DomainException.NullReference(typeof(V1Event), command.EventId); - correlation.ReleaseEvent(context, evt); - await this.Correlations.UpdateAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelationCommand.cs b/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelationCommand.cs deleted file mode 100644 index 1baa9e9e0..000000000 --- a/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelationCommand.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.Correlations -{ - - ///

- /// Represents the used to delete an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.Correlations.V1DeleteCorrelationCommand))] - public class V1DeleteCorrelationCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1DeleteCorrelationCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to delete - public V1DeleteCorrelationCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to delete - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1DeleteCorrelationCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1DeleteCorrelationCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository correlations) - : base(loggerFactory, mediator, mapper) - { - this.Correlations = correlations; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Correlations { get; } - - /// - public virtual async Task HandleAsync(V1DeleteCorrelationCommand command, CancellationToken cancellationToken = default) - { - var correlation = await this.Correlations.FindAsync(command.Id, cancellationToken); - if (correlation == null) - throw DomainException.NullReference(typeof(V1Correlation), command.Id); - correlation.Delete(); - await this.Correlations.UpdateAsync(correlation, cancellationToken); - await this.Correlations.RemoveAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - return this.Ok(); - } - } - -} diff --git a/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelationContextCommand.cs b/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelationContextCommand.cs deleted file mode 100644 index e296973e0..000000000 --- a/src/core/Synapse.Application/Commands/Correlations/v1/V1DeleteCorrelationContextCommand.cs +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.Correlations -{ - ///

- /// Represents the used to delete a 's - /// - [DataTransferObjectType(typeof(Integration.Commands.Correlations.V1DeleteCorrelationContextCommand))] - public class V1DeleteCorrelationContextCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1DeleteCorrelationContextCommand() { } - - /// - /// Initializes a new - /// - /// The id of the the to delete belongs to - /// The id of the to delete - public V1DeleteCorrelationContextCommand(string correlationId, string contextId) - { - this.CorrelationId = correlationId; - this.ContextId = contextId; - } - - /// - /// Gets the id of the the to delete belongs to - /// - public virtual string CorrelationId { get; protected set; } = null!; - - /// - /// Gets the id of the to delete - /// - public virtual string ContextId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1DeleteCorrelationContextCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1DeleteCorrelationContextCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository correlations) - : base(loggerFactory, mediator, mapper) - { - this.Correlations = correlations; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Correlations { get; } - - /// - public virtual async Task HandleAsync(V1DeleteCorrelationContextCommand command, CancellationToken cancellationToken = default) - { - var correlation = await this.Correlations.FindAsync(command.CorrelationId, cancellationToken); - if (correlation == null) throw DomainException.NullReference(typeof(V1Correlation), command.CorrelationId); - var context = correlation.Contexts?.FirstOrDefault(c => c.Id.Equals(command.ContextId, StringComparison.InvariantCultureIgnoreCase)); - if(context == null) throw DomainException.NullReference(typeof(V1CorrelationContext), command.ContextId); - correlation.ReleaseContext(context); - await this.Correlations.UpdateAsync(correlation, cancellationToken); - await this.Correlations.SaveChangesAsync(cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/EventDefinitionCollections/v1/V1CreateEventDefinitionCollectionCommand.cs b/src/core/Synapse.Application/Commands/EventDefinitionCollections/v1/V1CreateEventDefinitionCollectionCommand.cs deleted file mode 100644 index a71d4a3b8..000000000 --- a/src/core/Synapse.Application/Commands/EventDefinitionCollections/v1/V1CreateEventDefinitionCollectionCommand.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Application.Queries.EventDefinitionCollections; - -namespace Synapse.Application.Commands.EventDefinitionCollections -{ - - ///

- /// Represents the used to create a new - /// - [DataTransferObjectType(typeof(Integration.Commands.EventDefinitionCollections.V1CreateEventDefinitionCollectionCommand))] - public class V1CreateEventDefinitionCollectionCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateEventDefinitionCollectionCommand() - { - - } - - /// - /// Initializes a new - /// - /// The name of the to create - /// The version of the to create - /// The description of the to create - /// An containing the s the to create is made out of - public V1CreateEventDefinitionCollectionCommand(string name, string version, string? description, IEnumerable events) - { - this.Name = name; - this.Version = version; - this.Description = description; - this.Events = events.ToList().AsReadOnly(); - } - - /// - /// Gets the name of the to create - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the version of the to create - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the description of the to create - /// - public virtual string? Description { get; protected set; } - - /// - /// Gets an containing the s the to create is made out of - /// - public virtual IReadOnlyCollection Events { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateEventDefinitionCollectionCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1CreateEventDefinitionCollectionCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository eventCollections) - : base(loggerFactory, mediator, mapper) - { - this.EventCollections = eventCollections; - } - - /// - /// Gets the used to manage s - /// - protected IRepository EventCollections { get; } - - /// - public virtual async Task> HandleAsync(V1CreateEventDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - SemVersion version = null!; - try - { - var collectionDto = await this.Mediator.ExecuteAndUnwrapAsync(V1GetEventDefinitionCollectionByIdQuery.Parse(command.Name.ToLowerInvariant().Slugify("-"))); - version = SemVersion.Parse(collectionDto.Version, SemVersionStyles.Any); - } - catch {} - if (version == null) - { - version = SemVersion.Parse(command.Version, SemVersionStyles.Any); - } - else - { - if (SemVersion.ComparePrecedence(SemVersion.Parse(command.Version, SemVersionStyles.Any), version) <= 0) - version = version.WithPatch(version.Patch + 1); - } - var collection = await this.EventCollections.AddAsync(new V1EventDefinitionCollection(command.Name, version.ToString(), command.Description, command.Events.ToArray()), cancellationToken); - await this.EventCollections.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(collection)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Events/v1/V1PublishEventCommand.cs b/src/core/Synapse.Application/Commands/Events/v1/V1PublishEventCommand.cs deleted file mode 100644 index 15581cc44..000000000 --- a/src/core/Synapse.Application/Commands/Events/v1/V1PublishEventCommand.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.Events -{ - - ///

- /// Represents the used to publish a new - /// - [DataTransferObjectType(typeof(Integration.Commands.Events.V1PublishEventCommand))] - public class V1PublishEventCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1PublishEventCommand() { } - - /// - /// Initializes a new - /// - /// The to publish - public V1PublishEventCommand(V1Event e) - { - this.Event = e; - } - - /// - /// Gets the to publish - /// - public virtual V1Event Event { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1PublishEventCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The service used to publish and subscribe to s - public V1PublishEventCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, ICloudEventBus cloudEventBus) - : base(loggerFactory, mediator, mapper) - { - this.CloudEventBus = cloudEventBus; - } - - /// - /// Gets the service used to publish and subscribe to s - /// - protected ICloudEventBus CloudEventBus { get; } - - /// - public virtual async Task HandleAsync(V1PublishEventCommand command, CancellationToken cancellationToken = default) - { - var e = command.Event.AsCloudEvent(); - await this.CloudEventBus.PublishAsync(e, cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/FunctionDefinitionCollections/v1/V1CreateFunctionDefinitionCollectionCommand.cs b/src/core/Synapse.Application/Commands/FunctionDefinitionCollections/v1/V1CreateFunctionDefinitionCollectionCommand.cs deleted file mode 100644 index ec01e03dc..000000000 --- a/src/core/Synapse.Application/Commands/FunctionDefinitionCollections/v1/V1CreateFunctionDefinitionCollectionCommand.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Application.Queries.FunctionDefinitionCollections; - -namespace Synapse.Application.Commands.FunctionDefinitionCollections -{ - - ///

- /// Represents the used to create a new - /// - [DataTransferObjectType(typeof(Integration.Commands.FunctionDefinitionCollections.V1CreateFunctionDefinitionCollectionCommand))] - public class V1CreateFunctionDefinitionCollectionCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateFunctionDefinitionCollectionCommand() - { - - } - - /// - /// Initializes a new - /// - /// The name of the to create - /// The version of the to create - /// The description of the to create - /// An containing the s the to create is made out of - public V1CreateFunctionDefinitionCollectionCommand(string name, string version, string? description, IEnumerable functions) - { - this.Name = name; - this.Version = version; - this.Description = description; - this.Functions = functions.ToList().AsReadOnly(); - } - - /// - /// Gets the name of the to create - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the version of the to create - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the description of the to create - /// - public virtual string? Description { get; protected set; } - - /// - /// Gets an containing the s the to create is made out of - /// - public virtual IReadOnlyCollection Functions { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateFunctionDefinitionCollectionCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1CreateFunctionDefinitionCollectionCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository functionCollections) - : base(loggerFactory, mediator, mapper) - { - this.FunctionCollections = functionCollections; - } - - /// - /// Gets the used to manage s - /// - protected IRepository FunctionCollections { get; } - - /// - public virtual async Task> HandleAsync(V1CreateFunctionDefinitionCollectionCommand command, CancellationToken cancellationToken = default) - { - SemVersion version = null!; - try - { - var collectionDto = await this.Mediator.ExecuteAndUnwrapAsync(V1GetFunctionDefinitionCollectionByIdQuery.Parse(command.Name.ToLowerInvariant().Slugify("-"))); - version = SemVersion.Parse(collectionDto.Version, SemVersionStyles.Any); - } - catch {} - if (version == null) - { - version = SemVersion.Parse(command.Version, SemVersionStyles.Any); - } - else - { - if (SemVersion.ComparePrecedence(SemVersion.Parse(command.Version, SemVersionStyles.Any), version) <= 0) - version = version.WithPatch(version.Patch + 1); - } - var collection = await this.FunctionCollections.AddAsync(new V1FunctionDefinitionCollection(command.Name, version.ToString(), command.Description, command.Functions.ToArray()), cancellationToken); - await this.FunctionCollections.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(collection)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Generic/v1/V1DeleteCommand.cs b/src/core/Synapse.Application/Commands/Generic/v1/V1DeleteCommand.cs deleted file mode 100644 index 24b4aee41..000000000 --- a/src/core/Synapse.Application/Commands/Generic/v1/V1DeleteCommand.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain; - -namespace Synapse.Application.Commands.Generic -{ - - ///

- /// Represents the used to delete an existing by id - /// - /// The type of the to delete - /// The type of id used to uniquely identify the to delete - public class V1DeleteCommand - : Command - where TAggregate : class, IIdentifiable - where TKey : IEquatable - { - - /// - /// Initializes a new - /// - protected V1DeleteCommand() - { - this.Id = default!; - } - - /// - /// Initializes a new - /// - /// The id of the entity to delete - public V1DeleteCommand(TKey id) - { - this.Id = id; - } - - /// - /// Gets the id of the entity to delete - /// - public virtual TKey Id { get; protected set; } - - } - - /// - /// Represents the service used to handle instances - /// - /// The type of the to delete - /// The key used to uniquely identify the to delete - public class DeleteCommandHandler - : CommandHandlerBase, - ICommandHandler> - where TAggregate : class, IIdentifiable, IDeletable - where TKey : IEquatable - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage the entity to delete - public DeleteCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper) - { - this.Repository = repository; - } - - /// - /// Gets the used to manage the entity to delete - /// - protected IRepository Repository { get; } - - /// - public virtual async Task HandleAsync(V1DeleteCommand command, CancellationToken cancellationToken = default) - { - if (!await this.Repository.ContainsAsync(command.Id, cancellationToken)) - throw DomainException.NullReference(typeof(TAggregate), command.Id); - var aggregate = await this.Repository.FindAsync(command.Id, cancellationToken); - if (aggregate == null) - throw DomainException.NullReference(typeof(TAggregate), command.Id); - aggregate.Delete(); - await this.Repository.UpdateAsync(aggregate, cancellationToken); - await this.Repository.RemoveAsync(aggregate, cancellationToken); - await this.Repository.SaveChangesAsync(cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Generic/v1/V1PatchCommand.cs b/src/core/Synapse.Application/Commands/Generic/v1/V1PatchCommand.cs deleted file mode 100644 index c3f2eb571..000000000 --- a/src/core/Synapse.Application/Commands/Generic/v1/V1PatchCommand.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.JsonPatch; -using Microsoft.AspNetCore.JsonPatch.Adapters; - -namespace Synapse.Application.Commands.Generic -{ - - ///

- /// Represents the used to patch an existing - /// - /// The type of to patch - /// The type of to return - /// The type of id used to uniquely identify the to patch - public class V1PatchCommand - : Command - where TAggregate : class, IIdentifiable - where TKey : IEquatable - { - - /// - /// Initializes a new - /// - protected V1PatchCommand() - { - this.Id = default!; - this.Patch = null!; - } - - /// - /// Initializes a new - /// - /// The id of the entity to patch - /// The to apply - public V1PatchCommand(TKey id, JsonPatchDocument patch) - { - this.Id = id; - this.Patch = patch; - } - - /// - /// Gets the id of the entity to patch - /// - public virtual TKey Id { get; protected set; } - - /// - /// Gets the to apply - /// - public virtual JsonPatchDocument Patch { get; protected set; } - - } - - /// - /// Represents the service used to handle instances - /// - /// The type of to patch - /// The type of to return - /// The type of key used to uniquely identify the to patch - public class PatchCommandHandler - : CommandHandlerBase, - ICommandHandler, TResult> - where TAggregate : class, IIdentifiable - where TKey : IEquatable - { - - /// - public PatchCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository, IObjectAdapter adapter) - : base(loggerFactory, mediator, mapper) - { - this.Repository = repository; - this.Adapter = adapter; - } - - /// - /// Gets the used to manage the s of the specified type - /// - protected IRepository Repository { get; } - - /// - /// Gets the service used to apply JSON patches - /// - protected IObjectAdapter Adapter { get; } - - /// - public virtual async Task> HandleAsync(V1PatchCommand command, CancellationToken cancellationToken) - { - var aggregate = await this.Repository.FindAsync(command.Id, cancellationToken); - if (aggregate == null) - throw DomainException.NullReference(typeof(TAggregate), command.Id); - command.Patch.ApplyTo(aggregate, this.Adapter); - aggregate = await this.Repository.UpdateAsync(aggregate, cancellationToken); - await this.Repository.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(aggregate)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Schedules/v1/V1CompleteScheduleOccurenceCommand.cs b/src/core/Synapse.Application/Commands/Schedules/v1/V1CompleteScheduleOccurenceCommand.cs deleted file mode 100644 index 0c34354e5..000000000 --- a/src/core/Synapse.Application/Commands/Schedules/v1/V1CompleteScheduleOccurenceCommand.cs +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk; - -namespace Synapse.Application.Commands.Schedules -{ - - ///

- /// Represents the used to complete a 's occurence - /// - - public class V1CompleteScheduleOccurenceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CompleteScheduleOccurenceCommand() { } - - /// - /// Initializes a new - /// - /// The id of the to complete an occurence of - /// The id of the that has been executed - public V1CompleteScheduleOccurenceCommand(string scheduleId, string workflowInstanceId) - { - this.ScheduleId = scheduleId; - this.WorkflowInstanceId = workflowInstanceId; - } - - /// - /// Gets the id of the to complete an occurence of - /// - public virtual string ScheduleId { get; protected set; } = null!; - - /// - /// Gets the id of the that has been executed - /// - public virtual string WorkflowInstanceId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1CompleteScheduleOccurenceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage background jobs - public V1CompleteScheduleOccurenceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository schedules, IBackgroundJobManager backgroundJobManager) - : base(loggerFactory, mediator, mapper) - { - this.Schedules = schedules; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Schedules { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - public virtual async Task> HandleAsync(V1CompleteScheduleOccurenceCommand command, CancellationToken cancellationToken = default) - { - var schedule = await this.Schedules.FindAsync(command.ScheduleId, cancellationToken); - if (schedule == null) throw DomainException.NullReference(typeof(V1Schedule), command.ScheduleId); - schedule.CompleteOccurence(command.WorkflowInstanceId); - await this.Schedules.UpdateAsync(schedule, cancellationToken); - await this.Schedules.SaveChangesAsync(cancellationToken); - if (schedule.Definition.Type == ScheduleDefinitionType.Interval && schedule.NextOccurenceAt.HasValue) await this.BackgroundJobManager.ScheduleJobAsync(schedule, cancellationToken); - return this.Ok(this.Mapper.Map(schedule)); - } - - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Commands/Schedules/v1/V1CreateScheduleCommand.cs b/src/core/Synapse.Application/Commands/Schedules/v1/V1CreateScheduleCommand.cs deleted file mode 100644 index cd773c774..000000000 --- a/src/core/Synapse.Application/Commands/Schedules/v1/V1CreateScheduleCommand.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Application.Commands.Schedules -{ - - ///

- /// Represents the used to create a new - /// - [DataTransferObjectType(typeof(Integration.Commands.Schedules.V1CreateScheduleCommand))] - public class V1CreateScheduleCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateScheduleCommand() { } - - /// - /// Initializes a new - /// - /// The type of the to create - /// The definition of the to create - /// The id of the to schedule - public V1CreateScheduleCommand(V1ScheduleActivationType activationType, ScheduleDefinition definition, string workflowId) - { - this.ActivationType = activationType; - this.Definition = definition; - this.WorkflowId = workflowId; - } - - /// - /// Gets the type of the to create - /// - public virtual V1ScheduleActivationType ActivationType { get; protected set; } - - /// - /// Gets the definition of the to create - /// - public virtual ScheduleDefinition Definition { get; protected set; } = null!; - - /// - /// Gets the id of the to schedule - /// - public virtual string WorkflowId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateScheduleCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The used to manage s - /// The service used to manage background jobs - public V1CreateScheduleCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflows, IRepository schedules, IBackgroundJobManager backgroundJobManager) - : base(loggerFactory, mediator, mapper) - { - this.Workflows = workflows; - this.Schedules = schedules; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Workflows { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository Schedules { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - public virtual async Task> HandleAsync(V1CreateScheduleCommand command, CancellationToken cancellationToken = default) - { - var workflowId = (await this.Mediator.ExecuteAndUnwrapAsync(Queries.Workflows.V1GetWorkflowByIdQuery.Parse(command.WorkflowId), cancellationToken))?.Id; - if(string.IsNullOrWhiteSpace(workflowId)) throw DomainException.NullReference(typeof(V1Workflow), command.WorkflowId); - var workflow = await this.Workflows.FindAsync(workflowId, cancellationToken); - if (workflow == null) throw DomainException.NullReference(typeof(V1Workflow), workflowId); - var schedule = await this.Schedules.AddAsync(new(command.ActivationType, command.Definition, workflow), cancellationToken); - await this.Schedules.SaveChangesAsync(cancellationToken); - if (schedule.NextOccurenceAt.HasValue) await this.BackgroundJobManager.ScheduleJobAsync(schedule, cancellationToken); - return this.Ok(this.Mapper.Map(schedule)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Schedules/v1/V1MakeScheduleObsoleteCommand.cs b/src/core/Synapse.Application/Commands/Schedules/v1/V1MakeScheduleObsoleteCommand.cs deleted file mode 100644 index c7d669bd5..000000000 --- a/src/core/Synapse.Application/Commands/Schedules/v1/V1MakeScheduleObsoleteCommand.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.Schedules -{ - ///

- /// Represents the used to make a obsolete - /// - [DataTransferObjectType(typeof(Integration.Commands.Schedules.V1MakeScheduleObsoleteCommand))] - public class V1MakeScheduleObsoleteCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1MakeScheduleObsoleteCommand() { } - - /// - /// Initializes a new - /// - /// The id of the to make obsolete - public V1MakeScheduleObsoleteCommand(string scheduleId) - { - this.ScheduleId = scheduleId; - } - - /// - /// Gets the id of the to make obsolete - /// - public virtual string ScheduleId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1MakeScheduleObsoleteCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage background jobs - public V1MakeScheduleObsoleteCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository schedules, IBackgroundJobManager backgroundJobManager) - : base(loggerFactory, mediator, mapper) - { - this.Schedules = schedules; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Schedules { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - public virtual async Task HandleAsync(V1MakeScheduleObsoleteCommand command, CancellationToken cancellationToken = default) - { - var schedule = await this.Schedules.FindAsync(command.ScheduleId, cancellationToken); - if (schedule == null) throw DomainException.NullReference(typeof(V1Schedule), command.ScheduleId); - schedule.MakeObsolete(); - await this.Schedules.UpdateAsync(schedule, cancellationToken); - await this.Schedules.SaveChangesAsync(cancellationToken); - await this.BackgroundJobManager.CancelJobAsync(schedule.Id, cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Schedules/v1/V1ResumeScheduleCommand.cs b/src/core/Synapse.Application/Commands/Schedules/v1/V1ResumeScheduleCommand.cs deleted file mode 100644 index 3ef19d423..000000000 --- a/src/core/Synapse.Application/Commands/Schedules/v1/V1ResumeScheduleCommand.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Mediation; - -namespace Synapse.Application.Commands.Schedules -{ - ///

- /// Represents the used to resume a - /// - [DataTransferObjectType(typeof(Integration.Commands.Schedules.V1ResumeScheduleCommand))] - public class V1ResumeScheduleCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1ResumeScheduleCommand() { } - - /// - /// Initializes a new - /// - /// The id of the to resume - public V1ResumeScheduleCommand(string scheduleId) - { - this.ScheduleId = scheduleId; - } - - /// - /// Gets the id of the to resume - /// - public virtual string ScheduleId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1ResumeScheduleCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage background jobs - public V1ResumeScheduleCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository schedules, IBackgroundJobManager backgroundJobManager) - : base(loggerFactory, mediator, mapper) - { - this.Schedules = schedules; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Schedules { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - public virtual async Task> HandleAsync(V1ResumeScheduleCommand command, CancellationToken cancellationToken = default) - { - var schedule = await this.Schedules.FindAsync(command.ScheduleId, cancellationToken); - if (schedule == null) throw DomainException.NullReference(typeof(V1Schedule), command.ScheduleId); - schedule.Resume(); - schedule = await this.Schedules.UpdateAsync(schedule, cancellationToken); - await this.Schedules.SaveChangesAsync(cancellationToken); - if (schedule.NextOccurenceAt.HasValue) await this.BackgroundJobManager.ScheduleJobAsync(schedule, cancellationToken); - return this.Ok(this.Mapper.Map(schedule)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Schedules/v1/V1RetireScheduleCommand.cs b/src/core/Synapse.Application/Commands/Schedules/v1/V1RetireScheduleCommand.cs deleted file mode 100644 index 64c257178..000000000 --- a/src/core/Synapse.Application/Commands/Schedules/v1/V1RetireScheduleCommand.cs +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.Schedules -{ - - ///

- /// Represents the used to retire a - /// - [DataTransferObjectType(typeof(Integration.Commands.Schedules.V1RetireScheduleCommand))] - public class V1RetireScheduleCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1RetireScheduleCommand() { } - - /// - /// Initializes a new - /// - /// The id of the to retire - public V1RetireScheduleCommand(string scheduleId) - { - this.ScheduleId = scheduleId; - } - - /// - /// Gets the id of the to retire - /// - public virtual string ScheduleId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1RetireScheduleCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage background jobs - public V1RetireScheduleCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository schedules, IBackgroundJobManager backgroundJobManager) - : base(loggerFactory, mediator, mapper) - { - this.Schedules = schedules; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Schedules { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - public virtual async Task HandleAsync(V1RetireScheduleCommand command, CancellationToken cancellationToken = default) - { - var schedule = await this.Schedules.FindAsync(command.ScheduleId, cancellationToken); - if (schedule == null) throw DomainException.NullReference(typeof(V1Schedule), command.ScheduleId); - schedule.Retire(); - await this.Schedules.UpdateAsync(schedule, cancellationToken); - await this.Schedules.SaveChangesAsync(cancellationToken); - await this.BackgroundJobManager.CancelJobAsync(schedule.Id, cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Schedules/v1/V1SuspendScheduleCommand.cs b/src/core/Synapse.Application/Commands/Schedules/v1/V1SuspendScheduleCommand.cs deleted file mode 100644 index 46a0ed6bb..000000000 --- a/src/core/Synapse.Application/Commands/Schedules/v1/V1SuspendScheduleCommand.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.Schedules -{ - ///

- /// Represents the used to suspend a - /// - [DataTransferObjectType(typeof(Integration.Commands.Schedules.V1SuspendScheduleCommand))] - public class V1SuspendScheduleCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SuspendScheduleCommand() { } - - /// - /// Initializes a new - /// - /// The id of the to suspend - public V1SuspendScheduleCommand(string scheduleId) - { - this.ScheduleId = scheduleId; - } - - /// - /// Gets the id of the to suspend - /// - public virtual string ScheduleId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1SuspendScheduleCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage background jobs - public V1SuspendScheduleCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository schedules, IBackgroundJobManager backgroundJobManager) - : base(loggerFactory, mediator, mapper) - { - this.Schedules = schedules; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Schedules { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - public virtual async Task HandleAsync(V1SuspendScheduleCommand command, CancellationToken cancellationToken = default) - { - var schedule = await this.Schedules.FindAsync(command.ScheduleId, cancellationToken); - if (schedule == null) throw DomainException.NullReference(typeof(V1Schedule), command.ScheduleId); - schedule.Suspend(); - await this.Schedules.UpdateAsync(schedule, cancellationToken); - await this.Schedules.SaveChangesAsync(cancellationToken); - await this.BackgroundJobManager.CancelJobAsync(schedule.Id, cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Schedules/v1/V1TriggerScheduleCommand.cs b/src/core/Synapse.Application/Commands/Schedules/v1/V1TriggerScheduleCommand.cs deleted file mode 100644 index bdbfc833c..000000000 --- a/src/core/Synapse.Application/Commands/Schedules/v1/V1TriggerScheduleCommand.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk; -using Synapse.Application.Commands.WorkflowInstances; - -namespace Synapse.Application.Commands.Schedules -{ - - ///

- /// Represents the used to trigger a occurence - /// - [DataTransferObjectType(typeof(Integration.Commands.Schedules.V1TriggerScheduleCommand))] - public class V1TriggerScheduleCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1TriggerScheduleCommand() { } - - /// - /// Initializes a new - /// - /// The id of the to trigger - public V1TriggerScheduleCommand(string scheduleId) - { - this.ScheduleId = scheduleId; - } - - /// - /// Gets the id of the to trigger - /// - public virtual string ScheduleId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1TriggerScheduleCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage background jobs - public V1TriggerScheduleCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository schedules, IBackgroundJobManager backgroundJobManager) - : base(loggerFactory, mediator, mapper) - { - this.Schedules = schedules; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Schedules { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - public virtual async Task> HandleAsync(V1TriggerScheduleCommand command, CancellationToken cancellationToken = default) - { - var schedule = await this.Schedules.FindAsync(command.ScheduleId, cancellationToken); - if (schedule == null) throw DomainException.NullReference(typeof(V1Schedule), command.ScheduleId); - var workflowInstance = await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateWorkflowInstanceCommand(schedule.WorkflowId, V1WorkflowInstanceActivationType.Schedule, null, null, true, null)); - schedule.Occur(workflowInstance.Id); - schedule = await this.Schedules.UpdateAsync(schedule, cancellationToken); - await this.Schedules.SaveChangesAsync(cancellationToken); - if (schedule.Definition.Type == ScheduleDefinitionType.Cron && schedule.NextOccurenceAt.HasValue) await this.BackgroundJobManager.ScheduleJobAsync(schedule, cancellationToken); - return this.Ok(this.Mapper.Map(schedule)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CancelWorkflowActivityCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CancelWorkflowActivityCommand.cs deleted file mode 100644 index a6633ac43..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CancelWorkflowActivityCommand.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - - ///

- /// Represents the used to cancel a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1CancelWorkflowActivityCommand))] - public class V1CancelWorkflowActivityCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CancelWorkflowActivityCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to cancel - public V1CancelWorkflowActivityCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to cancel - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1CancelWorkflowActivityCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1CancelWorkflowActivityCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1CancelWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(V1WorkflowActivity), command.Id); - activity.Cancel(); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CompensateActivityCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CompensateActivityCommand.cs deleted file mode 100644 index 544a85980..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CompensateActivityCommand.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - ///

- /// Represents the used to compensate a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1CompensateActivityCommand))] - public class V1CompensateActivityCommand - : Command - { - - - /// - /// Initializes a new - /// - protected V1CompensateActivityCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to compensate - public V1CompensateActivityCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to compensate - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1CompensateActivityCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1CompensateActivityCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1CompensateActivityCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(V1WorkflowActivity), command.Id); - activity.Compensate(); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CreateWorkflowActivityCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CreateWorkflowActivityCommand.cs deleted file mode 100644 index 220440f37..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1CreateWorkflowActivityCommand.cs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - - ///

- /// Represents the used to create a new - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1CreateWorkflowActivityCommand))] - public class V1CreateWorkflowActivityCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateWorkflowActivityCommand() - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the the to create belongs to - /// The type of to create - /// The input data of the to create - /// The metadata of the to create - /// The id the parent of the to create - public V1CreateWorkflowActivityCommand(string workflowInstanceId, V1WorkflowActivityType type, object? input, IDictionary? metadata, string? parentId) - { - this.WorkflowInstanceId = workflowInstanceId; - this.Type = type; - this.Input = input; - this.Metadata = metadata; - this.ParentId = parentId; - } - - /// - /// Gets the id of the the to create belongs to - /// - public virtual string WorkflowInstanceId { get; protected set; } - - /// - /// Gets the type of to create - /// - public virtual V1WorkflowActivityType Type { get; protected set; } - - /// - /// Gets the input data of the to create - /// - public virtual object? Input { get; protected set; } - - /// - /// Gets the metadata of the to create - /// - public virtual IDictionary? Metadata { get; protected set; } - - /// - /// Gets the id the parent of the to create - /// - public virtual string? ParentId { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateWorkflowActivityCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1CreateWorkflowActivityCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository workflowInstances, IRepository workflowActivities) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - this.WorkflowActivities = workflowActivities; - } - - /// - /// Gets the used to manage s - /// - protected virtual IRepository WorkflowInstances { get; } - - /// - /// Gets the used to manage instances - /// - protected virtual IRepository WorkflowActivities { get; } - - /// - public virtual async Task> HandleAsync(V1CreateWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.WorkflowInstanceId, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.WorkflowInstanceId); - var parent = null as V1WorkflowActivity; - if (!string.IsNullOrWhiteSpace(command.ParentId)) - { - parent = await this.WorkflowActivities.FindAsync(command.ParentId, cancellationToken); - if (parent == null) - throw DomainException.NullReference(typeof(V1WorkflowActivity), command.ParentId); - } - var activity = await this.WorkflowActivities.AddAsync(new(workflowInstance, command.Type, command.Input, command.Metadata, parent), cancellationToken); - await this.WorkflowActivities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1FaultWorkflowActivityCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1FaultWorkflowActivityCommand.cs deleted file mode 100644 index bed0b686f..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1FaultWorkflowActivityCommand.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Models; - -namespace Synapse.Application.Commands.WorkflowActivities -{ - - ///

- /// Represents the used to fault a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1FaultWorkflowActivityCommand))] - public class V1FaultWorkflowActivityCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1FaultWorkflowActivityCommand() - { - this.Id = null!; - this.Error = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to fault - /// The that cause the to fault - public V1FaultWorkflowActivityCommand(string id, Neuroglia.Error error) - { - this.Id = id; - this.Error = error; - } - - /// - /// Gets the id of the to cancel - /// - public virtual string Id { get; protected set; } - - /// - /// Gets the that cause the to fault - /// - public virtual Neuroglia.Error Error { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1FaultWorkflowActivityCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1FaultWorkflowActivityCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1FaultWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(Domain.Models.V1WorkflowActivity), command.Id); - activity.Fault(command.Error); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1MarkActivityAsCompensatedCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1MarkActivityAsCompensatedCommand.cs deleted file mode 100644 index 0c2c5d9d6..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1MarkActivityAsCompensatedCommand.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - ///

- /// Represents the used to mark a as compensated - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1MarkActivityAsCompensatedCommand))] - public class V1MarkActivityAsCompensatedCommand - : Command - { - - - /// - /// Initializes a new - /// - protected V1MarkActivityAsCompensatedCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to mark as compensated - public V1MarkActivityAsCompensatedCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to mark as compensated - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1MarkActivityAsCompensatedCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1MarkActivityAsCompensatedCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1MarkActivityAsCompensatedCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(V1WorkflowActivity), command.Id); - activity.MarkAsCompensated(); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SetWorkflowActivityMetadataCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SetWorkflowActivityMetadataCommand.cs deleted file mode 100644 index a82529ae2..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SetWorkflowActivityMetadataCommand.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - - ///

- /// Represents the used to set the metadata of a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1SetWorkflowActivityMetadataCommand))] - public class V1SetWorkflowActivityMetadataCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SetWorkflowActivityMetadataCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to update the metadata of - /// An containing the 's updated metadata - public V1SetWorkflowActivityMetadataCommand(string id, IDictionary? metadata) - { - this.Id = id; - this.Metadata = metadata; - } - - /// - /// Gets the id of the to update the metadata of - /// - public virtual string Id { get; protected set; } - - /// - /// Gets the containing the 's updated metadata - /// - public virtual IDictionary? Metadata { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1SetWorkflowActivityMetadataCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage instances - public V1SetWorkflowActivityMetadataCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1SetWorkflowActivityMetadataCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(V1WorkflowActivity), command.Id); - activity.SetMetadata(command.Metadata); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SetWorkflowActivityOutputCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SetWorkflowActivityOutputCommand.cs deleted file mode 100644 index 9004cb011..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SetWorkflowActivityOutputCommand.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - ///

- /// Represents the used to set the output of a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1SetWorkflowActivityOutputCommand))] - public class V1SetWorkflowActivityOutputCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SetWorkflowActivityOutputCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to suspend - /// The 's output - public V1SetWorkflowActivityOutputCommand(string id, object? output) - { - this.Id = id; - this.Output = output; - } - - /// - /// Gets the id of the to suspend - /// - public virtual string Id { get; protected set; } - - /// - /// Gets the 's output - /// - public virtual object? Output { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1SetWorkflowActivityOutputCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1SetWorkflowActivityOutputCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1SetWorkflowActivityOutputCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(Domain.Models.V1WorkflowActivity), command.Id); - activity.SetOutput(command.Output); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SkipWorkflowActivityCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SkipWorkflowActivityCommand.cs deleted file mode 100644 index 25cff0f4a..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SkipWorkflowActivityCommand.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - ///

- /// Represents the used to skip the execution of a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1SkipWorkflowActivityCommand))] - public class V1SkipWorkflowActivityCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SkipWorkflowActivityCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to fault - public V1SkipWorkflowActivityCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to cancel - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1SkipWorkflowActivityCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1SkipWorkflowActivityCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1SkipWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(Domain.Models.V1WorkflowActivity), command.Id); - activity.Skip(); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1StartWorkflowActivityCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1StartWorkflowActivityCommand.cs deleted file mode 100644 index 6b9fd42e7..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1StartWorkflowActivityCommand.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - ///

- /// Represents the used to start a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1StartWorkflowActivityCommand))] - public class V1StartWorkflowActivityCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1StartWorkflowActivityCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to start - public V1StartWorkflowActivityCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to start - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1StartWorkflowActivityCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1StartWorkflowActivityCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1StartWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(Domain.Models.V1WorkflowActivity), command.Id); - activity.StartOrResume(); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SuspendWorkflowActivityCommand.cs b/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SuspendWorkflowActivityCommand.cs deleted file mode 100644 index 296baf7d5..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowActivities/v1/V1SuspendWorkflowActivityCommand.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowActivities -{ - - ///

- /// Represents the used to suspend a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowActivities.V1SuspendWorkflowActivityCommand))] - public class V1SuspendWorkflowActivityCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SuspendWorkflowActivityCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to suspend - public V1SuspendWorkflowActivityCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to suspend - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1SuspendWorkflowActivityCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1SuspendWorkflowActivityCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository activities) - : base(loggerFactory, mediator, mapper) - { - this.Activities = activities; - } - - /// - /// Gets the used to manage instances - /// - protected IRepository Activities { get; } - - /// - public virtual async Task> HandleAsync(V1SuspendWorkflowActivityCommand command, CancellationToken cancellationToken = default) - { - var activity = await this.Activities.FindAsync(command.Id, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(Domain.Models.V1WorkflowActivity), command.Id); - activity.Suspend(); - activity = await this.Activities.UpdateAsync(activity, cancellationToken); - await this.Activities.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(activity)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ArchiveWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ArchiveWorkflowInstanceCommand.cs deleted file mode 100644 index a29606234..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ArchiveWorkflowInstanceCommand.cs +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.EventSourcing; -using Neuroglia.Serialization; -using System.IO.Compression; - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to archive an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1ArchiveWorkflowInstanceCommand))] - public class V1ArchiveWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1ArchiveWorkflowInstanceCommand() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to archive - public V1ArchiveWorkflowInstanceCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to archive - /// - public virtual string Id { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1ArchiveWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The service used to access the current - /// The service used to provide s - /// The used to manage s - /// The used to manage s - /// The used to manage es - public V1ArchiveWorkflowInstanceCommandHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IOptions options, - ISerializerProvider serializerProvider, IRepository workflows, IRepository workflowInstances, IRepository workflowProcesses) - : base(loggerFactory, mediator, mapper) - { - this.Options = options.Value; - this.Serializer = serializerProvider.GetSerializer(this.Options.Archiving.SerializerType); - this.EventStore = serviceProvider.GetService(); - this.Workflows = workflows; - this.WorkflowInstances = workflowInstances; - this.WorkflowProcesses = workflowProcesses; - } - - /// - /// Gets the current - /// - protected SynapseApplicationOptions Options { get; } - - /// - /// Gets the service used to serialize the to archive and its components - /// - protected ISerializer Serializer { get; } - - /// - /// Gets the service used to store events - /// - protected IEventStore? EventStore { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository Workflows { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - /// Gets the used to manage es - /// - protected IRepository WorkflowProcesses { get; } - - /// - public virtual async Task> HandleAsync(V1ArchiveWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - var workflow = await this.Workflows.FindAsync(workflowInstance.WorkflowId, cancellationToken); - if(workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), workflowInstance.WorkflowId); - var directory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), workflowInstance.Id)); - if (directory.Exists) - directory.Delete(true); - directory.Create(); - var buffer = await this.Serializer.SerializeAsync(workflow.Definition, cancellationToken); - await File.WriteAllBytesAsync(Path.Combine(directory.FullName, $"definition{this.Options.Archiving.FileExtension}"), buffer, cancellationToken); - buffer = await this.Serializer.SerializeAsync(this.Mapper.Map(workflowInstance), cancellationToken); - await File.WriteAllBytesAsync(Path.Combine(directory.FullName, $"snapshot{this.Options.Archiving.FileExtension}"), buffer, cancellationToken); - if(this.EventStore != null) - { - var eventStream = await this.EventStore.GetStreamAsync($"{typeof(V1WorkflowInstance).Name.ToLower()}-{workflowInstance.Id}", cancellationToken); //todo: ATTENTION: we should not be building that key ourselves here. A service should take care of that. Possibly fix/change the way the EventSourcingRepository works - if(eventStream != null) - { - var events = await eventStream.ToListAsync(cancellationToken); - buffer = await this.Serializer.SerializeAsync(events, cancellationToken); - await File.WriteAllBytesAsync(Path.Combine(directory.FullName, $"stream{this.Options.Archiving.FileExtension}"), buffer, cancellationToken); - } - } - var processesDirectory = new DirectoryInfo(Path.Combine(directory.FullName, "processes")); - if (processesDirectory.Exists) - processesDirectory.Delete(true); - processesDirectory.Create(); - foreach (var processId in workflowInstance.Sessions.Select(s => s.ProcessId)) - { - var process = await this.WorkflowProcesses.FindAsync(processId, cancellationToken); - if (process == null) - throw DomainException.NullReference(typeof(V1WorkflowProcess), processId); - buffer = await this.Serializer.SerializeAsync(this.Mapper.Map(process), cancellationToken); - await File.WriteAllBytesAsync(Path.Combine(processesDirectory.FullName, $"{processId}{this.Options.Archiving.FileExtension}"), buffer, cancellationToken); - } - var archiveFilePath = Path.Combine(Path.GetTempPath(), $"{workflowInstance.Id}.zip"); - ZipFile.CreateFromDirectory(directory.FullName, archiveFilePath, CompressionLevel.Fastest, true); - using var fileStream = File.OpenRead(archiveFilePath); - var stream = new MemoryStream(); - await fileStream.CopyToAsync(stream, cancellationToken); - await stream.FlushAsync(cancellationToken); - stream.Position = 0; - return this.Ok(stream); - } - - } -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CancelWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CancelWorkflowInstanceCommand.cs deleted file mode 100644 index d42c39699..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CancelWorkflowInstanceCommand.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to cancel the execution of an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1CancelWorkflowInstanceCommand))] - public class V1CancelWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CancelWorkflowInstanceCommand() - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to cancel - public V1CancelWorkflowInstanceCommand(string id) - { - this.WorkflowInstanceId = id; - } - - /// - /// Gets the id of the to cancel - /// - public virtual string WorkflowInstanceId { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1CancelWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage runtime proxies - public V1CancelWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository workflowInstances, IWorkflowRuntimeProxyManager runtimeProxyManager) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - this.RuntimeHostManager = runtimeProxyManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - /// Gets the service used to manage runtime proxies - /// - protected IWorkflowRuntimeProxyManager RuntimeHostManager { get; } - - /// - public virtual async Task> HandleAsync(V1CancelWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.WorkflowInstanceId, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.WorkflowInstanceId); - if(this.RuntimeHostManager.TryGetProxy(command.WorkflowInstanceId, out var proxy)) - { - workflowInstance.Cancel(); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - await proxy.CancelAsync(cancellationToken); - } - else - { - workflowInstance.Cancel(); - workflowInstance.MarkAsCancelled(); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - } - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ConsumeOrBeginCorrelateEventCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ConsumeOrBeginCorrelateEventCommand.cs deleted file mode 100644 index 37606a765..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ConsumeOrBeginCorrelateEventCommand.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using Synapse.Application.Commands.Correlations; - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to consume a pending event of an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1ConsumeWorkflowInstancePendingEventCommand))] - public class V1ConsumeOrBeginCorrelateEventCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1ConsumeOrBeginCorrelateEventCommand() - { - this.WorkflowInstanceId = null!; - this.EventDefinition = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to consume a pending event of - /// The that describes the event to consume - public V1ConsumeOrBeginCorrelateEventCommand(string id, EventDefinition eventDefinition) - { - this.WorkflowInstanceId = id; - this.EventDefinition = eventDefinition; - } - - /// - /// Gets the id of the to consume a pending event of - /// - public virtual string WorkflowInstanceId { get; protected set; } - - /// - /// Gets the that describes the event to consume - /// - public virtual EventDefinition EventDefinition { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1ConsumeWorkflowInstancePendingEventCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1ConsumeWorkflowInstancePendingEventCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1ConsumeOrBeginCorrelateEventCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.WorkflowInstanceId, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.WorkflowInstanceId); - var e = workflowInstance.CorrelationContext.PendingEvents - .FirstOrDefault(e => e.Matches(command.EventDefinition)); - if (e == null) - { - var conditions = new List() { V1CorrelationCondition.Match(command.EventDefinition) }; - var outcome = new V1CorrelationOutcome(V1CorrelationOutcomeType.Correlate, command.WorkflowInstanceId); - await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateCorrelationCommand(V1CorrelationActivationType.Implicit, V1CorrelationLifetime.Singleton, V1CorrelationConditionType.AnyOf, conditions, outcome, workflowInstance.CorrelationContext), cancellationToken); - } - else - { - workflowInstance.CorrelationContext.RemoveEvent(e); - await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - } - return this.Ok(e == null ? null : this.Mapper.Map(e)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CorrelateWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CorrelateWorkflowInstanceCommand.cs deleted file mode 100644 index 4ebbdb7ee..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CorrelateWorkflowInstanceCommand.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents an used to correlate an existing - /// - public class V1CorrelateWorkflowInstanceCommand - : Command - { - - - /// - /// Initializes a new - /// - protected V1CorrelateWorkflowInstanceCommand() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to correlate - /// The to correlate the with - public V1CorrelateWorkflowInstanceCommand(string id, V1CorrelationContext correlationContext) - { - this.Id = id; - this.CorrelationContext = correlationContext; - } - - /// - /// Gets the id of the to correlate - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the to correlate the with - /// - public virtual V1CorrelationContext CorrelationContext { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1CorrelateWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage workflow runtime proxies - public V1CorrelateWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository workflowInstances, IWorkflowRuntimeProxyManager runtimeProxyManager) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - this.RuntimeProxyManager = runtimeProxyManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - - /// - /// Gets the service used to manage workflow runtime proxies - /// - protected IWorkflowRuntimeProxyManager RuntimeProxyManager { get; } - - /// - public virtual async Task> HandleAsync(V1CorrelateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if(workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - workflowInstance.SetCorrelationContext(command.CorrelationContext); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - switch (workflowInstance.Status) - { - case V1WorkflowInstanceStatus.Suspended: - await this.Mediator.ExecuteAndUnwrapAsync(new V1StartWorkflowInstanceCommand(workflowInstance.Id), cancellationToken); - break; - case V1WorkflowInstanceStatus.Running: - var runtimeProxy = this.RuntimeProxyManager.GetProxy(workflowInstance.Id); - await runtimeProxy.CorrelateAsync(command.CorrelationContext, cancellationToken); - break; - default: - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), workflowInstance.Id, workflowInstance.Status); - } - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CreateWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CreateWorkflowInstanceCommand.cs deleted file mode 100644 index 2c78c7a22..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1CreateWorkflowInstanceCommand.cs +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Expressions; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Schema; -using Synapse.Application.Queries.Workflows; - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to create a new s - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1CreateWorkflowInstanceCommand))] - public class V1CreateWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateWorkflowInstanceCommand() - { - this.WorkflowId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to instanciate - /// The 's activation type - /// The input data of the to create - /// The of the to create - /// A boolean indicating whether or not to start the once it has been created - /// The id of the parent of the to create - public V1CreateWorkflowInstanceCommand(string workflowId, V1WorkflowInstanceActivationType activationType, object? inputData, V1CorrelationContext? correlationContext, bool autoStart, string? parentId) - { - this.WorkflowId = workflowId; - this.ActivationType = activationType; - this.InputData = inputData; - this.CorrelationContext = correlationContext; - this.AutoStart = autoStart; - this.ParentId = parentId; - } - - /// - /// Gets the id of the to instanciate - /// - public virtual string WorkflowId { get; protected set; } - - /// - /// Gets the 's activation type - /// - public virtual V1WorkflowInstanceActivationType ActivationType { get; protected set; } - - /// - /// Gets the input data of the to create - /// - public virtual object? InputData { get; protected set; } - - /// - /// Gets of the to create - /// - public virtual V1CorrelationContext? CorrelationContext { get; protected set; } - - /// - /// Gets a boolean indicating whether or not to automatically start the once it has been created - /// - public virtual bool AutoStart { get; protected set; } - - /// - /// Gets the id of the parent of the to create - /// - public virtual string? ParentId { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1CreateWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IHttpClientFactory httpClientFactory, - IRepository workflows, IRepository workflowInstances, IExpressionEvaluatorProvider expressionEvaluatorProvider) - : base(loggerFactory, mediator, mapper) - { - this.HttpClientFactory = httpClientFactory; - this.Workflows = workflows; - this.WorkflowInstances = workflowInstances; - this.ExpressionEvaluatorProvider = expressionEvaluatorProvider; - } - - /// - /// Gets the service used to create s - /// - protected IHttpClientFactory HttpClientFactory { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository Workflows { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get;} - - /// - /// Gets the service used to provide s - /// - protected IExpressionEvaluatorProvider ExpressionEvaluatorProvider { get; } - - /// - public virtual async Task> HandleAsync(V1CreateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowIdComponents = command.WorkflowId.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = workflowIdComponents[0]; - var version = string.Empty; - if (workflowIdComponents.Length > 1) - version = workflowIdComponents[1]; - var workflowId = (await this.Mediator.ExecuteAndUnwrapAsync(new V1GetWorkflowByIdQuery(id, version), cancellationToken)).Id; - var workflow = await this.Workflows.FindAsync(workflowId, cancellationToken); - if(workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), workflowId); - var parent = null as V1WorkflowInstance; - if (!string.IsNullOrWhiteSpace(command.ParentId)) - { - parent = await this.WorkflowInstances.FindAsync(command.ParentId, cancellationToken); - if(parent == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.ParentId); - } - string? key = null; - var dataInputSchema = workflow.Definition.DataInputSchema?.Schema; - if (dataInputSchema == null - && workflow.Definition.DataInputSchemaUri != null) - { - using var httpClient = this.HttpClientFactory.CreateClient(); - var json = await httpClient.GetStringAsync(workflow.Definition.DataInputSchemaUri, cancellationToken); - dataInputSchema = JSchema.Parse(json); - } - if(dataInputSchema != null) - { - var input = command.InputData; - JObject? jobj; - if (input == null) - jobj = new JObject(); - else - jobj = JObject.FromObject(input); - if (!jobj.IsValid(dataInputSchema, out IList errors)) - throw new DomainArgumentException($"Invalid workflow input data:{Environment.NewLine}{string.Join(Environment.NewLine, errors)}", nameof(command.InputData)); - } - if (!string.IsNullOrWhiteSpace(workflow.Definition.Key) - && command.InputData != null) - { - try - { - key = this.ExpressionEvaluatorProvider.GetEvaluator(workflow.Definition.ExpressionLanguage)!.Evaluate(workflow.Definition.Key, command.InputData)?.ToString(); - } - catch { } - } - if (string.IsNullOrWhiteSpace(key)) - key = Guid.NewGuid().ToBase64(); - while (await this.WorkflowInstances.ContainsAsync(V1WorkflowInstance.BuildUniqueIdentifier(key, workflow), cancellationToken)) - { - key = Guid.NewGuid().ToBase64(); - } - var workflowInstance = await this.WorkflowInstances.AddAsync(new(key.ToLowerInvariant(), workflow, command.ActivationType, command.InputData, command.CorrelationContext, parent), cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - workflow.Instanciate(); - await this.Workflows.UpdateAsync(workflow, cancellationToken); - await this.Workflows.SaveChangesAsync(cancellationToken); - if (command.AutoStart) - await this.Mediator.ExecuteAndUnwrapAsync(new V1StartWorkflowInstanceCommand(workflowInstance.Id), cancellationToken); - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1DeleteWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1DeleteWorkflowInstanceCommand.cs deleted file mode 100644 index 63ac3b6c9..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1DeleteWorkflowInstanceCommand.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to delete an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1DeleteWorkflowInstanceCommand))] - public class V1DeleteWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1DeleteWorkflowInstanceCommand() - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to delete - public V1DeleteWorkflowInstanceCommand(string id) - { - this.WorkflowInstanceId = id; - } - - /// - /// Gets the id of the to delete - /// - public virtual string WorkflowInstanceId { get; protected set; } - - } - - /// - /// Represents the service used to delete an existing - /// - public class V1DeleteWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage runtime proxies - public V1DeleteWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository workflowInstances, IWorkflowRuntimeProxyManager runtimeProxyManager) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - this.RuntimeProxyManager = runtimeProxyManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - /// Gets the service used to manage runtime proxies - /// - protected IWorkflowRuntimeProxyManager RuntimeProxyManager { get; } - - /// - public virtual async Task HandleAsync(V1DeleteWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.WorkflowInstanceId, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.WorkflowInstanceId); - if(this.RuntimeProxyManager.TryGetProxy(command.WorkflowInstanceId, out var proxy)) - { - await proxy.CancelAsync(cancellationToken); - this.RuntimeProxyManager.Unregister(proxy); - } - workflowInstance.Delete(); - await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.RemoveAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1FaultWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1FaultWorkflowInstanceCommand.cs deleted file mode 100644 index 5639b4a6f..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1FaultWorkflowInstanceCommand.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to fault the execution of an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1FaultWorkflowInstanceCommand))] - public class V1FaultWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1FaultWorkflowInstanceCommand() - { - this.Id = null!; - this.Error = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to fault - /// The that caused the to fault - public V1FaultWorkflowInstanceCommand(string id, Error error) - { - this.Id = id; - this.Error = error; - } - - /// - /// Gets the id of the to fault - /// - public virtual string Id { get; protected set; } - - /// - /// Gets the that caused the to fault - /// - public virtual Neuroglia.Error Error { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1FaultWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1FaultWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1FaultWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - workflowInstance.Fault(command.Error); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsCancelledCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsCancelledCommand.cs deleted file mode 100644 index e64d69fe9..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsCancelledCommand.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to mark the execution of a as cancelled - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsCancelledCommand))] - public class V1MarkWorkflowInstanceAsCancelledCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1MarkWorkflowInstanceAsCancelledCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to mark as cancelled - public V1MarkWorkflowInstanceAsCancelledCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to mark as cancelled - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1MarkWorkflowInstanceAsCancelledCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1MarkWorkflowInstanceAsCancelledCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1MarkWorkflowInstanceAsCancelledCommand command, CancellationToken cancellationToken = default) - { - var instance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (instance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - instance.MarkAsCancelled(); - instance = await this.WorkflowInstances.UpdateAsync(instance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(instance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsStartedCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsStartedCommand.cs deleted file mode 100644 index 52458aecb..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsStartedCommand.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to mark the execution of a as started - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1SetWorkflowInstanceStartedCommand))] - public class V1MarkWorkflowInstanceAsStartedCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1MarkWorkflowInstanceAsStartedCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to mark as started - public V1MarkWorkflowInstanceAsStartedCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to mark as started - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1MarkWorkflowInstanceAsStartedCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1MarkWorkflowInstanceAsStartedCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1MarkWorkflowInstanceAsStartedCommand command, CancellationToken cancellationToken = default) - { - var instance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (instance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - instance.MarkAsRunning(); - instance = await this.WorkflowInstances.UpdateAsync(instance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(instance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsSuspendedCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsSuspendedCommand.cs deleted file mode 100644 index 67568511e..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1MarkWorkflowInstanceAsSuspendedCommand.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to mark the execution of a as suspended - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1MarkWorkflowInstanceAsSuspendedCommand))] - public class V1MarkWorkflowInstanceAsSuspendedCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1MarkWorkflowInstanceAsSuspendedCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to mark as suspended - public V1MarkWorkflowInstanceAsSuspendedCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to mark as suspended - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1MarkWorkflowInstanceAsSuspendedCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1MarkWorkflowInstanceAsSuspendedCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1MarkWorkflowInstanceAsSuspendedCommand command, CancellationToken cancellationToken = default) - { - var instance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (instance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - instance.MarkAsSuspended(); - instance = await this.WorkflowInstances.UpdateAsync(instance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(instance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ResumeWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ResumeWorkflowInstanceCommand.cs deleted file mode 100644 index df9306d41..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1ResumeWorkflowInstanceCommand.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to resume the execution of an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1ResumeWorkflowInstanceCommand))] - public class V1ResumeWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1ResumeWorkflowInstanceCommand() - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to resume the execution of - public V1ResumeWorkflowInstanceCommand(string id) - { - this.WorkflowInstanceId = id; - } - - /// - /// Gets the id of the to resume the execution of - /// - public virtual string WorkflowInstanceId { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1ResumeWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The used to manage s - /// The service used to manage es - public V1ResumeWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflows, IRepository workflowInstances, IWorkflowProcessManager processManager) - : base(loggerFactory, mediator, mapper) - { - this.Workflows = workflows; - this.WorkflowInstances = workflowInstances; - this.ProcessManager = processManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Workflows { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - /// Gets the service used to manage es - /// - protected IWorkflowProcessManager ProcessManager { get; } - - /// - public virtual async Task> HandleAsync(V1ResumeWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.WorkflowInstanceId, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.WorkflowInstanceId); - var workflow = await this.Workflows.FindAsync(workflowInstance.WorkflowId, cancellationToken); - if (workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), workflowInstance.WorkflowId); - var process = await this.ProcessManager.StartProcessAsync(workflow, workflowInstance, cancellationToken); - workflowInstance.Resume(process.Id); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SetWorkflowInstanceCorrelationMappingCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SetWorkflowInstanceCorrelationMappingCommand.cs deleted file mode 100644 index 4170a33ad..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SetWorkflowInstanceCorrelationMappingCommand.cs +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - ///

- /// Represents the used to set a correlation mapping for a - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1SetWorkflowInstanceCorrelationMappingCommand))] - public class V1SetWorkflowInstanceCorrelationMappingCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SetWorkflowInstanceCorrelationMappingCommand() - { - this.Id = null!; - this.Key = null!; - this.Value = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to set the correlation mapping for - /// The key of the correlation mapping to set - /// The value of the correlation mapping to set - public V1SetWorkflowInstanceCorrelationMappingCommand(string id, string key, string value) - { - this.Id = id; - this.Key = key; - this.Value = value; - } - - /// - /// Gets the id of the to cancel - /// - public virtual string Id { get; protected set; } - - /// - /// Gets the key of the correlation mapping to set - /// - public virtual string Key { get; protected set; } - - /// - /// Gets the value of the correlation mapping to set - /// - public virtual string Value { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1SetWorkflowInstanceCorrelationMappingCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - public V1SetWorkflowInstanceCorrelationMappingCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1SetWorkflowInstanceCorrelationMappingCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - workflowInstance.SetCorrelationMapping(command.Key, command.Value); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SetWorkflowInstanceOutputCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SetWorkflowInstanceOutputCommand.cs deleted file mode 100644 index a1555e7b4..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SetWorkflowInstanceOutputCommand.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to complete and set the output of an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1SetWorkflowInstanceOutputCommand))] - public class V1SetWorkflowInstanceOutputCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SetWorkflowInstanceOutputCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to start - /// The 's output - public V1SetWorkflowInstanceOutputCommand(string id, object? output) - { - this.Id = id; - this.Output = output; - } - - /// - /// Gets the id of the to start - /// - public virtual string Id { get; protected set; } - - /// - /// Gets the 's output - /// - public virtual object? Output { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1SetWorkflowInstanceOutputCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1SetWorkflowInstanceOutputCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1SetWorkflowInstanceOutputCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - workflowInstance.MarkAsCompleted(command.Output); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1StartWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1StartWorkflowInstanceCommand.cs deleted file mode 100644 index 092a16504..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1StartWorkflowInstanceCommand.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to start the execution of an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1StartWorkflowInstanceCommand))] - public class V1StartWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1StartWorkflowInstanceCommand() - { - this.Id = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to start - public V1StartWorkflowInstanceCommand(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to start - /// - public virtual string Id { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1StartWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The used to manage s - /// The service used to manage es - public V1StartWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflows, IRepository workflowInstances, IWorkflowProcessManager processManager) - : base(loggerFactory, mediator, mapper) - { - this.Workflows = workflows; - this.WorkflowInstances = workflowInstances; - this.ProcessManager = processManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository Workflows { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - /// Gets the service used to manage es - /// - protected IWorkflowProcessManager ProcessManager { get; } - - /// - public virtual async Task> HandleAsync(V1StartWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.Id, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.Id); - var workflow = await this.Workflows.FindAsync(workflowInstance.WorkflowId, cancellationToken); - if (workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), workflowInstance.WorkflowId); - var process = await this.ProcessManager.StartProcessAsync(workflow, workflowInstance, cancellationToken); - workflowInstance.Start(process.Id); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SuspendWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SuspendWorkflowInstanceCommand.cs deleted file mode 100644 index d7810c411..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1SuspendWorkflowInstanceCommand.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to suspend the execution of an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1SuspendWorkflowInstanceCommand))] - public class V1SuspendWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1SuspendWorkflowInstanceCommand() - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to suspend the execution of - public V1SuspendWorkflowInstanceCommand(string id) - { - this.WorkflowInstanceId = id; - } - - /// - /// Gets the id of the to suspend the execution of - /// - public virtual string WorkflowInstanceId { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1SuspendWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The service used to manage runtime proxies - public V1SuspendWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository workflowInstances, IWorkflowRuntimeProxyManager runtimeProxyManager) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - this.RuntimeHostManager = runtimeProxyManager; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - /// Gets the service used to manage runtime proxies - /// - protected IWorkflowRuntimeProxyManager RuntimeHostManager { get; } - - /// - public virtual async Task> HandleAsync(V1SuspendWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.WorkflowInstanceId, cancellationToken); - if (workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.WorkflowInstanceId); - if (this.RuntimeHostManager.TryGetProxy(command.WorkflowInstanceId, out var proxy)) - await proxy.SuspendAsync(cancellationToken); - workflowInstance.Suspend(); - workflowInstance = await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(this.Mapper.Map(workflowInstance)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1TryCorrelateWorkflowInstanceCommand.cs b/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1TryCorrelateWorkflowInstanceCommand.cs deleted file mode 100644 index ea3100b53..000000000 --- a/src/core/Synapse.Application/Commands/WorkflowInstances/v1/V1TryCorrelateWorkflowInstanceCommand.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Commands.WorkflowInstances -{ - - ///

- /// Represents the used to attempt correlating a to an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.WorkflowInstances.V1TryCorrelateWorkflowInstanceCommand))] - public class V1TryCorrelateWorkflowInstanceCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1TryCorrelateWorkflowInstanceCommand() - { - this.WorkflowInstanceId = null!; - this.Event = null!; - this.MappingKeys = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to correlate - /// The to correlate - /// An containing the mapping keys to use to correlate the - public V1TryCorrelateWorkflowInstanceCommand(string workflowInstanceId, V1Event e, IEnumerable mappingKeys) - { - this.WorkflowInstanceId = workflowInstanceId; - this.Event = e; - this.MappingKeys = mappingKeys; - } - - /// - /// Gets the id of the to correlate - /// - public virtual string WorkflowInstanceId { get; protected set; } - - /// - /// Gets the to correlate - /// - public virtual V1Event Event { get; protected set; } - - /// - /// Gets an containing the mapping keys to use to correlate the - /// - public virtual IEnumerable MappingKeys { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1TryCorrelateWorkflowInstanceCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - public V1TryCorrelateWorkflowInstanceCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task> HandleAsync(V1TryCorrelateWorkflowInstanceCommand command, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.WorkflowInstances.FindAsync(command.WorkflowInstanceId, cancellationToken); - if(workflowInstance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), command.WorkflowInstanceId); - var e = workflowInstance.CorrelationContext.PendingEvents.FirstOrDefault(e => e.Id == command.Event.Id); - if (e == null) - { - if (!workflowInstance.CorrelationContext.CorrelatesTo(command.Event)) - return this.Ok(false); - workflowInstance.CorrelationContext.Correlate(command.Event, command.MappingKeys); - } - else - { - workflowInstance.CorrelationContext.RemoveEvent(command.Event); - } - await this.WorkflowInstances.UpdateAsync(workflowInstance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - return this.Ok(true); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Workflows/v1/V1ArchiveWorkflowCommand.cs b/src/core/Synapse.Application/Commands/Workflows/v1/V1ArchiveWorkflowCommand.cs deleted file mode 100644 index a26455c3a..000000000 --- a/src/core/Synapse.Application/Commands/Workflows/v1/V1ArchiveWorkflowCommand.cs +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.EventSourcing; -using Neuroglia.Serialization; -using Synapse.Application.Commands.WorkflowInstances; -using Synapse.Application.Queries.WorkflowInstances; -using Synapse.Application.Queries.Workflows; -using System.IO.Compression; - -namespace Synapse.Application.Commands.Workflows -{ - - ///

- /// Represents the used to archive an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.Workflows.V1ArchiveWorkflowCommand))] - public class V1ArchiveWorkflowCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1ArchiveWorkflowCommand() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to archive - /// The version of the to archive - public V1ArchiveWorkflowCommand(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to archive - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to archive - /// - public virtual string? Version { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1ArchiveWorkflowCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The service used to access the current - /// The service used to provide s - /// The used to manage s - /// The used to manage s - /// The used to manage es - public V1ArchiveWorkflowCommandHandler(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IOptions options, - ISerializerProvider serializerProvider, IRepository workflows, IRepository workflowInstances, IRepository workflowProcesses) - : base(loggerFactory, mediator, mapper) - { - this.Options = options.Value; - this.Serializer = serializerProvider.GetSerializer(this.Options.Archiving.SerializerType); - this.EventStore = serviceProvider.GetService(); - this.Workflows = workflows; - this.WorkflowInstances = workflowInstances; - this.WorkflowProcesses = workflowProcesses; - } - - /// - /// Gets the current - /// - protected SynapseApplicationOptions Options { get; } - - /// - /// Gets the service used to serialize the to archive and its components - /// - protected ISerializer Serializer { get; } - - /// - /// Gets the service used to store events - /// - protected IEventStore? EventStore { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository Workflows { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - /// Gets the used to manage es - /// - protected IRepository WorkflowProcesses { get; } - - /// - public virtual async Task> HandleAsync(V1ArchiveWorkflowCommand command, CancellationToken cancellationToken = default) - { - List workflowVersions; - if (string.IsNullOrWhiteSpace(command.Version)) - workflowVersions = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetWorkflowVersionsQuery(command.Id), cancellationToken); - else - workflowVersions = new() { await this.Mediator.ExecuteAndUnwrapAsync(new V1GetWorkflowByIdQuery(command.Id, command.Version), cancellationToken: cancellationToken) }; - if (!workflowVersions.Any()) - throw DomainException.NullReference(typeof(V1Workflow), command.Id); - var definitionId = workflowVersions.First().Definition.Id!; - var workflowDirectory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), definitionId)); - if (workflowDirectory.Exists) - workflowDirectory.Delete(true); - workflowDirectory.Create(); - foreach (var workflowVersion in workflowVersions) - { - var versionDirectory = new DirectoryInfo(Path.Combine(workflowDirectory.FullName, workflowVersion.Definition.Version)); - if (versionDirectory.Exists) - versionDirectory.Delete(true); - versionDirectory.Create(); - foreach(var workflowInstance in await this.Mediator.ExecuteAndUnwrapAsync(new V1GetWorkflowInstancesByDefinitionIdQuery(workflowVersion.Id), cancellationToken)) - { - using var instanceArchiveStream = await this.Mediator.ExecuteAndUnwrapAsync(new V1ArchiveWorkflowInstanceCommand(workflowInstance.Id), cancellationToken); - using var instanceArchive = new ZipArchive(instanceArchiveStream, ZipArchiveMode.Read); - var instanceArchiveDirectory = new DirectoryInfo(Path.GetTempPath()); - instanceArchive.ExtractToDirectory(instanceArchiveDirectory.FullName, true); - instanceArchiveDirectory.GetDirectories(workflowInstance.Id).First().CopyTo(versionDirectory); - } - } - var archiveFilePath = Path.Combine(Path.GetTempPath(), $"{definitionId}.zip"); - if (File.Exists(archiveFilePath)) - File.Delete(archiveFilePath); - ZipFile.CreateFromDirectory(workflowDirectory.FullName, archiveFilePath, CompressionLevel.Fastest, true); - using var fileStream = File.OpenRead(archiveFilePath); - var stream = new MemoryStream(); - await fileStream.CopyToAsync(stream, cancellationToken); - await stream.FlushAsync(cancellationToken); - stream.Position = 0; - return this.Ok(stream); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Workflows/v1/V1CreateWorkflowCommand.cs b/src/core/Synapse.Application/Commands/Workflows/v1/V1CreateWorkflowCommand.cs deleted file mode 100644 index fb8656163..000000000 --- a/src/core/Synapse.Application/Commands/Workflows/v1/V1CreateWorkflowCommand.cs +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk; -using ServerlessWorkflow.Sdk.Models; -using ServerlessWorkflow.Sdk.Services.Validation; -using Synapse.Application.Commands.Correlations; -using Synapse.Application.Commands.Schedules; -using Synapse.Application.Queries.Workflows; -using System.ComponentModel.DataAnnotations; - -namespace Synapse.Application.Commands.Workflows -{ - - ///

- /// Represents the used to create a new - /// - [DataTransferObjectType(typeof(Integration.Commands.Workflows.V1CreateWorkflowCommand))] - public class V1CreateWorkflowCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1CreateWorkflowCommand() - { - this.Definition = null!; - } - - /// - /// Initializes a new - /// - /// The definition of the to create - /// A boolean indicating whether the should be created only if it does not already exist. Defaults to false, in which case the is automatically versionned - public V1CreateWorkflowCommand(WorkflowDefinition definition, bool ifNotExists) - { - this.Definition = definition; - this.IfNotExists = ifNotExists; - } - - /// - /// Gets the definition of the to create - /// - [Required] - public virtual WorkflowDefinition Definition { get; protected set; } - - /// - /// Gets a boolean indicating whether the should be created only if it does not already exist. Defaults to false, in which case the is automatically versionned - /// - public virtual bool IfNotExists { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1CreateWorkflowCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The service used to validate s - /// The used to manage s - /// The current - public V1CreateWorkflowCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IWorkflowValidator workflowValidator, IRepository workflows, IWorkflowRuntime runtimeHost) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowValidator = workflowValidator; - this.Workflows = workflows; - this.RuntimeHost = runtimeHost; - } - - /// - /// Gets the service used to validate s - /// - protected IWorkflowValidator WorkflowValidator { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository Workflows { get; } - - /// - /// Gets the current - /// - protected IWorkflowRuntime RuntimeHost { get; } - - /// - public virtual async Task> HandleAsync(V1CreateWorkflowCommand command, CancellationToken cancellationToken = default) - { - var validationResult = await this.WorkflowValidator.ValidateAsync(command.Definition, true, true, cancellationToken); - if (!validationResult.IsValid) - return this.Invalid(validationResult.AsErrors().ToArray()); - foreach (var subflowRef in command.Definition.GetSubflowReferences()) - { - var reference = subflowRef.WorkflowId; - if(!string.IsNullOrWhiteSpace(subflowRef.Version)) - reference += $":{subflowRef.Version}"; - var subflow = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetWorkflowByIdQuery(subflowRef.WorkflowId, subflowRef.Version), cancellationToken); - if (subflow == null) - throw DomainException.NullReference(typeof(V1Workflow), $"Failed to find the referenced workflow '{reference}'"); - } - if (command.IfNotExists - && await this.Workflows.ContainsAsync(command.Definition.GetUniqueIdentifier(), cancellationToken)) - return this.NotModified(); - while (await this.Workflows.ContainsAsync(command.Definition.GetUniqueIdentifier(), cancellationToken)) - { - var version = Version.Parse(command.Definition.Version); - version = new Version(version.Major, version.Minor, version.Build == -1 ? 1 : version.Build + 1); - command.Definition.Version = version.ToString(3); - } - var workflow = await this.Workflows.AddAsync(new(command.Definition), cancellationToken); - await this.Workflows.SaveChangesAsync(cancellationToken); - var startState = workflow.Definition.GetStartState(); - if (startState is EventStateDefinition eventState) - { - var lifetime = V1CorrelationLifetime.Transient; - var conditionType = eventState.Exclusive ? V1CorrelationConditionType.AnyOf : V1CorrelationConditionType.AllOf; - var conditions = new List(); - foreach(var trigger in eventState.Triggers) - { - var filters = new List(trigger.Events.Count); - foreach(var eventRef in trigger.Events) - { - if (!workflow.Definition.TryGetEvent(eventRef, out var e)) - throw DomainException.NullReference(typeof(EventDefinition), eventRef, nameof(EventDefinition.Name)); - filters.Add(V1EventFilter.Match(e)); - } - conditions.Add(new(filters.ToArray())); - } - var outcome = new V1CorrelationOutcome(V1CorrelationOutcomeType.Start, workflow.Id); - await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateCorrelationCommand(V1CorrelationActivationType.Implicit, lifetime, conditionType, conditions, outcome, null), cancellationToken: cancellationToken); - } - else if (workflow.Definition.Start?.Schedule != null) - { - await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateScheduleCommand(V1ScheduleActivationType.Implicit, workflow.Definition.Start.Schedule, workflow.Id), cancellationToken); - } - return this.Ok(this.Mapper.Map(workflow)); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Workflows/v1/V1DeleteWorkflowCommand.cs b/src/core/Synapse.Application/Commands/Workflows/v1/V1DeleteWorkflowCommand.cs deleted file mode 100644 index 373be84e3..000000000 --- a/src/core/Synapse.Application/Commands/Workflows/v1/V1DeleteWorkflowCommand.cs +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Application.Commands.Generic; -using Synapse.Application.Commands.WorkflowInstances; -using System.ComponentModel.DataAnnotations; - -namespace Synapse.Application.Commands.Workflows -{ - ///

- /// Represents the used to delete an existing - /// - [DataTransferObjectType(typeof(Integration.Commands.Workflows.V1DeleteWorkflowCommand))] - public class V1DeleteWorkflowCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1DeleteWorkflowCommand() - { - this.WorkflowId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to delete. Note that failing to specify the version will remove all versions of the specified - public V1DeleteWorkflowCommand(string workflowId) - { - this.WorkflowId = workflowId; - } - - /// - /// Gets the id of the to delete. Note that failing to specify the version will remove all versions of the specified - /// - [Required, MinLength(1)] - public virtual string WorkflowId { get; protected set; } - - } - - /// - /// Represents the service used to handle s - /// - public class V1DeleteWorkflowCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The used to manage s - /// The used to manage s - public V1DeleteWorkflowCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository workflowWriteModems, IRepository workflowReadModels, IRepository workflowInstances) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowWriteModels = workflowWriteModems; - this.WorkflowReadModels = workflowReadModels; - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowWriteModels { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowReadModels { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task HandleAsync(V1DeleteWorkflowCommand command, CancellationToken cancellationToken = default) - { - var components = command.WorkflowId.Split(":"); - if(components.Length == 2) - { - var workflow = await this.WorkflowWriteModels.FindAsync(command.WorkflowId, cancellationToken); - if (workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), command.WorkflowId); - foreach (var instanceId in this.WorkflowInstances.AsQueryable() - .Where(i => i.WorkflowId == workflow.Id) - .Select(i => i.Id) - .ToList()) - { - await this.Mediator.ExecuteAndUnwrapAsync(new V1DeleteWorkflowInstanceCommand(instanceId)); - } - workflow.Delete(); - await this.WorkflowWriteModels.UpdateAsync(workflow, cancellationToken); - await this.WorkflowWriteModels.RemoveAsync(workflow, cancellationToken); - await this.WorkflowWriteModels.SaveChangesAsync(cancellationToken); - } - else - { - var workflowIds = this.WorkflowReadModels.AsQueryable() - .Where(w => w.Definition.Id!.Equals(command.WorkflowId, StringComparison.OrdinalIgnoreCase)) - .Select(w => w.Id) - .ToList(); - if(!workflowIds.Any()) - throw DomainException.NullReference(typeof(V1Workflow), command.WorkflowId); - foreach (var workflowId in workflowIds) - { - var workflow = await this.WorkflowWriteModels.FindAsync(workflowId, cancellationToken); - if (workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), workflowId); - workflow.Delete(); - foreach (var instanceId in this.WorkflowInstances.AsQueryable() - .Where(i => i.WorkflowId == workflow.Id) - .Select(i => i.Id) - .ToList()) - { - await this.Mediator.ExecuteAndUnwrapAsync(new V1DeleteWorkflowInstanceCommand(instanceId)); - } - await this.WorkflowWriteModels.UpdateAsync(workflow, cancellationToken); - await this.WorkflowWriteModels.RemoveAsync(workflow, cancellationToken); - } - await this.WorkflowWriteModels.SaveChangesAsync(cancellationToken); - } - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Commands/Workflows/v1/V1UploadWorkflowCommand.cs b/src/core/Synapse.Application/Commands/Workflows/v1/V1UploadWorkflowCommand.cs deleted file mode 100644 index 9b52f4690..000000000 --- a/src/core/Synapse.Application/Commands/Workflows/v1/V1UploadWorkflowCommand.cs +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using ServerlessWorkflow.Sdk.Services.IO; -using System.ComponentModel.DataAnnotations; - -namespace Synapse.Application.Commands.Workflows -{ - - ///

- /// Represents the used to upload a new - /// - [DataTransferObjectType(typeof(Integration.Commands.Workflows.V1UploadWorkflowCommand))] - public class V1UploadWorkflowCommand - : Command - { - - /// - /// Initializes a new - /// - protected V1UploadWorkflowCommand() - { - - } - - /// - /// Initializes a new - /// - /// The that contains the to read - /// An of the s that contain the resources of the to read - public V1UploadWorkflowCommand(IFormFile definitionFile, IEnumerable? resourceFiles) - { - this.DefinitionFile = definitionFile; - this.ResourceFiles = resourceFiles; - } - - /// - /// Gets the that contains the to read - /// - [Required] - public virtual IFormFile DefinitionFile { get; protected set; } = null!; - - /// - /// Gets an of the s that contain the resources of the to read - /// - public virtual IEnumerable? ResourceFiles { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle s - /// - public class V1UploadWorkflowCommandHandler - : CommandHandlerBase, - ICommandHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The service used to read s - public V1UploadWorkflowCommandHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IWorkflowReader workflowReader) - : base(loggerFactory, mediator, mapper) - { - this.WorkflowReader = workflowReader; - } - - /// - /// Gets the service used to read s - /// - protected IWorkflowReader WorkflowReader { get; } - - /// - public virtual async Task> HandleAsync(V1UploadWorkflowCommand command, CancellationToken cancellationToken = default) - { - var directory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString())); - if (directory.Exists) - directory.Delete(true); - directory.Create(); - var filePath = Path.Combine(directory.FullName, command.DefinitionFile.FileName); - using var definitionFileStream = new FileStream(filePath, FileMode.Create); - await command.DefinitionFile.CopyToAsync(definitionFileStream, cancellationToken); - await definitionFileStream.FlushAsync(cancellationToken); - definitionFileStream.Position = 0; - if(command.ResourceFiles != null) - { - foreach (var resourceFile in command.ResourceFiles) - { - filePath = Path.Combine(directory.FullName, resourceFile.FileName); - using var resourceFileStream = new FileStream(filePath, FileMode.Create); - await resourceFile.CopyToAsync(resourceFileStream, cancellationToken); - await resourceFileStream.FlushAsync(cancellationToken); - } - } - var definition = await this.WorkflowReader.ReadAsync(definitionFileStream, new() { BaseDirectory = directory.FullName }, cancellationToken); - var workflow = await this.Mediator.ExecuteAndUnwrapAsync(new V1CreateWorkflowCommand(definition, false), cancellationToken); - return this.Ok(workflow); - } - - } - -} diff --git a/src/core/Synapse.Application/Configuration/ArchivingOptions.cs b/src/core/Synapse.Application/Configuration/ArchivingOptions.cs deleted file mode 100644 index 5a1b788f2..000000000 --- a/src/core/Synapse.Application/Configuration/ArchivingOptions.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Serialization; - -namespace Synapse.Application.Configuration -{ - ///

- /// Represents the options used to configure the way Synapse archives s - /// - public class ArchivingOptions - { - - /// - /// Gets/sets the type of to use to archive s - /// - public virtual Type SerializerType { get; set; } = typeof(NewtonsoftJsonSerializer); - - /// - /// Gets/sets the file extension - /// - public virtual string FileExtension { get; set; } = ".json"; - - } - -} diff --git a/src/core/Synapse.Application/Configuration/CloudEventOptions.cs b/src/core/Synapse.Application/Configuration/CloudEventOptions.cs deleted file mode 100644 index 770f3419e..000000000 --- a/src/core/Synapse.Application/Configuration/CloudEventOptions.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Configuration -{ - ///

- /// Represents the options used to configure the application's cloud eventss - /// - public class CloudEventOptions - { - - /// - /// Gets/sets the options used to configure the sink to post cloud events to - /// - public virtual CloudEventSinkOptions Sink { get; set; } = new(); - - } - -} diff --git a/src/core/Synapse.Application/Configuration/CloudEventSinkOptions.cs b/src/core/Synapse.Application/Configuration/CloudEventSinkOptions.cs deleted file mode 100644 index b5f9a686c..000000000 --- a/src/core/Synapse.Application/Configuration/CloudEventSinkOptions.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Configuration -{ - - ///

- /// Represents the options used to configure the sink to post cloud eventss to - /// - public class CloudEventSinkOptions - { - - /// - /// Initializes a new - /// - public CloudEventSinkOptions() - { - string? uri = EnvironmentVariables.CloudEvents.Sink.Uri.Value; - if (string.IsNullOrWhiteSpace(uri)) - this.Uri = null!; - else - this.Uri = new(uri); - } - - /// - /// Gets/sets the cloud event sink uri - /// - public virtual Uri Uri { get; set; } - - } - -} diff --git a/src/core/Synapse.Application/Configuration/ISynapseApplicationBuilder.cs b/src/core/Synapse.Application/Configuration/ISynapseApplicationBuilder.cs deleted file mode 100644 index 5f9fdc089..000000000 --- a/src/core/Synapse.Application/Configuration/ISynapseApplicationBuilder.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using AutoMapper; - -namespace Synapse.Application.Configuration -{ - - ///

- /// Defines the fundamentals of a service used to build a Synapse application - /// - public interface ISynapseApplicationBuilder - { - - /// - /// Gets the application's - /// - IConfiguration Configuration { get; } - - /// - /// Gets the application's - /// - IServiceCollection Services { get; } - - /// - /// Configures the to use the specified - /// - /// The type of the to use - /// The configured - ISynapseApplicationBuilder AddMappingProfile() - where TProfile : Profile; - - } - -} diff --git a/src/core/Synapse.Application/Configuration/IntegrationEventBusPipelineOptions.cs b/src/core/Synapse.Application/Configuration/IntegrationEventBusPipelineOptions.cs deleted file mode 100644 index 5418adf54..000000000 --- a/src/core/Synapse.Application/Configuration/IntegrationEventBusPipelineOptions.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Configuration -{ - ///

- /// Represents the options used to configure an - /// - public class IntegrationEventBusPipelineOptions - { - - /// - /// Gets/sets a containing the s the pipeline to configure is made out of - /// - public virtual List Middlewares { get; set; } = new(); - - } - -} diff --git a/src/core/Synapse.Application/Configuration/PersistenceOptions.cs b/src/core/Synapse.Application/Configuration/PersistenceOptions.cs deleted file mode 100644 index 11faa4bf4..000000000 --- a/src/core/Synapse.Application/Configuration/PersistenceOptions.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Configuration -{ - ///

- /// Represents the options used to configure the application's persistence layer - /// - public class PersistenceOptions - { - - /// - /// Initializes a new - /// - public PersistenceOptions() - { - var env = EnvironmentVariables.Persistence.WriteModel.DefaultRepository.Value; - if (!string.IsNullOrWhiteSpace(env)) - this.DefaultWriteModelRepository = new() { PluginName = env }; - env = EnvironmentVariables.Persistence.ReadModel.DefaultRepository.Value; - if (!string.IsNullOrWhiteSpace(env)) - this.DefaultReadModelRepository = new() { PluginName = env }; - } - - /// - /// Gets/sets the options used to configure the default write model - /// - public virtual RepositoryOptions DefaultWriteModelRepository { get; set; } = new(); - - /// - /// Gets/sets the options used to configure the default read model - /// - public virtual RepositoryOptions DefaultReadModelRepository { get; set; } = new(); - - /// - /// Gets/sets a containing the mappings of of entity type full names to the to use - /// - public virtual Dictionary Repositories { get; set; } = new(); - - } - -} diff --git a/src/core/Synapse.Application/Configuration/PluginsOptions.cs b/src/core/Synapse.Application/Configuration/PluginsOptions.cs deleted file mode 100644 index 554549ddd..000000000 --- a/src/core/Synapse.Application/Configuration/PluginsOptions.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Configuration -{ - - ///

- /// Represents the options used to configure the application's plugins - /// - public class PluginsOptions - { - - /// - /// Initializes a new - /// - public PluginsOptions() - { - var env = EnvironmentVariables.Plugins.Directory.Value; - if (!string.IsNullOrWhiteSpace(env)) - this.Directory = env; - } - - /// - /// Gets/sets the path to the application's plugins directory - /// - public virtual string Directory { get; set; } = Path.Combine(AppContext.BaseDirectory, "plugins"); - - } - -} diff --git a/src/core/Synapse.Application/Configuration/RepositoryOptions.cs b/src/core/Synapse.Application/Configuration/RepositoryOptions.cs deleted file mode 100644 index 217c3b366..000000000 --- a/src/core/Synapse.Application/Configuration/RepositoryOptions.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure.Plugins; - -namespace Synapse.Application.Configuration -{ - ///

- /// Represents the options used to configure an - /// - public class RepositoryOptions - { - - /// - /// Gets/sets the name of the to use - /// - public virtual string PluginName { get; set; } = null!; - - } - -} diff --git a/src/core/Synapse.Application/Configuration/SynapseApplicationBuilder.cs b/src/core/Synapse.Application/Configuration/SynapseApplicationBuilder.cs deleted file mode 100644 index c1a1cdbd0..000000000 --- a/src/core/Synapse.Application/Configuration/SynapseApplicationBuilder.cs +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using CloudNative.CloudEvents.NewtonsoftJson; -using Microsoft.AspNetCore.JsonPatch; -using Microsoft.AspNetCore.JsonPatch.Adapters; -using Microsoft.Extensions.Http; -using Neuroglia.Data.Expressions; -using Neuroglia.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using ServerlessWorkflow.Sdk; -using Synapse.Infrastructure; -using Synapse.Integration.Serialization.Converters; -using System.Reactive.Subjects; -using System.Reflection; - -namespace Synapse.Application.Configuration -{ - - ///

- /// Represents the default implementation of the interface - /// - public class SynapseApplicationBuilder - : ISynapseApplicationBuilder - { - - /// - /// Initializes a new - /// - /// The current - /// The to configure - public SynapseApplicationBuilder(IConfiguration configuration, IServiceCollection services) - { - this.Configuration = configuration; - this.Services = services; - } - - /// - /// Gets the current - /// - public IConfiguration Configuration { get; } - - /// - /// Gets the to configure - /// - public IServiceCollection Services { get; } - - /// - /// Gets a containing the assemblies to scan to automatically register mapping-related services - /// - protected List MapperAssemblies { get; } = new() { typeof(SynapseApplicationBuilder).Assembly }; - - /// - public virtual ISynapseApplicationBuilder AddMappingProfile() - where TProfile : AutoMapper.Profile - { - var assembly = typeof(TProfile).Assembly; - if(!this.MapperAssemblies.Contains(assembly)) - this.MapperAssemblies.Add(assembly); - return this; - } - - /// - public virtual void Build() - { - JsonConvert.DefaultSettings = () => - { - var settings = new JsonSerializerSettings() - { - ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }, - Converters = new[] { new FilteredExpandoObjectConverter() }, - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore - }; - return settings; - }; - - var writeModelTypes = TypeCacheUtil.FindFilteredTypes("syn:models-write", t => t.IsClass && !t.IsAbstract && typeof(IAggregateRoot).IsAssignableFrom(t), typeof(V1Workflow).Assembly).ToList(); - var readModelTypes = writeModelTypes - .Where(t => t.TryGetCustomAttribute(out _)) - .Select(t => t.GetCustomAttribute()!.Type) - .ToList(); - readModelTypes.AddRange(TypeCacheUtil.FindFilteredTypes("syn:models-read", t => t.IsClass && !t.IsAbstract && t.TryGetCustomAttribute(out _))); - readModelTypes = readModelTypes.Distinct().ToList(); - SynapseApplicationOptions options = new(); - this.Configuration.Bind(options); - - this.Services.AddSingleton(Options.Create(options)); - this.Services.AddLogging(); - this.Services.AddMediator(builder => - { - builder.ScanAssembly(typeof(SynapseApplicationBuilder).Assembly); - builder.UseDefaultPipelineBehavior(typeof(DomainExceptionHandlingMiddleware<,>)); - builder.UseDefaultPipelineBehavior(typeof(FluentValidationMiddleware<,>)); - }); - this.Services.AddGenericQueryHandlers(); - this.Services.AddGenericCommandHandlers(); - this.Services.AddMapper(this.MapperAssemblies.ToArray()); - this.Services.AddSingleton(); - this.Services.AddScoped(); - this.Services.AddTransient(); - this.Services.AddTransient(); - this.Services.AddSingleton(); - this.Services.AddSingleton(provider => provider.GetRequiredService()); - this.Services.AddSingleton(provider => provider.GetRequiredService()); - this.Services.AddSingleton(); - this.Services.AddSingleton(); - this.Services.AddSingleton(); - this.Services.AddHostedService(); - this.Services.AddHostedService(); - this.Services.AddTransient(provider => provider.GetRequiredService().Build()); - this.Services.AddNewtonsoftJsonSerializer(options => - { - options.ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }; - options.NullValueHandling = NullValueHandling.Ignore; - options.DefaultValueHandling = DefaultValueHandling.Ignore; - options.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor; - options.Converters.Add(new AbstractClassConverterFactory()); - options.Converters.Add(new FilteredExpandoObjectConverter()); - }); - this.Services.AddSingleton(); - this.Services.AddCloudEventBus(builder => - { - builder.WithBrokerUri(options.CloudEvents.Sink.Uri); - }); - this.Services.AddServerlessWorkflow(); - this.Services.AddSingleton(); - this.Services.AddSingleton(provider => provider.GetRequiredService()); - this.Services.AddIntegrationEventBus((provider, e) => - { - var stream = provider.GetRequiredService>(); - stream.OnNext(e); - return Task.CompletedTask; - }); - this.Services.AddIntegrationEventBus(async (provider, e) => - { - await provider.GetRequiredService().PublishAsync(e); - }); - this.Services.AddAuthorization(); - this.Services.AddSingleton(); - this.Services.AddSingleton(); - this.Services.AddSingleton(provider => provider.GetRequiredService()); - this.Services.AddSingleton(provider => provider.GetRequiredService()); - this.Services.AddScoped(); - this.Services.AddRepositories(writeModelTypes, ServiceLifetime.Scoped, ApplicationModelType.WriteModel); - this.Services.AddRepositories(readModelTypes, ServiceLifetime.Scoped, ApplicationModelType.ReadModel); - - if (options.SkipCertificateValidation) - { - System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; - this.Services.ConfigureAll(options => - { - options.HttpMessageHandlerBuilderActions.Add(builder => - { - builder.PrimaryHandler = new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true - }; - }); - }); - } - } - - } - -} diff --git a/src/core/Synapse.Application/Configuration/SynapseApplicationOptions.cs b/src/core/Synapse.Application/Configuration/SynapseApplicationOptions.cs deleted file mode 100644 index 64280d855..000000000 --- a/src/core/Synapse.Application/Configuration/SynapseApplicationOptions.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Configuration -{ - - ///

- /// Represents the options used to configure the Synapse application - /// - public class SynapseApplicationOptions - { - - /// - /// Initializes a new - /// - public SynapseApplicationOptions() - { - var env = EnvironmentVariables.SkipCertificateValidation.Value; - - var x = bool.TryParse(env, out _); - - if (!string.IsNullOrWhiteSpace(env) - && bool.TryParse(env, out var skipCertificateValidation)) - this.SkipCertificateValidation = skipCertificateValidation; - } - - /// - /// Gets/sets the options used to configure the way Synapse archives s - /// - public virtual ArchivingOptions Archiving { get; set; } = new(); - - /// - /// Gets/sets the options used to configure the application's cloud eventss - /// - public virtual CloudEventOptions CloudEvents { get; set; } = new(); - - /// - /// Gets/sets the options sued to configure the application's persistence layer - /// - public virtual PersistenceOptions Persistence { get; set; } = new(); - - /// - /// Gets/sets the options sued to configure the application's plugins directory - /// - public virtual PluginsOptions Plugins { get; set; } = new(); - - /// - /// Gets/sets the path to the directory used to monitor workflow definition files - /// - public virtual string DefinitionsDirectory { get; set; } = Path.Combine(AppContext.BaseDirectory, "data", "definitions"); - - /// - /// Gets/sets a boolean indicating whether or not to skip certificate validation when performing http requests - /// - public virtual bool SkipCertificateValidation { get; set; } - - } - -} diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1AuthenticationDefinitionCollectionDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1AuthenticationDefinitionCollectionDomainEventHandler.cs deleted file mode 100644 index 70aeeb01e..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1AuthenticationDefinitionCollectionDomainEventHandler.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Authentications.AuthenticationDefinitionCollections; - -namespace Synapse.Application.Events.Domain -{ - ///

- /// Represents the service used to handle -related s - /// - public class V1AuthenticationDefinitionCollectionDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler - { - - /// - public V1AuthenticationDefinitionCollectionDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1AuthenticationDefinitionCollectionCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1AuthenticationDefinitionCollectionDeletedDomainEvent e, CancellationToken cancellationToken = default) - { - var collection = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.Projections.RemoveAsync(collection, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1CorrelationDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1CorrelationDomainEventHandler.cs deleted file mode 100644 index ba873e95c..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1CorrelationDomainEventHandler.cs +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.Correlations; - -namespace Synapse.Application.Events.Domain -{ - - ///

- /// Represents the service used to handle -related s - /// - public class V1CorrelationDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - - /// - public V1CorrelationDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1CorrelationCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ContextAddedToCorrelationDomainEvent e, CancellationToken cancellationToken = default) - { - var correlation = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - if (correlation.Contexts == null) correlation.Contexts = new List(); - correlation.Contexts.Add(this.Mapper.Map(e.Context)); - await this.Projections.UpdateAsync(correlation, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1EventCorrelatedDomainEvent e, CancellationToken cancellationToken = default) - { - var correlation = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - var context = correlation.Contexts.FirstOrDefault(c => c.Id == e.ContextId); - if (context == null) return; - if (context.PendingEvents == null) context.PendingEvents = new List(); - if (context.PendingEvents.Any(evt => evt.Id == e.Event.Id)) return; - context.PendingEvents.Add(this.Mapper.Map< Integration.Models.V1Event>(e.Event)); - await this.Projections.UpdateAsync(correlation, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1CorrelatedEventReleasedDomainEvent e, CancellationToken cancellationToken = default) - { - var correlation = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - if (correlation.Contexts == null) correlation.Contexts = new List(); - var context = correlation.Contexts.FirstOrDefault(c => c.Id == e.ContextId); - if (context == null) return; - var evt = context.PendingEvents?.FirstOrDefault(x => x.Id.Equals(e.EventId, StringComparison.InvariantCultureIgnoreCase)); - if (evt == null) return; - context.PendingEvents!.Remove(evt); - await this.Projections.UpdateAsync(correlation, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1CorrelationContextReleasedDomainEvent e, CancellationToken cancellationToken = default) - { - var correlation = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - if (correlation.Contexts == null) correlation.Contexts = new List(); - var context = correlation.Contexts.FirstOrDefault(c => c.Id == e.ContextId); - if (context == null) return; - correlation.Contexts.Remove(context); - await this.Projections.UpdateAsync(correlation, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1CorrelationDeletedDomainEvent e, CancellationToken cancellationToken = default) - { - var correlation = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.Projections.RemoveAsync(correlation, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - } - -} diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1EventDefinitionCollectionDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1EventDefinitionCollectionDomainEventHandler.cs deleted file mode 100644 index f20dc77ed..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1EventDefinitionCollectionDomainEventHandler.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.EventDefinitionCollections; - -namespace Synapse.Application.Events.Domain -{ - ///

- /// Represents the service used to handle -related s - /// - public class V1EventDefinitionCollectionDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler - { - - /// - public V1EventDefinitionCollectionDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1EventDefinitionCollectionCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1EventDefinitionCollectionDeletedDomainEvent e, CancellationToken cancellationToken = default) - { - var collection = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.Projections.RemoveAsync(collection, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1FunctionDefinitionCollectionDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1FunctionDefinitionCollectionDomainEventHandler.cs deleted file mode 100644 index 8a64b7273..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1FunctionDefinitionCollectionDomainEventHandler.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using Synapse.Domain.Events.FunctionDefinitionCollections; - -namespace Synapse.Application.Events.Domain -{ - - ///

- /// Represents the service used to handle -related s - /// - public class V1FunctionDefinitionCollectionDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler - { - - /// - public V1FunctionDefinitionCollectionDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1FunctionDefinitionCollectionCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1FunctionDefinitionCollectionDeletedDomainEvent e, CancellationToken cancellationToken = default) - { - var collection = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.Projections.RemoveAsync(collection, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1OperationalReportDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1OperationalReportDomainEventHandler.cs deleted file mode 100644 index efa841f6b..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1OperationalReportDomainEventHandler.cs +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.WorkflowActivities; -using Synapse.Domain.Events.WorkflowInstances; -using Synapse.Domain.Events.Workflows; -using Synapse.Integration.Models; - -namespace Synapse.Application.Events.Domain -{ - - ///

- /// Represents the service used to handle s projected by - /// - public class V1OperationalReportDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to map objects - /// The service used to mediate calls - /// The service used to publish s - /// The current - /// The used to manage s - public V1OperationalReportDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions applicationOptions, IRepository operationalReports) - : base(loggerFactory, mapper, mediator, integrationEventBus, applicationOptions) - { - this.OperationalReports = operationalReports; - } - - /// - /// Gets the used to manage - /// - protected IRepository OperationalReports { get; } - - /// - public virtual async Task HandleAsync(V1WorkflowCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - var metrics = await this.GetOrCreateV1OperationalReportsForAsync(e.CreatedAt, cancellationToken); - metrics.TotalDefinitions++; - await this.OperationalReports.UpdateAsync(metrics, cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - var metrics = await this.GetOrCreateV1OperationalReportsForAsync(e.CreatedAt, cancellationToken); - metrics.TotalInstances++; - await this.OperationalReports.UpdateAsync(metrics, cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceStartedDomainEvent e, CancellationToken cancellationToken = default) - { - var metrics = await this.GetOrCreateV1OperationalReportsForAsync(e.CreatedAt, cancellationToken); - metrics.RunningInstances++; - await this.OperationalReports.UpdateAsync(metrics, cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceExecutedDomainEvent e, CancellationToken cancellationToken = default) - { - var metrics = await this.GetOrCreateV1OperationalReportsForAsync(e.CreatedAt, cancellationToken); - metrics.RunningInstances--; - metrics.ExecutedInstances++; - switch (e.Status) - { - case V1WorkflowInstanceStatus.Completed: - metrics.CompletedInstances++; - break; - case V1WorkflowInstanceStatus.Faulted: - metrics.FaultedInstances++; - break; - case V1WorkflowInstanceStatus.Cancelled: - metrics.CancelledInstances++; - break; - } - await this.OperationalReports.UpdateAsync(metrics, cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - var metrics = await this.GetOrCreateV1OperationalReportsForAsync(e.CreatedAt, cancellationToken); - metrics.TotalActivities++; - await this.OperationalReports.UpdateAsync(metrics, cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityStartedDomainEvent e, CancellationToken cancellationToken = default) - { - var metrics = await this.GetOrCreateV1OperationalReportsForAsync(e.CreatedAt, cancellationToken); - metrics.RunningActivities++; - await this.OperationalReports.UpdateAsync(metrics, cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityExecutedDomainEvent e, CancellationToken cancellationToken = default) - { - var metrics = await this.GetOrCreateV1OperationalReportsForAsync(e.CreatedAt, cancellationToken); - metrics.RunningActivities--; - metrics.ExecutedActivities++; - switch (e.Status) - { - case V1WorkflowActivityStatus.Completed: - metrics.CompletedActivities++; - break; - case V1WorkflowActivityStatus.Faulted: - metrics.FaultedActivities++; - break; - case V1WorkflowActivityStatus.Cancelled: - metrics.CancelledActivities++; - break; - case V1WorkflowActivityStatus.Skipped: - metrics.SkippedActivities++; - break; - } - await this.OperationalReports.UpdateAsync(metrics, cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - - /// - /// Gets or creates the for the specified date - /// - /// The date to get or create the for - /// A - /// The for the specified date - protected virtual async Task GetOrCreateV1OperationalReportsForAsync(DateTimeOffset date, CancellationToken cancellationToken) - { - var metrics = await this.OperationalReports.FindAsync(date.Date.ToString("yyyyMMdd")); - if (metrics == null) - { - metrics = await this.OperationalReports.AddAsync(new(date.Date), cancellationToken); - await this.OperationalReports.SaveChangesAsync(cancellationToken); - } - return metrics; - } - - } - -} diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1ScheduleDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1ScheduleDomainEventHandler.cs deleted file mode 100644 index d0181561c..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1ScheduleDomainEventHandler.cs +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Application.Commands.Schedules; -using Synapse.Domain.Events.Schedules; -using Synapse.Domain.Events.WorkflowInstances; - -namespace Synapse.Application.Events.Domain.v1 -{ - - ///

- /// Represents the service used to handle -related s - /// - public class V1ScheduleDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - - /// - public V1ScheduleDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1ScheduleCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleDefinitionChangedDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - schedule.LastModified = e.CreatedAt.DateTime; - schedule.Definition = e.Definition; - schedule.NextOccurenceAt = e.NextOccurenceAt?.Date; - await this.Projections.UpdateAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleSuspendedDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - schedule.LastModified = e.CreatedAt.UtcDateTime; - schedule.SuspendedAt = e.CreatedAt.UtcDateTime; - schedule.NextOccurenceAt = null; - schedule.Status = V1ScheduleStatus.Suspended; - await this.Projections.UpdateAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleResumedDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - schedule.LastModified = e.CreatedAt.UtcDateTime; - schedule.NextOccurenceAt = e.NextOccurenceAt?.UtcDateTime; - schedule.SuspendedAt = null; - schedule.Status = V1ScheduleStatus.Active; - await this.Projections.UpdateAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleOccuredDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - schedule.LastModified = e.CreatedAt.UtcDateTime; - schedule.LastOccuredAt = e.CreatedAt.UtcDateTime; - schedule.NextOccurenceAt = e.NextOccurenceAt?.UtcDateTime; - schedule.TotalOccurences++; - if (schedule.ActiveOccurences == null) schedule.ActiveOccurences = new(); - schedule.ActiveOccurences.Add(e.WorkflowInstanceId); - await this.Projections.UpdateAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleOccurenceCompletedDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - schedule.LastModified = e.CreatedAt.UtcDateTime; - schedule.LastCompletedAt = e.CreatedAt.UtcDateTime; - schedule.NextOccurenceAt = e.NextOccurenceAt?.UtcDateTime; - if (schedule.ActiveOccurences != null) schedule.ActiveOccurences.Remove(e.WorkflowInstanceId); - await this.Projections.UpdateAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleRetiredDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - schedule.LastModified = e.CreatedAt.UtcDateTime; - schedule.NextOccurenceAt = null; - schedule.Status = V1ScheduleStatus.Retired; - await this.Projections.UpdateAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleObsolitedDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - schedule.LastModified = e.CreatedAt.UtcDateTime; - schedule.NextOccurenceAt = null; - schedule.Status = V1ScheduleStatus.Obsolete; - await this.Projections.UpdateAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1ScheduleDeletedDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.Projections.RemoveAsync(schedule, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceExecutedDomainEvent e, CancellationToken cancellationToken = default) - { - var schedule = this.Projections.AsQueryable() - .FirstOrDefault(s => s.ActiveOccurences != null && s.ActiveOccurences.Contains(e.AggregateId)); - if (schedule == null) return; - await this.Mediator.ExecuteAndUnwrapAsync(new V1CompleteScheduleOccurenceCommand(schedule.Id, e.AggregateId), cancellationToken); - } - - } - -} diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowActivityDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowActivityDomainEventHandler.cs deleted file mode 100644 index 0072766b9..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowActivityDomainEventHandler.cs +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Neuroglia.Serialization; -using Synapse.Domain.Events.WorkflowActivities; - -namespace Synapse.Application.Events.Domain -{ - ///

- /// Represents the service used to handle -related s - /// - public class V1WorkflowActivityDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - - /// - public V1WorkflowActivityDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections, - IRepository workflowInstances) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - this.WorkflowInstances = workflowInstances; - } - - /// - /// Gets the used to manage - /// - protected IRepository WorkflowInstances { get; } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityStartedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.StartedAt = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Running; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivitySuspendedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Suspended; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityResumedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Running; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityFaultedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.ExecutedAt = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Faulted; - activity.Error = this.Mapper.Map(e.Error); - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityCompensatingDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Compensating; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityCompensatedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Compensated; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityCancelledDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.ExecutedAt = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Cancelled; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivitySkippedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.ExecutedAt = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Skipped; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityCompletedDomainEvent e, CancellationToken cancellationToken = default) - { - var activity = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - activity.LastModified = e.CreatedAt.UtcDateTime; - activity.ExecutedAt = e.CreatedAt.UtcDateTime; - activity.Status = V1WorkflowActivityStatus.Completed; - var outputValue = e.Output as Dynamic; - if (outputValue == null - && e.Output != null) - outputValue = Dynamic.FromObject(e.Output); - activity.Output = outputValue; - await this.Projections.UpdateAsync(activity, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.UpdateParentWorkflowInstanceAsync(activity, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowActivityExecutedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - /// Updates the specified parent - /// - /// The to update the parent of - /// A - /// A new awaitable - protected virtual async ValueTask UpdateParentWorkflowInstanceAsync(Integration.Models.V1WorkflowActivity activity, CancellationToken cancellationToken) - { - var instance = await this.WorkflowInstances.FindAsync(activity.WorkflowInstanceId, cancellationToken); - Integration.Models.V1WorkflowActivity existingActivity = instance.Activities.FirstOrDefault(a => a.Id == activity.Id)!; - if (existingActivity == null) - { - instance.Activities.Add(activity); - } - else - { - var list = instance.Activities.ToList(); - var index = list.IndexOf(existingActivity); - existingActivity = this.Mapper.Map(activity, existingActivity); - list[index] = existingActivity; - instance.Activities = list; - } - await this.WorkflowInstances.UpdateAsync(instance, cancellationToken); - await this.WorkflowInstances.SaveChangesAsync(cancellationToken); - } - - } - -} diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowDomainEventHandler.cs deleted file mode 100644 index 571e9d3d4..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowDomainEventHandler.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Application.Queries.Generic; -using Synapse.Domain.Events.WorkflowInstances; -using Synapse.Domain.Events.Workflows; - -namespace Synapse.Application.Events.Domain -{ - - ///

- /// Represents the service used to handle -related s - /// - public class V1WorkflowDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - - /// - public V1WorkflowDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1WorkflowCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanciatedDomainEvent e, CancellationToken cancellationToken = default) - { - var workflow = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - workflow.LastModified = e.CreatedAt.UtcDateTime; - workflow.LastInstanciated = e.CreatedAt.UtcDateTime; - workflow.TotalInstanceCount++; - await this.Projections.UpdateAsync(workflow, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowDeletedDomainEvent e, CancellationToken cancellationToken = default) - { - var workflow = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.Projections.RemoveAsync(workflow, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceStartedDomainEvent e, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.Mediator.ExecuteAndUnwrapAsync(new V1FindByIdQuery(e.AggregateId)); - var workflow = await this.GetOrReconcileProjectionAsync(workflowInstance.WorkflowId, cancellationToken); - workflow.RunningInstanceCount++; - await this.Projections.UpdateAsync(workflow, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceExecutedDomainEvent e, CancellationToken cancellationToken = default) - { - var workflowInstance = await this.Mediator.ExecuteAndUnwrapAsync(new V1FindByIdQuery(e.AggregateId)); - var workflow = await this.GetOrReconcileProjectionAsync(workflowInstance.WorkflowId, cancellationToken); - workflow.RunningInstanceCount--; - workflow.ExecutedInstanceCount++; - switch (e.Status) - { - case V1WorkflowInstanceStatus.Completed: - workflow.CompletedInstanceCount++; - break; - case V1WorkflowInstanceStatus.Faulted: - workflow.FaultedInstanceCount++; - break; - case V1WorkflowInstanceStatus.Cancelled: - workflow.CancelledInstanceCount++; - break; - } - if (!workflowInstance.ExecutedAt.HasValue) - workflowInstance.ExecutedAt = e.CreatedAt.DateTime; - workflow.TotalInstanceExecutionTime = workflow.TotalInstanceExecutionTime .Add(workflowInstance.Duration!.Value); - if (!workflow.ShortestInstanceDuration.HasValue - || workflowInstance.Duration < workflow.ShortestInstanceDuration) - workflow.ShortestInstanceDuration = workflowInstance.Duration; - if (!workflow.LongestInstanceDuration.HasValue - || workflowInstance.Duration > workflow.LongestInstanceDuration) - workflow.LongestInstanceDuration = workflowInstance.Duration; - await this.Projections.UpdateAsync(workflow, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - } - - } - -} diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowInstanceDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowInstanceDomainEventHandler.cs deleted file mode 100644 index 0eb052324..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowInstanceDomainEventHandler.cs +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Serialization; -using Synapse.Domain.Events.WorkflowInstances; - -namespace Synapse.Application.Events.Domain -{ - - ///

- /// Represents the service used to handle -related s - /// - public class V1WorkflowInstanceDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - - /// - public V1WorkflowInstanceDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceCreatedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceSchedulingDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Scheduling; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceScheduledDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Scheduled; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceStartingDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Starting; - if (instance.Sessions == null) - instance.Sessions = new List(); - instance.Sessions.Add(new() { StartedAt = e.CreatedAt.DateTime, ProcessId = e.ProcessId }); - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceStartedDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.StartedAt = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Running; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceSuspendingDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Suspending; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceSuspendedDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Suspended; - var session = instance.Sessions?.FirstOrDefault(s => s.IsActive); - if(session != null) - session.EndedAt = e.CreatedAt.UtcDateTime; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceResumingDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Resuming; - if (instance.Sessions == null) - instance.Sessions = new List(); - instance.Sessions.Add(new() { StartedAt = e.CreatedAt.DateTime, ProcessId = e.ProcessId }); - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceResumedDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Running; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceFaultedDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.ExecutedAt = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Faulted; - instance.Error = this.Mapper.Map(e.Error); - var session = instance.Sessions?.FirstOrDefault(s => s.IsActive); - if (session != null) - session.EndedAt = e.CreatedAt.UtcDateTime; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceCancellingDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Cancelling; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceCancelledDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.ExecutedAt = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Cancelled; - var session = instance.Sessions?.FirstOrDefault(s => s.IsActive); - if (session != null) - session.EndedAt = e.CreatedAt.UtcDateTime; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceCompletedDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - instance.LastModified = e.CreatedAt.UtcDateTime; - instance.ExecutedAt = e.CreatedAt.UtcDateTime; - instance.Status = V1WorkflowInstanceStatus.Completed; - var outputValue = e.Output as Dynamic; - if (outputValue == null - && e.Output != null) - outputValue = Dynamic.FromObject(e.Output); - instance.Output = outputValue; - var session = instance.Sessions?.FirstOrDefault(s => s.IsActive); - if (session != null) - session.EndedAt = e.CreatedAt.UtcDateTime; - await this.Projections.UpdateAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceExecutedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowInstanceDeletedDomainEvent e, CancellationToken cancellationToken = default) - { - var instance = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.Projections.RemoveAsync(instance, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowProcessDomainEventHandler.cs b/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowProcessDomainEventHandler.cs deleted file mode 100644 index aff3842ad..000000000 --- a/src/core/Synapse.Application/Events/Domain/v1/V1WorkflowProcessDomainEventHandler.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.V1WorkflowProcesses; - -namespace Synapse.Application.Events.Domain -{ - - ///

- /// Represents the service used to handle -related s - /// - public class V1WorkflowProcessDomainEventHandler - : DomainEventHandlerBase, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - - /// - public V1WorkflowProcessDomainEventHandler(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, IOptions synapseOptions, - IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions, aggregates, projections) - { - - } - - /// - public virtual async Task HandleAsync(V1WorkflowProcessStartedDomainEvent e, CancellationToken cancellationToken = default) - { - await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - await this.PublishIntegrationEventAsync(e, cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowProcessLogOutputDomainEvent e, CancellationToken cancellationToken = default) - { - var process = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - process.Logs += e.Log + Environment.NewLine; - await this.Projections.UpdateAsync(process, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - } - - /// - public virtual async Task HandleAsync(V1WorkflowProcessExitedDomainEvent e, CancellationToken cancellationToken = default) - { - var process = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken); - process.ExitedAt = e.CreatedAt.UtcDateTime; - process.ExitCode = e.ExitCode; - await this.Projections.UpdateAsync(process, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - } - - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Events/DomainEventHandlerBase.cs b/src/core/Synapse.Application/Events/DomainEventHandlerBase.cs deleted file mode 100644 index 944e2745a..000000000 --- a/src/core/Synapse.Application/Events/DomainEventHandlerBase.cs +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events; -using System.Net.Mime; - -namespace Synapse.Application.Events -{ - - ///

- /// Represents the base class for all handlers - /// - public abstract class DomainEventHandlerBase - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to map objects - /// The service used to mediate calls - /// The service used to publish s - /// The current - protected DomainEventHandlerBase(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, IOptions applicationOptions) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Mapper = mapper; - this.Mediator = mediator; - this.IntegrationEventBus = integrationEventBus; - this.ApplicationOptions = applicationOptions.Value; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to publish s - /// - protected IIntegrationEventBus IntegrationEventBus { get; } - - /// - /// Gets the current - /// - protected SynapseApplicationOptions ApplicationOptions { get; } - - /// - /// Publishes a for the specified - /// - /// The to publish a new for - /// A - /// A new awaitable - protected virtual async Task PublishIntegrationEventAsync(TEvent e, CancellationToken cancellationToken) - where TEvent : class, IDomainEvent - { - if (!e.GetType().TryGetCustomAttribute(out DataTransferObjectTypeAttribute dataTransferObjectTypeAttribute)) - return; - var integrationEvent = (V1IntegrationEvent)this.Mapper.Map(e, e.GetType(), dataTransferObjectTypeAttribute.Type); - var aggregateType = e.GetType().GetGenericType(typeof(DomainEvent<,>)).GetGenericArguments()[0]; - var cloudEvent = new CloudEvent() - { - Id = Guid.NewGuid().ToString(), - Source = CloudEvents.Source, - Type = CloudEvents.TypeOf(typeof(TEvent), aggregateType), - Time = e.CreatedAt, - Subject = e.AggregateId.ToString(), - DataSchema = CloudEvents.SchemaOf(typeof(TEvent), aggregateType), - DataContentType = MediaTypeNames.Application.Json, - Data = integrationEvent - }; - await this.IntegrationEventBus.PublishAsync(cloudEvent, cancellationToken); - } - - } - - /// - /// Represents the base class for all handlers - /// - /// The type of to handle the s of - /// The type of projection managed by the - /// The type of key used to uniquely authenticate the s that produce handled s - public abstract class DomainEventHandlerBase - : DomainEventHandlerBase - where TAggregate : class, IAggregateRoot - where TProjection : class, IIdentifiable - where TKey : IEquatable - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to map objects - /// The service used to mediate calls - /// The service used to publish s - /// The current - /// The used to manage the s to handle the s of - /// The used to manage the 's projections - protected DomainEventHandlerBase(ILoggerFactory loggerFactory, IMapper mapper, IMediator mediator, IIntegrationEventBus integrationEventBus, - IOptions synapseOptions, IRepository aggregates, IRepository projections) - : base(loggerFactory, mapper, mediator, integrationEventBus, synapseOptions) - { - this.Aggregates = aggregates; - this.Projections = projections; - } - - /// - /// Gets the used to manage the s to handle the s of - /// - protected IRepository Aggregates { get; } - - /// - /// Gets the used to manage the 's projections - /// - protected IRepository Projections { get; } - - /// - /// Gets or reconciles the projection for the with the specified key - /// - /// The id of the to get the projection for - /// A - /// The projection for the with the specified key - protected virtual async Task GetOrReconcileProjectionAsync(TKey aggregateKey, CancellationToken cancellationToken) - { - TProjection projection = await this.Projections.FindAsync(aggregateKey, cancellationToken); - if (projection == null) - { - TAggregate aggregate = await this.Aggregates.FindAsync(aggregateKey, cancellationToken); - if (aggregate == null) - { - this.Logger.LogError("Failed to find an aggregate of type '{aggregateType}' with the specified key '{key}'", typeof(TAggregate), aggregateKey); - throw new Exception($"Failed to find an aggregate of type '{typeof(TAggregate)}' with the specified key '{aggregateKey}'"); - } - projection = await this.ProjectAsync(aggregate, cancellationToken); - projection = await this.Projections.AddAsync(projection, cancellationToken); - await this.Projections.SaveChangesAsync(cancellationToken); - } - return projection; - } - - /// - /// Projects the specified - /// - /// The to project - /// A - /// The projected - protected virtual async Task ProjectAsync(TAggregate aggregate, CancellationToken cancellationToken) - { - return await Task.FromResult(this.Mapper.Map(aggregate)); - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/DirectoryInfoExtensions.cs b/src/core/Synapse.Application/Extensions/DirectoryInfoExtensions.cs deleted file mode 100644 index 11cf615ea..000000000 --- a/src/core/Synapse.Application/Extensions/DirectoryInfoExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class DirectoryInfoExtensions - { - - /// - /// Copies the to the specified target - /// - /// The source - /// The target - - public static void CopyTo(this DirectoryInfo sourceDirectory, DirectoryInfo targetDirectory) - { - foreach (var directory in sourceDirectory.GetDirectories("*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(directory.FullName.Replace(sourceDirectory.FullName, targetDirectory.FullName)); - } - foreach (var file in sourceDirectory.GetFiles("*.*", SearchOption.AllDirectories)) - { - File.Copy(file.FullName, file.FullName.Replace(sourceDirectory.FullName, targetDirectory.FullName), true); - } - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/FileInfoExtensions.cs b/src/core/Synapse.Application/Extensions/FileInfoExtensions.cs deleted file mode 100644 index 5e53443ea..000000000 --- a/src/core/Synapse.Application/Extensions/FileInfoExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class FileInfoExtensions - { - - /// - /// Determines whether or not the is locked - /// - /// The to check - /// A boolean indicating whether or not the is locked - /// Code taken from - public static bool IsLocked(this FileInfo file) - { - var stream = null as FileStream; - try - { - stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); - } - catch (IOException) - { - return true; - } - finally - { - if (stream != null) - stream.Close(); - } - return false; - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IBackgroundJobManagerExtensions.cs b/src/core/Synapse.Application/Extensions/IBackgroundJobManagerExtensions.cs deleted file mode 100644 index 046d7eedf..000000000 --- a/src/core/Synapse.Application/Extensions/IBackgroundJobManagerExtensions.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Application.Commands.Schedules; - -namespace Synapse -{ - - ///

- /// Defines extensions for s - /// - public static class IBackgroundJobManagerExtensions - { - - /// - /// Schedules a new background job - /// - /// The service used to manage background jobs - /// An object that defines the background job to schedule - /// A - /// A new awaitable - public static async Task ScheduleJobAsync(this IBackgroundJobManager backgroundJobManager, V1Schedule schedule, CancellationToken cancellationToken = default) - { - if (schedule == null) throw new ArgumentNullException(nameof(schedule)); - if (!schedule.NextOccurenceAt.HasValue) throw new ArgumentException("The specified schedule does not define a next occurence", nameof(schedule)); - await backgroundJobManager.ScheduleJobAsync(schedule.Id, async provider => await OnInstanciateWorkflowAsync(provider, schedule.Id), schedule.NextOccurenceAt.Value, cancellationToken); - } - - /// - /// Schedules a new background job - /// - /// The service used to manage background jobs - /// An object that defines the background job to schedule - /// A - /// A new awaitable - public static async Task ScheduleJobAsync(this IBackgroundJobManager backgroundJobManager, Integration.Models.V1Schedule schedule, CancellationToken cancellationToken = default) - { - if (schedule == null) throw new ArgumentNullException(nameof(schedule)); - if (!schedule.NextOccurenceAt.HasValue) throw new ArgumentException("The specified schedule does not define a next occurence", nameof(schedule)); - await backgroundJobManager.ScheduleJobAsync(schedule.Id, async provider => await OnInstanciateWorkflowAsync(provider, schedule.Id), schedule.NextOccurenceAt.Value, cancellationToken); - } - - private static async Task OnInstanciateWorkflowAsync(IServiceProvider serviceProvider, string scheduleId) - { - var mediator = serviceProvider.GetRequiredService(); - await mediator.ExecuteAndUnwrapAsync(new V1TriggerScheduleCommand(scheduleId)); - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IHostEnvironmentExtensions.cs b/src/core/Synapse.Application/Extensions/IHostEnvironmentExtensions.cs deleted file mode 100644 index 5348b54c7..000000000 --- a/src/core/Synapse.Application/Extensions/IHostEnvironmentExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class IHostEnvironmentExtensions - { - - /// - /// Determines whether or not the runs in Docker - /// - /// The to check - /// A boolean indicating whether or not the runs in Docker - public static bool RunsInDocker(this IHostEnvironment env) - { - return File.Exists("/.dockerenv"); - } - - /// - /// Determines whether or not the runs in Kubernetes - /// - /// The to check - /// A boolean indicating whether or not the runs in Kubernetes - public static bool RunsInKubernetes(this IHostEnvironment env) - { - return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST")); - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IMediatorExtensions.cs b/src/core/Synapse.Application/Extensions/IMediatorExtensions.cs deleted file mode 100644 index b591c1330..000000000 --- a/src/core/Synapse.Application/Extensions/IMediatorExtensions.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Defines extensions for s - /// - public static class IMediatorExtensions - { - - /// - /// Executes and unwraps the result of the specified - /// - /// The expected type of result returned by the to process - /// The to use to execute the - /// The to execute - /// A - /// A new awaitable - /// A new in case errors occured during the 's execution - public static async Task ExecuteAndUnwrapAsync(this IMediator mediator, ICommand request, CancellationToken cancellationToken = default) - where TResult : IOperationResult - { - var result = await mediator.ExecuteAsync(request, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(result); - } - - /// - /// Executes and unwraps the result of the specified - /// - /// The expected type of result returned by the to process - /// The type of data wrapped by the result returned by the to process - /// The to use to execute the - /// The to execute - /// A - /// A new awaitable - /// A new in case errors occured during the 's execution - public static async Task ExecuteAndUnwrapAsync(this IMediator mediator, ICommand request, CancellationToken cancellationToken = default) - where TResult : IOperationResult - { - var result = await mediator.ExecuteAsync(request, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(result); - return result.Data; - } - - /// - /// Executes and unwraps the result of the specified - /// - /// The expected type of result returned by the to process - /// The type of data wrapped by the result returned by the to process - /// The to use to execute the - /// The to execute - /// A - /// A new awaitable - /// A new in case errors occured during the 's execution - public static async Task ExecuteAndUnwrapAsync(this IMediator mediator, IQuery request, CancellationToken cancellationToken = default) - where TResult : IOperationResult - { - var result = await mediator.ExecuteAsync(request, cancellationToken); - if (!result.Succeeded) - throw new OperationResultException(result); - return result.Data; - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IPluginManagerExtensions.cs b/src/core/Synapse.Application/Extensions/IPluginManagerExtensions.cs deleted file mode 100644 index f14b763c5..000000000 --- a/src/core/Synapse.Application/Extensions/IPluginManagerExtensions.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure.Plugins; - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class IPluginManagerExtensions - { - - /// - /// Gets the of the specified type - /// - /// The type of the to get - /// The extended - /// The of the specified type - public static TPlugin GetRequiredPlugin(this IPluginManager pluginManager) - where TPlugin : IPlugin - { - var plugin = pluginManager.GetPlugin(); - if (plugin == null) - throw new NullReferenceException($"Failed to find the required plugin of type '{typeof(TPlugin).FullName}'"); - return plugin; - } - - /// - /// Gets the of the specified type, with the specified name - /// - /// The type of the to get - /// The extended - /// The name of the to get - /// The of the specified type - public static TPlugin? GetPlugin(this IPluginManager pluginManager, string name) - where TPlugin : IPlugin - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullException(nameof(name)); - return (TPlugin)pluginManager.GetPlugin(name)!; - } - - /// - /// Attempts to get the with the specified name - /// - /// The extended - /// The name of the to get - /// The matched plugin - /// A boolean indicating whether or an with the specified name could be found - public static bool TryGetPlugin(this IPluginManager pluginManager, string name, out IPlugin plugin) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullException(nameof(name)); - plugin = pluginManager.GetPlugin(name)!; - return plugin != null; - } - - /// - /// Attempts to get the with the specified name - /// - /// The type of the to get - /// The extended - /// The name of the to get - /// The matched plugin - /// A boolean indicating whether or an with the specified name could be found - public static bool TryGetPlugin(this IPluginManager pluginManager, string name, out TPlugin plugin) - where TPlugin : IPlugin - { - if(string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullException(nameof(name)); - plugin = pluginManager.GetPlugin(name)!; - return plugin != null; - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IRepositoryFactoryExtensions.cs b/src/core/Synapse.Application/Extensions/IRepositoryFactoryExtensions.cs deleted file mode 100644 index b08846b75..000000000 --- a/src/core/Synapse.Application/Extensions/IRepositoryFactoryExtensions.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure; - -namespace Synapse -{ - ///

- /// Defines extensions for instances - /// - public static class IRepositoryFactoryExtensions - { - - /// - /// Creates a new - /// - /// The extended - /// The type of entity to create the for - /// The type of the application model to create a new for - /// A new - public static IRepository CreateRepository(this IRepositoryFactory factory, Type entityType, ApplicationModelType modelType) - { - if (entityType == null) - throw new ArgumentNullException(nameof(entityType)); - var keyType = entityType.GetGenericType(typeof(IIdentifiable<>)).GetGenericArguments()[0]; - return factory.CreateRepository(entityType, keyType, modelType); - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IServiceCollectionExtensions.cs b/src/core/Synapse.Application/Extensions/IServiceCollectionExtensions.cs deleted file mode 100644 index b64a9a7e2..000000000 --- a/src/core/Synapse.Application/Extensions/IServiceCollectionExtensions.cs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.DependencyInjection.Extensions; -using Synapse.Application.Commands.Generic; -using Synapse.Application.Queries.Generic; -using Synapse.Domain; -using Synapse.Infrastructure; -using System.Reflection; - -namespace Synapse.Application.Configuration -{ - - ///

- /// Defines extensions for s - /// - public static class IServiceCollectionExtensions - { - - /// - /// Adds and configures the services required by the Synapse Serverless Workflow runtime - /// - /// The to configure - /// The current - /// An used to configure Synapse - /// The configured - public static IServiceCollection AddSynapse(this IServiceCollection services, IConfiguration configuration, Action setup) - { - var applicationOptions = new SynapseApplicationOptions(); - configuration.Bind(applicationOptions); - services.Configure(configuration); - var appBuilder = new SynapseApplicationBuilder(configuration, services); - setup(appBuilder); - appBuilder.Build(); - return services; - } - - /// - /// Adds and configures the service - /// - /// The to configure - /// The configured - public static IServiceCollection AddIntegrationEventBus(this IServiceCollection services) - { - services.TryAddSingleton(); - services.TryAddSingleton(provider => provider.GetRequiredService().Create()); - return services; - } - - /// - /// Adds the specified to the pipeline - /// - /// The to configure - /// The middleware to add to the pipeline - /// The configured - public static IServiceCollection AddIntegrationEventBus(this IServiceCollection services, PublishIntegrationEventDelegate middleware) - { - services.AddIntegrationEventBus(); - services.Configure(options => - { - options.Middlewares.Add(middleware); - }); - return services; - } - - internal static IServiceCollection AddGenericQueryHandlers(this IServiceCollection services, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped) - { - foreach (Type queryableType in TypeCacheUtil.FindFilteredTypes("nqt", t => t.TryGetCustomAttribute(out _), typeof(QueryableAttribute).Assembly)) - { - var keyType = queryableType.GetGenericType(typeof(IIdentifiable<>)).GetGenericArguments().First(); - var queryType = typeof(V1FindByIdQuery<,>).MakeGenericType(queryableType, keyType); - var resultType = typeof(IOperationResult<>).MakeGenericType(queryableType); - var handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); - var handlerImplementationType = typeof(V1FindByKeyQueryHandler<,>).MakeGenericType(queryableType, keyType); - services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); - services.Add(new ServiceDescriptor(typeof(IMiddleware<,>).MakeGenericType(queryType, resultType), typeof(DomainExceptionHandlingMiddleware<,>).MakeGenericType(queryType, resultType), serviceLifetime)); - - queryType = typeof(V1ListQuery<>).MakeGenericType(queryableType); - resultType = typeof(IOperationResult<>).MakeGenericType(typeof(IQueryable<>).MakeGenericType(queryableType)); - handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); - handlerImplementationType = typeof(V1ListQueryHandler<>).MakeGenericType(queryableType); - services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); - services.Add(new ServiceDescriptor(typeof(IMiddleware<,>).MakeGenericType(queryType, resultType), typeof(DomainExceptionHandlingMiddleware<,>).MakeGenericType(queryType, resultType), serviceLifetime)); - - queryType = typeof(V1FilterQuery<>).MakeGenericType(queryableType); - resultType = typeof(IOperationResult<>).MakeGenericType(typeof(List<>).MakeGenericType(queryableType)); - handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(queryType, resultType); - handlerImplementationType = typeof(V1FilterQueryHandler<>).MakeGenericType(queryableType); - services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); - services.Add(new ServiceDescriptor(typeof(IMiddleware<,>).MakeGenericType(queryType, resultType), typeof(DomainExceptionHandlingMiddleware<,>).MakeGenericType(queryType, resultType), serviceLifetime)); - } - return services; - } - - internal static IServiceCollection AddGenericCommandHandlers(this IServiceCollection services, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped) - { - foreach (Type aggregateType in TypeCacheUtil.FindFilteredTypes("domain:aggregates", t => t.IsClass && !t.IsAbstract && typeof(IAggregateRoot).IsAssignableFrom(t), typeof(V1Workflow).Assembly)) - { - Type dtoType = aggregateType.GetCustomAttribute()!.Type; - Type keyType = aggregateType.GetGenericType(typeof(IIdentifiable<>)).GetGenericArguments().First(); - Type commandType; - Type resultType; - Type handlerServiceType; - Type handlerImplementationType; - if (aggregateType.TryGetCustomAttribute(out PatchableAttribute patchableAttribute)) - { - commandType = typeof(V1PatchCommand<,,>).MakeGenericType(aggregateType, dtoType, keyType); - resultType = typeof(IOperationResult<>).MakeGenericType(dtoType); - handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); - handlerImplementationType = typeof(PatchCommandHandler<,,>).MakeGenericType(aggregateType, dtoType, keyType); - services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); - services.Add(new ServiceDescriptor(typeof(IMiddleware<,>).MakeGenericType(commandType, resultType), typeof(DomainExceptionHandlingMiddleware<,>).MakeGenericType(commandType, resultType), serviceLifetime)); - } - - if (typeof(IDeletable).IsAssignableFrom(aggregateType)) - { - commandType = typeof(V1DeleteCommand<,>).MakeGenericType(aggregateType, keyType); - resultType = typeof(IOperationResult); - handlerServiceType = typeof(IRequestHandler<,>).MakeGenericType(commandType, resultType); - handlerImplementationType = typeof(DeleteCommandHandler<,>).MakeGenericType(aggregateType, keyType); - services.Add(new ServiceDescriptor(handlerServiceType, handlerImplementationType, serviceLifetime)); - services.Add(new ServiceDescriptor(typeof(IMiddleware<,>).MakeGenericType(commandType, resultType), typeof(DomainExceptionHandlingMiddleware<,>).MakeGenericType(commandType, resultType), serviceLifetime)); - } - } - return services; - } - - static IServiceCollection AddRepository(this IServiceCollection services, Type entityType, ServiceLifetime lifetime, ApplicationModelType modelType) - { - var keyType = entityType.GetGenericType(typeof(IIdentifiable<>)).GetGenericArguments()[0]; - var genericRepositoryType = typeof(IRepository<,>).MakeGenericType(entityType, keyType); - services.Add(new(genericRepositoryType, provider => provider.GetRequiredService().CreateRepository(entityType, modelType), lifetime)); - services.Add(new(typeof(IRepository<>).MakeGenericType(entityType), provider => provider.GetRequiredService(genericRepositoryType), lifetime)); - return services; - } - - internal static IServiceCollection AddRepositories(this IServiceCollection services, IEnumerable entityTypes, ServiceLifetime lifetime, ApplicationModelType modelType) - { - foreach (Type entityType in entityTypes) - { - services.AddRepository(entityType, lifetime, modelType); - } - return services; - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IWorkflowRuntimeProxyManagerExtensions.cs b/src/core/Synapse.Application/Extensions/IWorkflowRuntimeProxyManagerExtensions.cs deleted file mode 100644 index 02aa67387..000000000 --- a/src/core/Synapse.Application/Extensions/IWorkflowRuntimeProxyManagerExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class IWorkflowRuntimeProxyManagerExtensions - { - - /// - /// Attempts to get the for the specified - /// - /// - /// The id of the to get the for - /// The for the specified , if any - /// A boolean indicating whether or not a proxy for the specified could be found - public static bool TryGetProxy(this IWorkflowRuntimeProxyManager proxyManager, string workflowInstanceId, out IWorkflowRuntimeProxy proxy) - { - proxy = null!; - try - { - proxy = proxyManager.GetProxy(workflowInstanceId); - return proxy != null; - } - catch - { - return false; - } - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/IWorkflowValidationResultExtensions.cs b/src/core/Synapse.Application/Extensions/IWorkflowValidationResultExtensions.cs deleted file mode 100644 index 84920c057..000000000 --- a/src/core/Synapse.Application/Extensions/IWorkflowValidationResultExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Services.Validation; - -namespace Synapse -{ - - ///

- /// Defines extensions for - /// - public static class IWorkflowValidationResultExtensions - { - - /// - /// Converts the into a new of s - /// - /// The to convert - /// A new of s - public static IEnumerable AsErrors(this IWorkflowValidationResult validationResult) - { - List errors = new(validationResult.SchemaValidationErrors.Count() + validationResult.DslValidationErrors.Count()); - errors.AddRange(validationResult.SchemaValidationErrors.Select(e => new Error(e.Path, e.Message))); - errors.AddRange(validationResult.DslValidationErrors.Select(e => new Error(e.ErrorCode, e.ErrorMessage))); - return errors; - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/SwitchStateExtensions.cs b/src/core/Synapse.Application/Extensions/SwitchStateExtensions.cs deleted file mode 100644 index 1d3b8d8b1..000000000 --- a/src/core/Synapse.Application/Extensions/SwitchStateExtensions.cs +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse -{ - - ///

- /// Defines extensions for s - /// - public static class SwitchStateExtensions - { - - /// - /// Gets the with the specified name - /// - /// The to search for the with the specified name - /// The name of the to get - /// The with the specified name - public static SwitchCaseDefinition? GetCase(this SwitchStateDefinition state, string caseName) - { - SwitchCaseDefinition @case; - switch (state.SwitchType) - { - case SwitchStateType.Data: - if (caseName == "default") - @case = new DataCaseDefinition() { Name = "default", Transition = state.DefaultCondition.Transition, End = state.DefaultCondition.End }; - else - @case = state.DataConditions?.Single(c => c.Name == caseName)!; - break; - case SwitchStateType.Event: - if (caseName == "default") - @case = new EventCaseDefinition() { Name = "default", Transition = state.DefaultCondition.Transition, End = state.DefaultCondition.End }; - else - @case = state.EventConditions?.Single(c => c.Name == caseName)!; - break; - default: - throw new NotSupportedException($"The specified switch state type '{state.SwitchType}' is not supported in this context"); - } - return @case; - } - - /// - /// Attempts to get the with the specified name - /// - /// The to search for the with the specified name - /// The name of the to get - /// The with the specified name - /// A boolean indicating whether or not the with the specified name could be found - public static bool TryGetCase(this SwitchStateDefinition state, string caseName, out SwitchCaseDefinition @case) - { - @case = null!; - try - { - @case = state.GetCase(caseName)!; - } - catch - { - return false; - } - return @case != null; - } - - /// - /// Gets the that applies to the specified event - /// - /// The to search for the that applies to the specified event - /// The name of the event the to get applies to - /// The that applies to the specified event - public static EventCaseDefinition? GetEventCondition(this SwitchStateDefinition state, string eventReference) - { - return state.EventConditions?.FirstOrDefault(c => c.Event == eventReference); - } - - /// - /// Attempts to get the that applies to the specified event - /// - /// The to search for the specified - /// The reference of the event the to get applies to - /// The that applies to the specified event - /// A boolean indicating whether or not a with the specified id could be found - public static bool TryGetEventCondition(this SwitchStateDefinition state, string eventReference, out EventCaseDefinition @case) - { - @case = null!; - try - { - @case = state.GetEventCondition(eventReference)!; - } - catch - { - return false; - } - return @case != null; - } - - } - -} diff --git a/src/core/Synapse.Application/Extensions/V1EventExtensions.cs b/src/core/Synapse.Application/Extensions/V1EventExtensions.cs deleted file mode 100644 index a60e5c7c0..000000000 --- a/src/core/Synapse.Application/Extensions/V1EventExtensions.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class V1EventExtensions - { - - /// - /// Converts the into a new - /// - /// The to convert - /// A new - public static CloudEvent AsCloudEvent(this V1Event e) - { - var specVersion = CloudEventsSpecVersion.FromVersionId(e.SpecVersion); - if (specVersion == null) specVersion = CloudEventsSpecVersion.Default; - var ce = new CloudEvent(specVersion); - ce.Id = e.Id; - ce.Time = e.Time; - ce.Source = e.Source; - ce.Type = e.Type; - ce.Subject = e.Subject; - ce.DataContentType = e.DataContentType; - ce.DataSchema = e.DataSchema; - ce.Data = e.Data; - if(e.ExtensionAttributes != null) - { - foreach (var extension in e.ExtensionAttributes) - { - ce.SetAttributeFromString(extension.Key, extension.Value?.ToString()!); - } - } - return ce; - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/AnyMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/AnyMappingConfiguration.cs deleted file mode 100644 index 487d670c1..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/AnyMappingConfiguration.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; -using Neuroglia.Serialization; -using Newtonsoft.Json.Linq; - -namespace Synapse.Application.Mapping.Configuration -{ - internal class AnyMappingConfiguration - : IMappingConfiguration - { - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ConvertUsing((source, destination) => - { - if (source == null) - return null!; - if (source is JObject jobj) - return DynamicObject.FromObject(jobj.ToObject())!; - if (source is Dynamic dyn) - return dyn; - return Dynamic.FromObject(source)!; - }); - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/DateTimeMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/DateTimeMappingConfiguration.cs deleted file mode 100644 index 1740bf231..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/DateTimeMappingConfiguration.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; - -namespace Synapse.Application.Mapping.Configuration -{ - internal class DateTimeMappingConfiguration - : IMappingConfiguration, - IMappingConfiguration - { - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ConvertUsing((source, target) => new(source)); - } - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ConvertUsing((source, target) => source.ToUniversalTime().DateTime); - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/ErrorMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/ErrorMappingConfiguration.cs deleted file mode 100644 index a1caab26a..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/ErrorMappingConfiguration.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; -using Synapse.Integration.Models; - -namespace Synapse.Application.Mapping.Configuration -{ - internal class ErrorMappingConfiguration - : IMappingConfiguration, - IMappingConfiguration - { - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - - } - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/ExpandoObjectMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/ExpandoObjectMappingConfiguration.cs deleted file mode 100644 index 2017f2528..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/ExpandoObjectMappingConfiguration.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; -using System.Dynamic; - -namespace Synapse.Application.Mapping.Configuration -{ - - internal class ExpandoObjectMappingConfiguration - : IMappingConfiguration - { - - public void Configure(IMappingExpression mapping) - { - mapping.ConvertUsing((source, destination) => (source == null ? null : new(source!))!); - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/V1CorrelationConditionMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/V1CorrelationConditionMappingConfiguration.cs deleted file mode 100644 index 0e1fd1f79..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/V1CorrelationConditionMappingConfiguration.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; - -namespace Synapse.Application.Mapping.Configuration -{ - internal class V1CorrelationConditionMappingConfiguration - : IMappingConfiguration - { - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - - } - - } -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/V1CorrelationOutcomeMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/V1CorrelationOutcomeMappingConfiguration.cs deleted file mode 100644 index 0fdb138e8..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/V1CorrelationOutcomeMappingConfiguration.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; - -namespace Synapse.Application.Mapping.Configuration -{ - internal class V1CorrelationOutcomeMappingConfiguration - : IMappingConfiguration - { - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/V1EventFilterMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/V1EventFilterMappingConfiguration.cs deleted file mode 100644 index e2a5a3576..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/V1EventFilterMappingConfiguration.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; - -namespace Synapse.Application.Mapping.Configuration -{ - internal class V1EventFilterMappingConfiguration - : IMappingConfiguration - { - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/Configuration/V1EventMappingConfiguration.cs b/src/core/Synapse.Application/Mapping/Configuration/V1EventMappingConfiguration.cs deleted file mode 100644 index be1f69dc5..000000000 --- a/src/core/Synapse.Application/Mapping/Configuration/V1EventMappingConfiguration.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; - -namespace Synapse.Application.Mapping.Configuration -{ - - internal class V1EventMappingConfiguration - : IMappingConfiguration, - IMappingConfiguration, - IMappingConfiguration - { - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(e => e.SpecVersion, options => options.MapFrom(ce => ce.SpecVersion.VersionId)); - } - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(e => e.Attributes, options => options.Ignore()); - mapping.ForMember(e => e.ContextAttributes, options => options.Ignore()); - } - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(e => e.Attributes, options => options.Ignore()); - } - - } -} diff --git a/src/core/Synapse.Application/Mapping/Converters/JsonPatchDocumentConverter.cs b/src/core/Synapse.Application/Mapping/Converters/JsonPatchDocumentConverter.cs deleted file mode 100644 index 021d18c43..000000000 --- a/src/core/Synapse.Application/Mapping/Converters/JsonPatchDocumentConverter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using AutoMapper; -using Microsoft.AspNetCore.JsonPatch; -using Newtonsoft.Json.Linq; - -namespace Synapse.Application.Mapping.Converters -{ - - internal class JsonPatchDocumentConverter - : ITypeConverter> - where T : class - { - - JsonPatchDocument ITypeConverter>.Convert(JsonPatchDocument source, JsonPatchDocument destination, ResolutionContext context) - { - try - { - return JToken.FromObject(source)!.ToObject>()!; - } - catch - { - throw; - } - - } - - } - -} diff --git a/src/core/Synapse.Application/Mapping/MappingProfile.cs b/src/core/Synapse.Application/Mapping/MappingProfile.cs deleted file mode 100644 index 0c0339303..000000000 --- a/src/core/Synapse.Application/Mapping/MappingProfile.cs +++ /dev/null @@ -1,125 +0,0 @@ -using AutoMapper; -using Microsoft.AspNetCore.JsonPatch; -using Synapse.Application.Commands.Generic; -using Synapse.Application.Mapping.Converters; -using System.Reflection; - -namespace Synapse.Application.Mapping -{ - - ///

- /// Represents the application mapping - /// - public class MappingProfile - : Profile - { - - /// - /// Initializes a new - /// - public MappingProfile() - { - this.AllowNullCollections = true; - this.MappingConfigurationTypes = new List(); - this.ApplyMappingConfigurations(); - this.ConfigureEntities(); - this.ConfigureCommands(); - this.ConfigureQueries(); - this.ConfigureEvents(); - this.CreateMap(typeof(JsonPatchDocument), typeof(JsonPatchDocument<>)) - .ConvertUsing(typeof(JsonPatchDocumentConverter<>)); - } - - /// - /// Gets a containing the types of all existing s - /// - protected List MappingConfigurationTypes { get; } - - /// - /// Initializes the - /// - protected void ApplyMappingConfigurations() - { - foreach (Type mappingConfigurationType in this.GetType().Assembly - .GetTypes() - .Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass && typeof(IMappingConfiguration).IsAssignableFrom(t))) - { - this.MappingConfigurationTypes.Add(mappingConfigurationType); - this.ApplyConfiguration((IMappingConfiguration)Activator.CreateInstance(mappingConfigurationType, new object[] { })!); - } - } - - /// - /// Configures entity mappings - /// - protected void ConfigureEntities() - { - foreach (Type entityType in new Assembly[] { typeof(V1Workflow).Assembly } - .SelectMany(a => a.GetTypes()) - .Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass)) - { - DataTransferObjectTypeAttribute? dtoTypeAttribute = entityType.GetCustomAttribute(); - if (dtoTypeAttribute != null && !this.MappingConfigurationTypes.Any(t => typeof(IMappingConfiguration<,>).MakeGenericType(entityType, dtoTypeAttribute.Type).IsAssignableFrom(t))) - this.CreateMap(entityType, dtoTypeAttribute.Type); - } - } - - /// - /// Configures command mappings - /// - protected void ConfigureCommands() - { - foreach (var commandType in typeof(MappingProfile).Assembly - .GetTypes() - .Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass && typeof(ICommand).IsAssignableFrom(t))) - { - var dtoTypeAttribute = commandType.GetCustomAttribute(); - if (dtoTypeAttribute != null && !this.MappingConfigurationTypes.Any(t => typeof(IMappingConfiguration<,>).MakeGenericType(dtoTypeAttribute.Type, commandType).IsAssignableFrom(t))) - this.CreateMap(dtoTypeAttribute.Type, commandType); - } - foreach (var patchableAggregateType in typeof(V1Workflow).Assembly - .GetTypes() - .Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass && typeof(IAggregateRoot).IsAssignableFrom(t) && t.TryGetCustomAttribute(out _))) - { - var keyType = patchableAggregateType.GetGenericType(typeof(IIdentifiable<>)).GetGenericArguments()[0]; - var dtoType = patchableAggregateType.GetCustomAttribute()!.Type; - var patchCommandType = typeof(V1PatchCommand<,,>).MakeGenericType(patchableAggregateType, dtoType, keyType); - this.CreateMap(typeof(Integration.Commands.Generic.V1PatchCommand), patchCommandType); - } - } - - /// - /// Configures query mappings - /// - protected void ConfigureQueries() - { - foreach (Type queryType in typeof(MappingProfile).Assembly - .GetTypes() - .Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass && typeof(IQuery).IsAssignableFrom(t))) - { - DataTransferObjectTypeAttribute? dtoTypeAttribute = queryType.GetCustomAttribute(); - if (dtoTypeAttribute != null && !this.MappingConfigurationTypes.Any(t => typeof(IMappingConfiguration<,>).MakeGenericType(dtoTypeAttribute.Type, queryType).IsAssignableFrom(t))) - this.CreateMap(dtoTypeAttribute.Type, queryType); - } - } - - /// - /// Configures event mappings - /// - protected void ConfigureEvents() - { - foreach (Type eventType in typeof(MappingProfile).Assembly - .GetTypes() - .Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass && typeof(IDomainEvent).IsAssignableFrom(t))) - { - DataTransferObjectTypeAttribute? dtoTypeAttribute = eventType.GetCustomAttribute(); - if (dtoTypeAttribute != null && !this.MappingConfigurationTypes.Any(t => typeof(IMappingConfiguration<,>).MakeGenericType(eventType, dtoTypeAttribute.Type).IsAssignableFrom(t))) - this.CreateMap(eventType, dtoTypeAttribute.Type); - } - this.CreateMap(typeof(IDomainEvent), typeof(IIntegrationEvent)) - .IncludeAllDerived(); - } - - } - -} diff --git a/src/core/Synapse.Application/Properties/GlobalUsings.cs b/src/core/Synapse.Application/Properties/GlobalUsings.cs deleted file mode 100644 index 168a58923..000000000 --- a/src/core/Synapse.Application/Properties/GlobalUsings.cs +++ /dev/null @@ -1,12 +0,0 @@ -global using CloudNative.CloudEvents; -global using Microsoft.Extensions.Logging; -global using Microsoft.Extensions.Options; -global using Neuroglia; -global using Neuroglia.Data; -global using Neuroglia.Eventing; -global using Neuroglia.Mapping; -global using Neuroglia.Mediation; -global using Synapse.Application.Configuration; -global using Synapse.Application.Services; -global using Synapse.Infrastructure.Services; -global using Synapse.Domain.Models; \ No newline at end of file diff --git a/src/core/Synapse.Application/Properties/launchSettings.json b/src/core/Synapse.Application/Properties/launchSettings.json deleted file mode 100644 index d7a6d8e4f..000000000 --- a/src/core/Synapse.Application/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "profiles": { - "Synapse.Application": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:62012;http://localhost:62013" - } - } -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Queries/Application/V1GetApplicationInfoQuery.cs b/src/core/Synapse.Application/Queries/Application/V1GetApplicationInfoQuery.cs deleted file mode 100644 index eae1a1e63..000000000 --- a/src/core/Synapse.Application/Queries/Application/V1GetApplicationInfoQuery.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Expressions; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Infrastructure.Plugins; -using Synapse.Integration.Models; -using System.Collections; -using System.Runtime.InteropServices; - -namespace Synapse.Application.Queries.Application -{ - - ///

- /// Represents the used to retrieve information about the running Synapse instance - /// - public class V1GetApplicationInfoQuery - : Query - { - - /// - /// Initializes a new - /// - public V1GetApplicationInfoQuery() - { - - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetApplicationInfoQueryHandler - : QueryHandlerBase, - IQueryHandler - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The current - /// The service used to manage s - /// An containing all registered s - public V1GetApplicationInfoQueryHandler(IHostEnvironment environment, ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IWorkflowRuntime workflowRuntime, IPluginManager pluginManager, IEnumerable expressionEvaluators) - : base(loggerFactory, mediator, mapper) - { - this.Environment = environment; - this.WorkflowRuntime = workflowRuntime; - this.PluginManager = pluginManager; - this.ExpressionEvaluators = expressionEvaluators; - } - - /// - /// Gets the current - /// - protected IHostEnvironment Environment { get; } - - /// - /// Gets the service used to manage s - /// - protected IPluginManager PluginManager { get; } - - /// - /// Gets the current - /// - protected IWorkflowRuntime WorkflowRuntime { get; } - - /// - /// Gets an containing all registered s - /// - protected IEnumerable ExpressionEvaluators { get; } - - /// - public virtual async Task> HandleAsync(V1GetApplicationInfoQuery query, CancellationToken cancellationToken = default) - { - var name = this.Environment.ApplicationName; - var version = typeof(V1GetApplicationInfoQuery).Assembly.GetName().Version!.ToString(); - var osDescription = RuntimeInformation.OSDescription; - var frameworkDescription = RuntimeInformation.FrameworkDescription; - var serverlessWorkflowSdkVersion = typeof(WorkflowDefinition).Assembly.GetName().Version!.ToString(); - var environmentName = this.Environment.EnvironmentName; - var workflowRuntimeName = this.WorkflowRuntime.GetType().Name.Replace("Runtime", string.Empty); - var supportedRuntimeExpressionLanguages = new[] { "jq" }; //todo: need to provide a list based on IExpressionEvaluators. The latter interface should be edited to return an array of supported languages - var environmentVariables = System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process) - .OfType() - .ToDictionary(kvp => kvp.Key.ToString()!, kvp => kvp.Value!.ToString()!); - var plugins = this.PluginManager.Plugins.Select(p => new V1PluginInfo(new FileInfo(p.MetadataFilePath).Directory!.FullName, p.Metadata, p.IsLoaded)); - var info = new V1ApplicationInfo(name, version, osDescription, frameworkDescription, serverlessWorkflowSdkVersion, environmentName, - workflowRuntimeName, supportedRuntimeExpressionLanguages, environmentVariables, plugins); - return await Task.FromResult(this.Ok(info)); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/AuthenticationDefinitionCollections/V1GetAuthenticationDefinitionCollectionByIdQuery.cs b/src/core/Synapse.Application/Queries/AuthenticationDefinitionCollections/V1GetAuthenticationDefinitionCollectionByIdQuery.cs deleted file mode 100644 index 0af21effa..000000000 --- a/src/core/Synapse.Application/Queries/AuthenticationDefinitionCollections/V1GetAuthenticationDefinitionCollectionByIdQuery.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; - -namespace Synapse.Application.Queries.AuthenticationDefinitionCollections -{ - - ///

- /// Represents the used to get a by id - /// - public class V1GetAuthenticationDefinitionCollectionByIdQuery - : Query - { - - /// - /// Initializes a new - /// - protected V1GetAuthenticationDefinitionCollectionByIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - /// The version of the to get. Defaults to 'lastest' - public V1GetAuthenticationDefinitionCollectionByIdQuery(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to get. Defaults to 'latest' - /// - public virtual string? Version { get; protected set; } = null!; - - /// - /// Parses the specified reference into a new - /// - /// The reference to parse - /// A new - public static V1GetAuthenticationDefinitionCollectionByIdQuery Parse(string reference) - { - if (string.IsNullOrEmpty(reference)) - throw new ArgumentNullException(nameof(reference)); - var components = reference.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - string? version = null; - if (components.Length == 2) - version = components.Last(); - return new(id, version); - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetAuthenticationDefinitionCollectionByIdQueryHandler - : QueryHandlerBase, - IQueryHandler - { - - /// - public V1GetAuthenticationDefinitionCollectionByIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task> HandleAsync(V1GetAuthenticationDefinitionCollectionByIdQuery query, CancellationToken cancellationToken = default) - { - Integration.Models.V1AuthenticationDefinitionCollection? collection; - if (string.IsNullOrWhiteSpace(query.Version) - || query.Version == "latest") - { - collection = this.Repository.AsQueryable() - .Where(fc => fc.Name!.Equals(query.Id, StringComparison.OrdinalIgnoreCase)) - .ToList() - .OrderByDescending(fc => SemVersion.Parse(fc.Version, SemVersionStyles.Any)) - .FirstOrDefault()!; - } - else - { - collection = await this.Repository.FindAsync($"{query.Id}:{query.Version}", cancellationToken); - } - if (collection == null) - throw DomainException.NullReference(typeof(V1AuthenticationDefinitionCollection), query.Id); - return this.Ok(collection); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/AuthenticationDefinitionCollections/V1GetRawAuthenticationDefinitionCollectionByIdQuery.cs b/src/core/Synapse.Application/Queries/AuthenticationDefinitionCollections/V1GetRawAuthenticationDefinitionCollectionByIdQuery.cs deleted file mode 100644 index c564cd665..000000000 --- a/src/core/Synapse.Application/Queries/AuthenticationDefinitionCollections/V1GetRawAuthenticationDefinitionCollectionByIdQuery.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Application.Queries.AuthenticationDefinitionCollections -{ - ///

- /// Represents the used to get the raw contents of a by id - /// - public class V1GetRawAuthenticationDefinitionCollectionByIdQuery - : Query> - { - - /// - /// Initializes a new - /// - protected V1GetRawAuthenticationDefinitionCollectionByIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - /// The version of the to get. Defaults to 'lastest' - public V1GetRawAuthenticationDefinitionCollectionByIdQuery(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to get. Defaults to 'latest' - /// - public virtual string? Version { get; protected set; } = null!; - - /// - /// Parses the specified reference into a new - /// - /// The reference to parse - /// A new - public static V1GetRawAuthenticationDefinitionCollectionByIdQuery Parse(string reference) - { - if (string.IsNullOrEmpty(reference)) - throw new ArgumentNullException(nameof(reference)); - var components = reference.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - string? version = null; - if (components.Length == 2) - version = components.Last(); - return new(id, version); - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetRawAuthenticationDefinitionCollectionByIdQueryHandler - : QueryHandlerBase, - IQueryHandler> - { - - /// - public V1GetRawAuthenticationDefinitionCollectionByIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task>> HandleAsync(V1GetRawAuthenticationDefinitionCollectionByIdQuery query, CancellationToken cancellationToken = default) - { - var collection = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetAuthenticationDefinitionCollectionByIdQuery(query.Id, query.Version), cancellationToken); - if (collection == null) - throw DomainException.NullReference(typeof(V1AuthenticationDefinitionCollection), query.Id); - return this.Ok(collection.Authentications.ToList()); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/Correlations/V1GetEventCorrelationsQuery.cs b/src/core/Synapse.Application/Queries/Correlations/V1GetEventCorrelationsQuery.cs deleted file mode 100644 index df8d737b8..000000000 --- a/src/core/Synapse.Application/Queries/Correlations/V1GetEventCorrelationsQuery.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Queries.Correlations -{ - - ///

- /// Represents the used to get the s matching a given - /// - public class V1GetEventCorrelationsQuery - : Query> - { - - /// - /// Initializes a new - /// - protected V1GetEventCorrelationsQuery() - { - this.Event = null!; - } - - /// - /// Initializes a new - /// - /// The to get the matching s for - public V1GetEventCorrelationsQuery(V1Event e) - { - this.Event = e; - } - - /// - /// Gets the to get the matching s for - /// - public virtual V1Event Event { get; protected set; } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetEventCorrelationsQueryHandler - : QueryHandlerBase, - IQueryHandler> - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage s - /// The used to manage s - - public V1GetEventCorrelationsQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, - IRepository correlationWriteModels, IRepository correlationReadModels) - : base(loggerFactory, mediator, mapper) - { - this.CorrelationWriteModels = correlationWriteModels; - this.CorrelationReadModels = correlationReadModels; - } - - /// - /// Gets the used to manage s - /// - protected IRepository CorrelationWriteModels { get; } - - /// - /// Gets the used to manage s - /// - protected IRepository CorrelationReadModels { get; } - - /// - public virtual async Task>> HandleAsync(V1GetEventCorrelationsQuery query, CancellationToken cancellationToken = default) - { - IEnumerable correlations = await this.CorrelationReadModels.ToListAsync(cancellationToken); - var totalTriggerCount = correlations.Count(); - var e = this.Mapper.Map(query.Event); - correlations = correlations.Where(c => c.AppliesTo(e)); - return this.Ok(this.GetCorrelationsAsync(correlations.Select(c => c.Id).ToList())); - } - - /// - /// Gets all matching s - /// - /// A containing the ids of the s to list - /// A new awaitable - protected virtual async IAsyncEnumerable GetCorrelationsAsync(List correlationIds) - { - foreach (var correlationId in correlationIds) - { - yield return await this.CorrelationWriteModels.FindAsync(correlationId); - } - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/EventDefinitionCollections/V1GetEventDefinitionCollectionByIdQuery.cs b/src/core/Synapse.Application/Queries/EventDefinitionCollections/V1GetEventDefinitionCollectionByIdQuery.cs deleted file mode 100644 index ec6dfd724..000000000 --- a/src/core/Synapse.Application/Queries/EventDefinitionCollections/V1GetEventDefinitionCollectionByIdQuery.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; - -namespace Synapse.Application.Queries.EventDefinitionCollections -{ - - ///

- /// Represents the used to get a by id - /// - public class V1GetEventDefinitionCollectionByIdQuery - : Query - { - - /// - /// Initializes a new - /// - protected V1GetEventDefinitionCollectionByIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - /// The version of the to get. Defaults to 'lastest' - public V1GetEventDefinitionCollectionByIdQuery(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to get. Defaults to 'latest' - /// - public virtual string? Version { get; protected set; } = null!; - - /// - /// Parses the specified reference into a new - /// - /// The reference to parse - /// A new - public static V1GetEventDefinitionCollectionByIdQuery Parse(string reference) - { - if (string.IsNullOrEmpty(reference)) - throw new ArgumentNullException(nameof(reference)); - var components = reference.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - string? version = null; - if (components.Length == 2) - version = components.Last(); - return new(id, version); - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetEventDefinitionCollectionByIdQueryHandler - : QueryHandlerBase, - IQueryHandler - { - - /// - public V1GetEventDefinitionCollectionByIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task> HandleAsync(V1GetEventDefinitionCollectionByIdQuery query, CancellationToken cancellationToken = default) - { - Integration.Models.V1EventDefinitionCollection? collection; - if (string.IsNullOrWhiteSpace(query.Version) - || query.Version == "latest") - { - collection = this.Repository.AsQueryable() - .Where(fc => fc.Name!.Equals(query.Id, StringComparison.OrdinalIgnoreCase)) - .ToList() - .OrderByDescending(fc => SemVersion.Parse(fc.Version, SemVersionStyles.Any)) - .FirstOrDefault()!; - } - else - { - collection = await this.Repository.FindAsync($"{query.Id}:{query.Version}", cancellationToken); - } - if (collection == null) - throw DomainException.NullReference(typeof(V1EventDefinitionCollection), query.Id); - return this.Ok(collection); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/EventDefinitionCollections/V1GetRawEventDefinitionCollectionByIdQuery.cs b/src/core/Synapse.Application/Queries/EventDefinitionCollections/V1GetRawEventDefinitionCollectionByIdQuery.cs deleted file mode 100644 index db469a6b7..000000000 --- a/src/core/Synapse.Application/Queries/EventDefinitionCollections/V1GetRawEventDefinitionCollectionByIdQuery.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Application.Queries.EventDefinitionCollections -{ - ///

- /// Represents the used to get the raw contents of a by id - /// - public class V1GetRawEventDefinitionCollectionByIdQuery - : Query> - { - - /// - /// Initializes a new - /// - protected V1GetRawEventDefinitionCollectionByIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - /// The version of the to get. Defaults to 'lastest' - public V1GetRawEventDefinitionCollectionByIdQuery(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to get. Defaults to 'latest' - /// - public virtual string? Version { get; protected set; } = null!; - - /// - /// Parses the specified reference into a new - /// - /// The reference to parse - /// A new - public static V1GetRawEventDefinitionCollectionByIdQuery Parse(string reference) - { - if (string.IsNullOrEmpty(reference)) - throw new ArgumentNullException(nameof(reference)); - var components = reference.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - string? version = null; - if (components.Length == 2) - version = components.Last(); - return new(id, version); - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetRawEventDefinitionCollectionByIdQueryHandler - : QueryHandlerBase, - IQueryHandler> - { - - /// - public V1GetRawEventDefinitionCollectionByIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task>> HandleAsync(V1GetRawEventDefinitionCollectionByIdQuery query, CancellationToken cancellationToken = default) - { - var collection = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetEventDefinitionCollectionByIdQuery(query.Id, query.Version), cancellationToken); - if (collection == null) - throw DomainException.NullReference(typeof(V1EventDefinitionCollection), query.Id); - return this.Ok(collection.Events.ToList()); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/FunctionDefinitionCollections/V1GetFunctionDefinitionCollectionByIdQuery.cs b/src/core/Synapse.Application/Queries/FunctionDefinitionCollections/V1GetFunctionDefinitionCollectionByIdQuery.cs deleted file mode 100644 index 03bc915ab..000000000 --- a/src/core/Synapse.Application/Queries/FunctionDefinitionCollections/V1GetFunctionDefinitionCollectionByIdQuery.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; - -namespace Synapse.Application.Queries.FunctionDefinitionCollections -{ - - ///

- /// Represents the used to get a by id - /// - public class V1GetFunctionDefinitionCollectionByIdQuery - : Query - { - - /// - /// Initializes a new - /// - protected V1GetFunctionDefinitionCollectionByIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - /// The version of the to get. Defaults to 'lastest' - public V1GetFunctionDefinitionCollectionByIdQuery(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to get. Defaults to 'latest' - /// - public virtual string? Version { get; protected set; } = null!; - - /// - /// Parses the specified reference into a new - /// - /// The reference to parse - /// A new - public static V1GetFunctionDefinitionCollectionByIdQuery Parse(string reference) - { - if (string.IsNullOrEmpty(reference)) - throw new ArgumentNullException(nameof(reference)); - var components = reference.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - string? version = null; - if (components.Length == 2) - version = components.Last(); - return new(id, version); - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetFunctionDefinitionCollectionByIdQueryHandler - : QueryHandlerBase, - IQueryHandler - { - - /// - public V1GetFunctionDefinitionCollectionByIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task> HandleAsync(V1GetFunctionDefinitionCollectionByIdQuery query, CancellationToken cancellationToken = default) - { - Integration.Models.V1FunctionDefinitionCollection? collection; - if (string.IsNullOrWhiteSpace(query.Version) - || query.Version == "latest") - { - collection = this.Repository.AsQueryable() - .Where(fc => fc.Name!.Equals(query.Id, StringComparison.OrdinalIgnoreCase)) - .ToList() - .OrderByDescending(fc => SemVersion.Parse(fc.Version, SemVersionStyles.Any)) - .FirstOrDefault()!; - } - else - { - collection = await this.Repository.FindAsync($"{query.Id}:{query.Version}", cancellationToken); - } - if (collection == null) - throw DomainException.NullReference(typeof(V1FunctionDefinitionCollection), query.Id); - return this.Ok(collection); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/FunctionDefinitionCollections/V1GetRawFunctionDefinitionCollectionByIdQuery.cs b/src/core/Synapse.Application/Queries/FunctionDefinitionCollections/V1GetRawFunctionDefinitionCollectionByIdQuery.cs deleted file mode 100644 index 1e9937e90..000000000 --- a/src/core/Synapse.Application/Queries/FunctionDefinitionCollections/V1GetRawFunctionDefinitionCollectionByIdQuery.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Application.Queries.FunctionDefinitionCollections -{ - ///

- /// Represents the used to get the raw contents of a by id - /// - public class V1GetRawFunctionDefinitionCollectionByIdQuery - : Query> - { - - /// - /// Initializes a new - /// - protected V1GetRawFunctionDefinitionCollectionByIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - /// The version of the to get. Defaults to 'lastest' - public V1GetRawFunctionDefinitionCollectionByIdQuery(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to get. Defaults to 'latest' - /// - public virtual string? Version { get; protected set; } = null!; - - /// - /// Parses the specified reference into a new - /// - /// The reference to parse - /// A new - public static V1GetRawFunctionDefinitionCollectionByIdQuery Parse(string reference) - { - if (string.IsNullOrEmpty(reference)) - throw new ArgumentNullException(nameof(reference)); - var components = reference.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - string? version = null; - if (components.Length == 2) - version = components.Last(); - return new(id, version); - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetRawFunctionDefinitionCollectionByIdQueryHandler - : QueryHandlerBase, - IQueryHandler> - { - - /// - public V1GetRawFunctionDefinitionCollectionByIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task>> HandleAsync(V1GetRawFunctionDefinitionCollectionByIdQuery query, CancellationToken cancellationToken = default) - { - var collection = await this.Mediator.ExecuteAndUnwrapAsync(new V1GetFunctionDefinitionCollectionByIdQuery(query.Id, query.Version), cancellationToken); - if (collection == null) - throw DomainException.NullReference(typeof(V1FunctionDefinitionCollection), query.Id); - return this.Ok(collection.Functions.ToList()); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/Generic/V1FilterQuery.cs b/src/core/Synapse.Application/Queries/Generic/V1FilterQuery.cs deleted file mode 100644 index 5f4d530d5..000000000 --- a/src/core/Synapse.Application/Queries/Generic/V1FilterQuery.cs +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.OData.Query; -using Microsoft.AspNetCore.OData.Query.Expressions; -using Microsoft.OData.Edm; -using System.Linq.Expressions; - -namespace Synapse.Application.Queries.Generic -{ - - ///

- /// Represents the used to filter the entities of the specified type - /// - /// The type of The type of entities to query - public class V1FilterQuery - : Query> - where TEntity : class, IIdentifiable - { - - /// - /// Initializes a new - /// - protected V1FilterQuery() - { - this.Options = null!; - } - - /// - /// Initializes a new - /// - /// The used to filter the entities - public V1FilterQuery(ODataQueryOptions options) - { - this.Options = options; - } - - /// - /// Gets the used to filter the entities - /// - public ODataQueryOptions Options { get; protected set; } - - } - - /// - /// Represents the service used to handle instances - /// - /// The type of entity to filter - public class V1FilterQueryHandler - : QueryHandlerBase, - IQueryHandler, List> - where TEntity : class, IIdentifiable - { - - /// - public V1FilterQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository, ISearchBinder searchBinder, IEdmModel edmModel) - : base(loggerFactory, mediator, mapper, repository) - { - this.SearchBinder = searchBinder; - this.EdmModel = edmModel; - } - - /// - /// Gets the service used to bind ODATA searches - /// - protected ISearchBinder SearchBinder { get; } - - /// - /// Gets the current - /// - protected IEdmModel EdmModel { get; } - - /// - public virtual async Task>> HandleAsync(V1FilterQuery query, CancellationToken cancellationToken = default) - { - var toFilter = (await this.Repository.ToListAsync(cancellationToken)).AsQueryable(); - if (query.Options?.Search != null) - { - var searchExpression = (Expression>)this.SearchBinder.BindSearch(query.Options.Search.SearchClause, new(this.EdmModel, new(), typeof(TEntity))); - toFilter = toFilter.Where(searchExpression); - } - var filtered = query.Options?.ApplyTo(toFilter); - if (filtered == null) - filtered = toFilter; - return this.Ok(filtered.OfType().ToList()); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/Generic/V1FindByIdQuery.cs b/src/core/Synapse.Application/Queries/Generic/V1FindByIdQuery.cs deleted file mode 100644 index e9b9dd198..000000000 --- a/src/core/Synapse.Application/Queries/Generic/V1FindByIdQuery.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Queries.Generic -{ - - ///

- /// Represents an used to get an entity by id - /// - /// The type of entity to query - /// The type of id used to uniquely identify the entity to get - public class V1FindByIdQuery - : Query - where TEntity : class, IIdentifiable - where TKey : IEquatable - { - - /// - /// Initializes a new - /// - protected V1FindByIdQuery() - { - this.Id = default!; - } - - /// - /// Initializes a new - /// - /// The id of the entity to find - public V1FindByIdQuery(TKey id) - { - this.Id = id; - } - - /// - /// Gets the id of the entity to find - /// - public virtual TKey Id { get; } - - } - - /// - /// Represents the service used to handle instances - /// - /// The type of entity to find - /// The type of key used to uniquely identify the entity to find - public class V1FindByKeyQueryHandler - : QueryHandlerBase, - IQueryHandler, TEntity> - where TEntity : class, IIdentifiable - where TKey : IEquatable - { - - /// - public V1FindByKeyQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - } - - /// - public virtual async Task> HandleAsync(V1FindByIdQuery query, CancellationToken cancellationToken = default) - { - var entity = await this.Repository.FindAsync(query.Id, cancellationToken); - if (entity == null) - throw DomainException.NullReference(typeof(TEntity), query.Id, nameof(query.Id)); - return this.Ok(entity); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/Generic/V1ListQuery.cs b/src/core/Synapse.Application/Queries/Generic/V1ListQuery.cs deleted file mode 100644 index 4dd7e4f0d..000000000 --- a/src/core/Synapse.Application/Queries/Generic/V1ListQuery.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Queries.Generic -{ - - ///

- /// Represents the used to get an of the entities of the specified type - /// - /// The type of The type of entities to query - public class V1ListQuery - : Query> - where TEntity : class, IIdentifiable - { - - - - } - - /// - /// Represents the service used to handle instances - /// - /// The type of entity to query - public class V1ListQueryHandler - : QueryHandlerBase, - IQueryHandler, IQueryable> - where TEntity : class, IIdentifiable - { - - /// - public V1ListQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task>> HandleAsync(V1ListQuery query, CancellationToken cancellationToken = default) - { - return await Task.FromResult(this.Ok(this.Repository.AsQueryable())); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/QueryHandlerBase.cs b/src/core/Synapse.Application/Queries/QueryHandlerBase.cs deleted file mode 100644 index 282a30dfa..000000000 --- a/src/core/Synapse.Application/Queries/QueryHandlerBase.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Queries -{ - - ///

- /// Represents the base class for all implementations - /// - public abstract class QueryHandlerBase - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - protected QueryHandlerBase(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Mediator = mediator; - this.Mapper = mapper; - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - } - - /// - /// Represents the base class for all implementations - /// - /// The type of entity to query - public abstract class QueryHandlerBase - : QueryHandlerBase - where TEntity : class, IIdentifiable - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage the entities to query - protected QueryHandlerBase(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper) - { - this.Repository = repository; - } - - /// - /// Gets the used to manage the entities to query - /// - protected IRepository Repository { get; } - - } - - /// - /// Represents the base class for all implementations - /// - /// The type of entity to query - /// The type of key used to uniquely identify the entities to query - public abstract class QueryHandlerBase - : QueryHandlerBase - where TEntity : class, IIdentifiable - where TKey : IEquatable - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage the entities to query - protected QueryHandlerBase(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - /// Gets the used to manage the entities to query - /// - protected new IRepository Repository => (IRepository)base.Repository; - - } - -} diff --git a/src/core/Synapse.Application/Queries/WorkflowActivities/V1GetActivityParentStateDataQuery.cs b/src/core/Synapse.Application/Queries/WorkflowActivities/V1GetActivityParentStateDataQuery.cs deleted file mode 100644 index 82320c914..000000000 --- a/src/core/Synapse.Application/Queries/WorkflowActivities/V1GetActivityParentStateDataQuery.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Serialization; - -namespace Synapse.Application.Queries.WorkflowActivities -{ - - ///

- /// Represents the used to get the data of a 's parent state - /// - [DataTransferObjectType(typeof(Integration.Queries.WorkflowActivities.V1GetActivityParentStateDataQuery))] - public class V1GetActivityParentStateDataQuery - : Query - { - - /// - /// Initializes a new - /// - protected V1GetActivityParentStateDataQuery() - { - this.ActivityId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the activity to get the parent state data of belongs to - public V1GetActivityParentStateDataQuery(string activityId) - { - this.ActivityId = activityId; - } - - /// - /// Gets the id of the activity to get the parent state data of belongs to - /// - public virtual string ActivityId { get; protected set; } - - } - - /// - /// Represents the service used to handle - /// - public class V1GetActivityParentStateDataQueryHandler - : QueryHandlerBase, - IQueryHandler - { - - /// - public V1GetActivityParentStateDataQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task> HandleAsync(V1GetActivityParentStateDataQuery query, CancellationToken cancellationToken = default) - { - var activity = await this.Repository.FindAsync(query.ActivityId, cancellationToken); - if (activity == null) - throw DomainException.NullReference(typeof(V1WorkflowActivity), query.ActivityId); - while (activity != null) - { - if (activity.Type == V1WorkflowActivityType.State) - return this.Ok(activity.Input); - activity = await this.Repository.FindAsync(activity.ParentId, cancellationToken); - } - return this.Ok(); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/WorkflowActivities/V1GetWorkflowActivitiesQuery.cs b/src/core/Synapse.Application/Queries/WorkflowActivities/V1GetWorkflowActivitiesQuery.cs deleted file mode 100644 index 67cf3548a..000000000 --- a/src/core/Synapse.Application/Queries/WorkflowActivities/V1GetWorkflowActivitiesQuery.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.OData.Query; -using System.Collections; - -namespace Synapse.Application.Queries.WorkflowActivities -{ - - ///

- /// Represents the used to get the instances that belong to a specific - /// - [DataTransferObjectType(typeof(Integration.Queries.WorkflowActivities.V1GetWorkflowActivitiesQuery))] - public class V1GetWorkflowActivitiesQuery - : Query> - { - - /// - /// Initializes a new - /// - protected V1GetWorkflowActivitiesQuery() - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the to get the instances of - /// A boolean indicating whether or not to include non-operative activities - /// The id of the to get the child activities of - /// An object used to configure the to execute - public V1GetWorkflowActivitiesQuery(string workflowInstanceId, bool includeNonOperative = false, string? parentId = null, ODataQueryOptions? options = null) - { - this.WorkflowInstanceId = workflowInstanceId; - this.IncludeNonOperative = includeNonOperative; - this.ParentId = parentId; - this.Options = options; - } - - /// - /// Gets the id of the to get the instances of - /// - public virtual string WorkflowInstanceId { get; protected set; } - - /// - /// Gets a boolean indicating whether or not to include non-operative activities - /// - public virtual bool IncludeNonOperative { get; protected set; } - - /// - /// Gets the id of the to get the child activities of - /// - public virtual string? ParentId { get; protected set; } - - /// - /// Gets an object used to configure the to execute - /// - public virtual ODataQueryOptions? Options { get; protected set; } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetWorkflowActivitiesQueryHandler - : QueryHandlerBase, - IQueryHandler> - { - - /// - public V1GetWorkflowActivitiesQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task>> HandleAsync(V1GetWorkflowActivitiesQuery query, CancellationToken cancellationToken = default) - { - return await Task.Run(() => - { - var activities = this.Repository.AsQueryable() - .Where(a => a.WorkflowInstanceId == query.WorkflowInstanceId && a.ParentId == query.ParentId); - if (!query.IncludeNonOperative) - activities = activities.Where(a => a.Status < V1WorkflowActivityStatus.Faulted); - var results = activities as IQueryable; - if (query.Options != null) - results = query.Options.ApplyTo(activities); - return this.Ok(results.ToList().OfType().ToList()); - }, cancellationToken); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/WorkflowInstances/V1GetWorkflowInstanceLogsQuery.cs b/src/core/Synapse.Application/Queries/WorkflowInstances/V1GetWorkflowInstanceLogsQuery.cs deleted file mode 100644 index 3ae154093..000000000 --- a/src/core/Synapse.Application/Queries/WorkflowInstances/V1GetWorkflowInstanceLogsQuery.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Queries.WorkflowInstances -{ - - ///

- /// Represents the used to retrieve a logs - /// - public class V1GetWorkflowInstanceLogsQuery - : Query - { - - /// - /// Initializes a new - /// - protected V1GetWorkflowInstanceLogsQuery() - { - - } - - /// - /// Initializes a new - /// - /// - public V1GetWorkflowInstanceLogsQuery(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to get the logs of - /// - public virtual string Id { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetWorkflowInstanceLogsQueryHandler - : QueryHandlerBase, - IQueryHandler - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage the entities to query - /// The used to manage es - public V1GetWorkflowInstanceLogsQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository, IRepository processes) - : base(loggerFactory, mediator, mapper, repository) - { - this.Processes = processes; - } - - /// - /// Gets the used to manage es - /// - protected IRepository Processes { get; } - - /// - public virtual async Task> HandleAsync(V1GetWorkflowInstanceLogsQuery query, CancellationToken cancellationToken = default) - { - var instance = await this.Repository.FindAsync(query.Id, cancellationToken); - if (instance == null) - throw DomainException.NullReference(typeof(V1WorkflowInstance), query.Id); - if (instance.Sessions == null) - return this.Ok(string.Empty); - var processes = new List(instance.Sessions.Count); - foreach(var processId in instance.Sessions.Select(s => s.ProcessId)) - { - var process = await this.Processes.FindAsync(processId, cancellationToken); - if (process == null) - throw DomainException.NullReference(typeof(V1WorkflowProcess), processId); - processes.Add(process); - } - return this.Ok(processes.Select(p => p.Logs).Aggregate((l1, l2) => l1 += l2)!); - } - - } -} diff --git a/src/core/Synapse.Application/Queries/WorkflowInstances/V1GetWorkflowInstancesByDefinitionIdQuery.cs b/src/core/Synapse.Application/Queries/WorkflowInstances/V1GetWorkflowInstancesByDefinitionIdQuery.cs deleted file mode 100644 index 843d3cb11..000000000 --- a/src/core/Synapse.Application/Queries/WorkflowInstances/V1GetWorkflowInstancesByDefinitionIdQuery.cs +++ /dev/null @@ -1,74 +0,0 @@ -namespace Synapse.Application.Queries.WorkflowInstances -{ - - /// - /// Represents the used to get the s of a specific - /// - public class V1GetWorkflowInstancesByDefinitionIdQuery - : Query> - { - - /// - /// Initializes a new - /// - protected V1GetWorkflowInstancesByDefinitionIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// - public V1GetWorkflowInstancesByDefinitionIdQuery(string workflowId) - { - this.WorkflowId = workflowId; - } - - /// - /// Gets the id of the to get the s of - /// - public virtual string WorkflowId { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetWorkflowInstancesByDefinitionIdQueryHandler - : QueryHandlerBase, - IQueryHandler> - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to mediate calls - /// The service used to map objects - /// The used to manage the entities to query - /// The used to manage s - public V1GetWorkflowInstancesByDefinitionIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository, IRepository workflows) - : base(loggerFactory, mediator, mapper, repository) - { - this.Workflows = workflows; - } - - /// - /// Gets the used to manage es - /// - protected IRepository Workflows { get; } - - /// - public virtual async Task>> HandleAsync(V1GetWorkflowInstancesByDefinitionIdQuery query, CancellationToken cancellationToken = default) - { - var workflow = await this.Workflows.FindAsync(query.WorkflowId, cancellationToken); - if (workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), query.WorkflowId); - return this.Ok(this.Repository.AsQueryable() - .Where(wf => wf.WorkflowId == workflow.Id) - .ToList()); - } - - } -} diff --git a/src/core/Synapse.Application/Queries/Workflows/V1GetWorkflowByIdQuery.cs b/src/core/Synapse.Application/Queries/Workflows/V1GetWorkflowByIdQuery.cs deleted file mode 100644 index ea962d427..000000000 --- a/src/core/Synapse.Application/Queries/Workflows/V1GetWorkflowByIdQuery.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; - -namespace Synapse.Application.Queries.Workflows -{ - - ///

- /// Represents the used to get a by id - /// - public class V1GetWorkflowByIdQuery - : Query - { - - /// - /// Initializes a new - /// - protected V1GetWorkflowByIdQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - /// The version of the to get. Defaults to 'lastest' - public V1GetWorkflowByIdQuery(string id, string? version) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - /// - /// Gets the version of the to get. Defaults to 'latest' - /// - public virtual string? Version { get; protected set; } = null!; - - /// - /// Parses the specified reference into a new - /// - /// The reference to parse - /// A new - public static V1GetWorkflowByIdQuery Parse(string reference) - { - if (string.IsNullOrEmpty(reference)) - throw new ArgumentNullException(nameof(reference)); - var components = reference.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - string? version = null; - if (components.Length == 2) - version = components.Last(); - return new(id, version); - } - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetWorkflowByIdQueryHandler - : QueryHandlerBase, - IQueryHandler - { - - /// - public V1GetWorkflowByIdQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task> HandleAsync(V1GetWorkflowByIdQuery query, CancellationToken cancellationToken = default) - { - Integration.Models.V1Workflow? workflow; - if (string.IsNullOrWhiteSpace(query.Version) - || query.Version == "latest") - { - workflow = this.Repository.AsQueryable() - .Where(wf => wf.Definition.Id!.Equals(query.Id, StringComparison.OrdinalIgnoreCase)) - .ToList() - .OrderByDescending(wf => SemVersion.Parse(wf.Definition.Version, SemVersionStyles.Any)) - .FirstOrDefault()!; - } - else - { - workflow = await this.Repository.FindAsync($"{query.Id}:{query.Version}", cancellationToken); - } - if (workflow == null) - throw DomainException.NullReference(typeof(V1Workflow), query.Id); - return this.Ok(workflow); - } - - } - -} diff --git a/src/core/Synapse.Application/Queries/Workflows/V1GetWorkflowVersionsQuery.cs b/src/core/Synapse.Application/Queries/Workflows/V1GetWorkflowVersionsQuery.cs deleted file mode 100644 index ab0d87a2a..000000000 --- a/src/core/Synapse.Application/Queries/Workflows/V1GetWorkflowVersionsQuery.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Queries.Workflows -{ - ///

- /// Represents the used to get a by id - /// - public class V1GetWorkflowVersionsQuery - : Query> - { - - /// - /// Initializes a new - /// - protected V1GetWorkflowVersionsQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the to get. Should NOT include the version component - public V1GetWorkflowVersionsQuery(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to get. Should NOT include the version component - /// - public virtual string Id { get; protected set; } = null!; - - } - - /// - /// Represents the service used to handle instances - /// - public class V1GetWorkflowVersionsQueryHandler - : QueryHandlerBase, - IQueryHandler> - { - - /// - public V1GetWorkflowVersionsQueryHandler(ILoggerFactory loggerFactory, IMediator mediator, IMapper mapper, IRepository repository) - : base(loggerFactory, mediator, mapper, repository) - { - - } - - /// - public virtual async Task>> HandleAsync(V1GetWorkflowVersionsQuery query, CancellationToken cancellationToken = default) - { - return await Task.FromResult(this.Ok(this.Repository.AsQueryable().Where(wf => wf.Definition.Id == query.Id).ToList())); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/BackgroundJob.cs b/src/core/Synapse.Application/Services/BackgroundJob.cs deleted file mode 100644 index 951ba1460..000000000 --- a/src/core/Synapse.Application/Services/BackgroundJob.cs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Services; - -///

-/// Holds information about a scheduled background job -/// -public class BackgroundJob - : IDisposable -{ - - /// - /// Initializes a new - /// - /// The 's id - /// The current - /// The service used to perform logging - /// A that represents the job to execute - public BackgroundJob(string id, IServiceProvider serviceProvider, ILogger logger, Func job) - { - this.Id = id; - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.Job = job; - } - - /// - /// Gets the 's id - /// - public string Id { get; } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets a that represents the job to execute - /// - public Func Job { get; protected set; } - - /// - /// Gets the date and time the has been scheduled at - /// - public DateTimeOffset ScheduledAt { get; protected set; } - - /// - /// Gets the used to clock the 's occurences - /// - protected Timer Timer { get; set; } = null!; - - /// - /// Schedules the - /// - /// The date and time at which to schedule the - public virtual void Schedule(DateTimeOffset scheduleAt) - { - if (scheduleAt.ToUniversalTime() < DateTimeOffset.UtcNow) throw new ArgumentOutOfRangeException(nameof(scheduleAt), "The specified value cannot be a date in the past"); - this.ScheduledAt = scheduleAt; - var dueTime = this.ScheduledAt.ToUniversalTime() - DateTimeOffset.UtcNow; - this.Timer?.Dispose(); - this.Timer = new(OnExecuteJobAsync, null, dueTime, Timeout.InfiniteTimeSpan); - } - - /// - /// Executes the scheduled work - /// - /// The async state passed to the upon initialization - protected virtual async void OnExecuteJobAsync(object? state) - { - using var scope = this.ServiceProvider.CreateScope(); - try - { - await this.Job(scope.ServiceProvider); - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while performing the scheduled job with id '{jobId}': {ex}", this.Id, ex); - } - } - - private bool _disposed; - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._disposed) - { - if (disposing) this.Timer?.Dispose(); - this._disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Services/CloudEventCorrelator.cs b/src/core/Synapse.Application/Services/CloudEventCorrelator.cs deleted file mode 100644 index f530fc0d2..000000000 --- a/src/core/Synapse.Application/Services/CloudEventCorrelator.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Application.Commands.Correlations; -using System.Reactive.Subjects; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents a service used to correlate s - /// - public class CloudEventCorrelator - : BackgroundService - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to perform logging - /// The used to stream s - public CloudEventCorrelator(IServiceProvider serviceProvider , ILogger logger, ISubject cloudEventStream) - { - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.CloudEventStream = cloudEventStream; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the used to stream s - /// - protected ISubject CloudEventStream { get; } - - /// - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - this.CloudEventStream.SubscribeAsync(async e => - { - using var scope = this.ServiceProvider.CreateScope(); - await this.CorrelateAsync(scope.ServiceProvider, e, stoppingToken); - }); - return Task.CompletedTask; - } - - /// - /// Ingests the specified - /// - /// The current - /// The to ingest - /// A - /// A new awaitable - protected virtual async Task CorrelateAsync(IServiceProvider serviceProvider, CloudEvent e, CancellationToken cancellationToken) - { - try - { - var mediator = serviceProvider.GetRequiredService(); - await mediator.ExecuteAndUnwrapAsync(new V1CorrelateEventCommand(V1Event.CreateFrom(e)), cancellationToken); - } - catch (TaskCanceledException) - { - throw; - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while processing an incoming cloud event: {ex}", ex.ToString()); - } - } - - } - -} diff --git a/src/core/Synapse.Application/Services/DelegateIntegrationEventBus.cs b/src/core/Synapse.Application/Services/DelegateIntegrationEventBus.cs deleted file mode 100644 index 17c13ffb1..000000000 --- a/src/core/Synapse.Application/Services/DelegateIntegrationEventBus.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Synapse.Application.Services -{ - /// - /// Represents a delegate-based - /// - public class DelegateIntegrationEventBus - : IIntegrationEventBus - { - - /// - /// Initializes a new - /// - /// The current - /// The delegate to use - public DelegateIntegrationEventBus(IServiceProvider serviceProvider, Func delegateFunction) - { - this.ServiceProvider = serviceProvider; - this.DelegateFunction = delegateFunction; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the delegate to use - /// - protected Func DelegateFunction { get; } - - /// - public virtual async Task PublishAsync(CloudEvent e, CancellationToken cancellationToken = default) - { - await this.DelegateFunction(this.ServiceProvider, e, cancellationToken); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/EdmModelBuilder.cs b/src/core/Synapse.Application/Services/EdmModelBuilder.cs deleted file mode 100644 index a659dc45c..000000000 --- a/src/core/Synapse.Application/Services/EdmModelBuilder.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Microsoft.OData.Edm; -using Microsoft.OData.ModelBuilder; -using Neuroglia.Serialization; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Integration.Models; -using System.Dynamic; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation of the - /// - public class EdmModelBuilder - : IEdmModelBuilder - { - - /// - public virtual IEdmModel Build() - { - ODataConventionModelBuilder builder = new(); - builder.EnableLowerCamelCase(); - - builder.EntitySet("V1Workflows"); - builder.EntitySet("V1WorkflowInstances"); - builder.EntitySet("V1WorkflowActivities"); - builder.EntitySet("V1Correlations"); - builder.EntitySet("V1FunctionDefinitionCollections"); - builder.EntitySet("V1Schedules"); - - builder.AddComplexType(typeof(Dynamic)); - builder.AddComplexType(typeof(Neuroglia.Serialization.DynamicObject)); - builder.AddComplexType(typeof(DynamicArray)); - builder.AddComplexType(typeof(ExpandoObject)); - builder.AddComplexType(typeof(WorkflowDefinition)); - builder.AddComplexType(typeof(NameValueCollection)); - builder.AddComplexType(typeof(List)); - - return builder.GetEdmModel(); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/IEdmModelBuilder.cs b/src/core/Synapse.Application/Services/IEdmModelBuilder.cs deleted file mode 100644 index 3d153663b..000000000 --- a/src/core/Synapse.Application/Services/IEdmModelBuilder.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Microsoft.OData.Edm; - -namespace Synapse.Application.Services -{ - - ///

- /// Defines the fundamentals of a service used to build s - /// - public interface IEdmModelBuilder - { - - /// - /// Builds a new - /// - /// The application's - IEdmModel Build(); - - } - -} diff --git a/src/core/Synapse.Application/Services/IODataQueryOptionsParser.cs b/src/core/Synapse.Application/Services/IODataQueryOptionsParser.cs deleted file mode 100644 index b5646f603..000000000 --- a/src/core/Synapse.Application/Services/IODataQueryOptionsParser.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.OData.Query; - -namespace Synapse.Application.Services -{ - - ///

- /// Defines the fundamentals of a service used to parse - /// - public interface IODataQueryOptionsParser - { - - /// - /// Parses the specified ODATA query string into a new - /// - /// The type of entity to query - /// The ODATA query string to parse - /// A new - ODataQueryOptions Parse(string? query) - where TEntity : class, IIdentifiable; - - } - -} diff --git a/src/core/Synapse.Application/Services/InMemoryBackgroundJobManager.cs b/src/core/Synapse.Application/Services/InMemoryBackgroundJobManager.cs deleted file mode 100644 index 0eabc7a85..000000000 --- a/src/core/Synapse.Application/Services/InMemoryBackgroundJobManager.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Collections.Concurrent; - -namespace Synapse.Application.Services; - -///

-/// Represents the default, in-memory implementation of the interface -/// -public class InMemoryBackgroundJobManager - : IBackgroundJobManager -{ - - /// - /// Initializes a new - /// - /// The current - public InMemoryBackgroundJobManager(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets a used to store active s - /// - protected ConcurrentDictionary BackgroundJobs { get; } = new(); - - /// - public virtual Task ScheduleJobAsync(string jobId, Func job, DateTimeOffset scheduleAt, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(jobId)) throw new ArgumentNullException(nameof(jobId)); - if (job == null) throw new ArgumentNullException(nameof(job)); - if (scheduleAt.ToUniversalTime() < DateTimeOffset.UtcNow) throw new ArgumentOutOfRangeException(nameof(scheduleAt), "The specified value cannot be a date in the past"); - if(!this.BackgroundJobs.TryGetValue(jobId, out var backgroundJob)) - { - backgroundJob = ActivatorUtilities.CreateInstance(this.ServiceProvider, jobId, job); - this.BackgroundJobs.TryAdd(jobId, backgroundJob); - } - backgroundJob.Schedule(scheduleAt); - return Task.CompletedTask; - } - - /// - public virtual Task CancelJobAsync(string jobId, CancellationToken cancellationToken = default) - { - if (string.IsNullOrWhiteSpace(jobId)) throw new ArgumentNullException(nameof(jobId)); - if (!this.BackgroundJobs.TryGetValue(jobId, out var backgroundJob)) throw new NullReferenceException($"Failed to find a background job with the specified id '{jobId}'"); - this.BackgroundJobs.TryRemove(jobId, out backgroundJob); - backgroundJob?.Dispose(); - return Task.CompletedTask; - } - -} diff --git a/src/core/Synapse.Application/Services/IntegrationBusPipelineFactory.cs b/src/core/Synapse.Application/Services/IntegrationBusPipelineFactory.cs deleted file mode 100644 index d0687d87b..000000000 --- a/src/core/Synapse.Application/Services/IntegrationBusPipelineFactory.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation of the - /// - public class IntegrationBusPipelineFactory - : IIntegrationEventBusFactory - { - - /// - /// Initializes a new - /// - /// The current - public IntegrationBusPipelineFactory(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - public virtual IIntegrationEventBus Create() - { - return ActivatorUtilities.CreateInstance(this.ServiceProvider); - } - } - -} diff --git a/src/core/Synapse.Application/Services/IntegrationEventBusPipeline.cs b/src/core/Synapse.Application/Services/IntegrationEventBusPipeline.cs deleted file mode 100644 index fea11961c..000000000 --- a/src/core/Synapse.Application/Services/IntegrationEventBusPipeline.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class IntegrationEventBusPipeline - : IIntegrationEventBus - { - - /// - /// Initializes a new - /// - /// The current - /// The current - public IntegrationEventBusPipeline(IServiceProvider serviceProvider, IOptions options) - { - this.ServiceProvider = serviceProvider; - this.Options = options.Value; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the current - /// - protected IntegrationEventBusPipelineOptions Options { get; } - - /// - public virtual async Task PublishAsync(CloudEvent e, CancellationToken cancellationToken = default) - { - var tasks = new List(); - foreach (var middleware in this.Options.Middlewares) - { - tasks.Add(middleware.Invoke(this.ServiceProvider, e)); - } - await Task.WhenAll(tasks); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/ODataQueryOptionsParser.cs b/src/core/Synapse.Application/Services/ODataQueryOptionsParser.cs deleted file mode 100644 index dd2dde19e..000000000 --- a/src/core/Synapse.Application/Services/ODataQueryOptionsParser.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.OData.Query; -using Microsoft.OData.Edm; -using Microsoft.OData.UriParser; - -namespace Synapse.Application.Services -{ - ///

- /// Represents the default implementation of the - /// - public class ODataQueryOptionsParser - : IODataQueryOptionsParser - { - - /// - /// Initializes a new - /// - /// The semantic description of the application's EDM model - public ODataQueryOptionsParser(IEdmModel edmModel) - { - this.EdmModel = edmModel; - } - - /// - /// Gets the semantic description of the application's EDM model - /// - protected IEdmModel EdmModel { get; } - - /// - public virtual ODataQueryOptions Parse(string? query) - where TEntity : class, IIdentifiable - { - ODataQueryOptions queryOptions = null!; - if (!string.IsNullOrWhiteSpace(query)) - { - var context = new DefaultHttpContext(); - context.Request.QueryString = new($"?{query}"); - var parser = new ODataUriParser(this.EdmModel, new(string.Empty, UriKind.Relative)); - var path = parser.ParsePath(); - queryOptions = new ODataQueryOptions(new(this.EdmModel, typeof(TEntity), path), context.Request); - } - return queryOptions; - } - - } - -} diff --git a/src/core/Synapse.Application/Services/ODataSearchBinder.cs b/src/core/Synapse.Application/Services/ODataSearchBinder.cs deleted file mode 100644 index 3f9cc2f89..000000000 --- a/src/core/Synapse.Application/Services/ODataSearchBinder.cs +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.OData.Query.Expressions; -using Microsoft.OData.UriParser; -using System.Linq.Expressions; -using System.Reflection; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation - /// - public class ODataSearchBinder - : QueryBinder, ISearchBinder - { - - private static readonly Dictionary BinaryOperatorMapping = new Dictionary - { - { BinaryOperatorKind.And, ExpressionType.AndAlso }, - { BinaryOperatorKind.Or, ExpressionType.OrElse }, - }; - - private static readonly MethodInfo FilterWorkflowMethod = typeof(ODataSearchBinder).GetMethod(nameof(FilterWorkflow), BindingFlags.Static | BindingFlags.NonPublic)!; - private static readonly MethodInfo FilterWorkflowInstanceMethod = typeof(ODataSearchBinder).GetMethod(nameof(FilterWorkflowInstance), BindingFlags.Static | BindingFlags.NonPublic)!; - private static readonly MethodInfo FilterWorkflowActivityMethod = typeof(ODataSearchBinder).GetMethod(nameof(FilterWorkflowActivity), BindingFlags.Static | BindingFlags.NonPublic)!; - private static readonly MethodInfo FilterCorrelationMethod = typeof(ODataSearchBinder).GetMethod(nameof(FilterCorrelation), BindingFlags.Static | BindingFlags.NonPublic)!; - private static readonly MethodInfo FilterFunctionCollectionMethod = typeof(ODataSearchBinder).GetMethod(nameof(FilterFunctionCollection), BindingFlags.Static | BindingFlags.NonPublic)!; - private static readonly MethodInfo FilterScheduleMethod = typeof(ODataSearchBinder).GetMethod(nameof(FilterSchedule), BindingFlags.Static | BindingFlags.NonPublic)!; - - /// - public Expression BindSearch(SearchClause searchClause, QueryBinderContext context) - { - return Expression.Lambda(this.BindSingleValueNode(searchClause.Expression, context), context.CurrentParameter); - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - public override Expression BindSingleValueNode(SingleValueNode node, QueryBinderContext context) - { - return node switch - { - BinaryOperatorNode binaryOperatorNode => this.BindBinaryOperatorNode(binaryOperatorNode, context), - SearchTermNode searchTermNode => this.BindSearchTerm(searchTermNode, context), - UnaryOperatorNode unaryOperatorNode => this.BindUnaryOperatorNode(unaryOperatorNode, context), - _ => throw new NotSupportedException($"The specified {nameof(SingleValueNode)} type '{node.GetType().Name}' is not supported") - }; - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - public override Expression BindBinaryOperatorNode(BinaryOperatorNode binaryOperatorNode, QueryBinderContext context) - { - var left = this.Bind(binaryOperatorNode.Left, context); - var right = this.Bind(binaryOperatorNode.Right, context); - if (!BinaryOperatorMapping.TryGetValue(binaryOperatorNode.OperatorKind, out ExpressionType binaryExpressionType)) - throw new NotImplementedException($"Binary operator '{binaryOperatorNode.OperatorKind}' is not supported!"); - return Expression.MakeBinary(binaryExpressionType, left, right); - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - public Expression BindSearchTerm(SearchTermNode term, QueryBinderContext context) - { - if (term == null) - throw new ArgumentNullException(nameof(term)); - if (context.ElementClrType == typeof(Integration.Models.V1Workflow)) - return this.BindWorkflowSearchTerm(term, context); - else if (context.ElementClrType == typeof(Integration.Models.V1WorkflowInstance)) - return this.BindWorkflowInstanceSearchTerm(term, context); - else if (context.ElementClrType == typeof(Integration.Models.V1WorkflowActivity)) - return this.BindWorkflowActivitySearchTerm(term, context); - else if (context.ElementClrType == typeof(Integration.Models.V1Correlation)) - return this.BindCorrelationSearchTerm(term, context); - else if (context.ElementClrType == typeof(Integration.Models.V1FunctionDefinitionCollection)) - return this.BindFunctionCollectionSearchTerm(term, context); - else if (context.ElementClrType == typeof(Integration.Models.V1Schedule)) - return this.BindScheduleSearchTerm(term, context); - else - throw new NotSupportedException($"Search is not allowed on element type '{context.ElementClrType.Name}'"); - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - protected virtual Expression BindWorkflowSearchTerm(SearchTermNode searchTermNode, QueryBinderContext context) - { - var searchTerm = searchTermNode.Text.ToLowerInvariant(); - var searchQuery = Expression.IsTrue(Expression.Call(null, FilterWorkflowMethod, context.CurrentParameter, Expression.Constant(searchTerm))); - return searchQuery; - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - protected virtual Expression BindWorkflowInstanceSearchTerm(SearchTermNode searchTermNode, QueryBinderContext context) - { - var searchTerm = searchTermNode.Text.ToLowerInvariant(); - var searchQuery = Expression.IsTrue(Expression.Call(null, FilterWorkflowInstanceMethod, context.CurrentParameter, Expression.Constant(searchTerm))); - return searchQuery; - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - protected virtual Expression BindWorkflowActivitySearchTerm(SearchTermNode searchTermNode, QueryBinderContext context) - { - var searchTerm = searchTermNode.Text.ToLowerInvariant(); - var searchQuery = Expression.IsTrue(Expression.Call(null, FilterWorkflowActivityMethod, context.CurrentParameter, Expression.Constant(searchTerm))); - return searchQuery; - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - protected virtual Expression BindCorrelationSearchTerm(SearchTermNode searchTermNode, QueryBinderContext context) - { - var searchTerm = searchTermNode.Text.ToLowerInvariant(); - var searchQuery = Expression.IsTrue(Expression.Call(null, FilterCorrelationMethod, context.CurrentParameter, Expression.Constant(searchTerm))); - return searchQuery; - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - protected virtual Expression BindFunctionCollectionSearchTerm(SearchTermNode searchTermNode, QueryBinderContext context) - { - var searchTerm = searchTermNode.Text.ToLowerInvariant(); - var searchQuery = Expression.IsTrue(Expression.Call(null, FilterFunctionCollectionMethod, context.CurrentParameter, Expression.Constant(searchTerm))); - return searchQuery; - } - - /// - /// Binds the specified - /// - /// The to bind - /// The current - /// A new - protected virtual Expression BindScheduleSearchTerm(SearchTermNode searchTermNode, QueryBinderContext context) - { - var searchTerm = searchTermNode.Text.ToLowerInvariant(); - var searchQuery = Expression.IsTrue(Expression.Call(null, FilterScheduleMethod, context.CurrentParameter, Expression.Constant(searchTerm))); - return searchQuery; - } - - static bool FilterWorkflow(Integration.Models.V1Workflow workflow, string searchTerm) - { - return workflow.Id.ToLower().Contains(searchTerm) - || (!string.IsNullOrWhiteSpace(workflow.Definition.Id) && workflow.Definition.Id.ToLowerInvariant().Contains(searchTerm)) - || workflow.Definition.Name.ToLowerInvariant().Contains(searchTerm) - || (!string.IsNullOrWhiteSpace(workflow.Definition.Description) && workflow.Definition.Description.ToLowerInvariant().Contains(searchTerm)) - || workflow.Definition.Version.ToLowerInvariant().Contains(searchTerm) - || workflow.Definition.SpecVersion.ToLowerInvariant().Contains(searchTerm) - || (workflow.Definition.Annotations != null && workflow.Definition.Annotations.Any(a => a.ToLowerInvariant() == searchTerm)); - } - - static bool FilterWorkflowInstance(Integration.Models.V1WorkflowInstance workflowInstance, string searchTerm) - { - return workflowInstance.Id.ToLower().Contains(searchTerm) - || workflowInstance.WorkflowId.ToLowerInvariant().Contains(searchTerm) - || workflowInstance.Key.ToLowerInvariant().Contains(searchTerm) - || (!string.IsNullOrWhiteSpace(workflowInstance.ParentId) && workflowInstance.ParentId.ToLowerInvariant().Contains(searchTerm)) - || EnumHelper.Stringify(workflowInstance.ActivationType).ToLowerInvariant().Contains(searchTerm) - || EnumHelper.Stringify(workflowInstance.Status).ToLowerInvariant().Contains(searchTerm); - } - - static bool FilterWorkflowActivity(Integration.Models.V1WorkflowActivity workflowActivity, string searchTerm) - { - return workflowActivity.Id.ToLower().Contains(searchTerm) - || (workflowActivity.Error != null && (workflowActivity.Error.Code.ToLowerInvariant().Contains(searchTerm) || workflowActivity.Error.Message.ToLowerInvariant().Contains(searchTerm))) - || (!string.IsNullOrWhiteSpace(workflowActivity.ParentId) && workflowActivity.ParentId.ToLowerInvariant().Contains(searchTerm)) - || EnumHelper.Stringify(workflowActivity.Type).ToLowerInvariant().Contains(searchTerm) - || EnumHelper.Stringify(workflowActivity.Status).ToLowerInvariant().Contains(searchTerm); - } - - static bool FilterCorrelation(Integration.Models.V1Correlation correlation, string searchTerm) - { - return correlation.Id.ToLower().Contains(searchTerm) - || (correlation.Conditions != null && correlation.Conditions.Any(c => - c.Filters != null - && (c.Filters.Any(f => - (f.Attributes != null && f.Attributes.Any(kvp => kvp.Key.ToLower().Contains(searchTerm) || kvp.Value.ToLower().Contains(searchTerm))) - || (f.CorrelationMappings != null && f.CorrelationMappings.Any(kvp => kvp.Key.ToLower().Contains(searchTerm) || (!string.IsNullOrWhiteSpace(kvp.Value) && kvp.Value.ToLower().Contains(searchTerm)))))))) - || (correlation.Contexts != null && correlation.Contexts.Any(c => c.Id.Contains(searchTerm))) - || (correlation.Contexts != null && correlation.Contexts.Any(c => c.Mappings != null && (c.Mappings.Any(kvp => kvp.Key.ToLower().Contains(searchTerm) || kvp.Value.ToLower().Contains(searchTerm))))) - || EnumHelper.Stringify(correlation.Lifetime).ToLowerInvariant().Contains(searchTerm) - || EnumHelper.Stringify(correlation.ConditionType).ToLowerInvariant().Contains(searchTerm); - } - - static bool FilterFunctionCollection(Integration.Models.V1FunctionDefinitionCollection collection, string searchTerm) - { - return collection.Id.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || collection.Name.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || collection.Version.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || collection.Description.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || (collection.Functions != null && collection.Functions.Any(f => f.Name.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase))) - || (collection.Functions != null && collection.Functions.Any(f => f.Operation.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase))) - || (collection.Functions != null && collection.Functions.Any(f => EnumHelper.Stringify(f.Type).Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase))); - } - - static bool FilterSchedule(Integration.Models.V1Schedule schedule, string searchTerm) - { - return schedule.Id.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || schedule.WorkflowId.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || EnumHelper.Stringify(schedule.ActivationType).Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || EnumHelper.Stringify(schedule.Status).Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || (schedule.Definition.Cron != null && schedule.Definition.Cron!.Expression.Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase)) - || (schedule.Definition.Interval.HasValue && schedule.Definition.Interval.Value.ToString().Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase)) - || schedule.CreatedAt.ToString().Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || schedule.LastModified.ToString().Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase) - || (schedule.LastOccuredAt.HasValue && schedule.LastOccuredAt.Value.ToString().Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase)) - || (schedule.NextOccurenceAt.HasValue && schedule.NextOccurenceAt.Value.ToString().Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase)) - || (schedule.LastCompletedAt.HasValue && schedule.LastCompletedAt.Value.ToString().Contains(searchTerm, StringComparison.InvariantCultureIgnoreCase)); - } - - - } - -} diff --git a/src/core/Synapse.Application/Services/PluginAssemblyLoadContext.cs b/src/core/Synapse.Application/Services/PluginAssemblyLoadContext.cs deleted file mode 100644 index d70dcb78f..000000000 --- a/src/core/Synapse.Application/Services/PluginAssemblyLoadContext.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure.Plugins; -using System.Reflection; -using System.Runtime.Loader; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents an used to load assemblies - /// - public class PluginAssemblyLoadContext - : AssemblyLoadContext - { - - /// - /// Initializes a new - /// - /// The service used to resolve assembly dependencies - public PluginAssemblyLoadContext(AssemblyDependencyResolver assemblyDependencyResolver) - : base("PluginAssemblyLoadContext", true) - { - this.AssemblyDependencyResolver = assemblyDependencyResolver; - } - - /// - /// Initializes a new - /// - /// The path of the plugin to load - public PluginAssemblyLoadContext(string path) - : this(new AssemblyDependencyResolver(path)) - { - var directory = new DirectoryInfo(Path.GetDirectoryName(path)!); - foreach(var assemblyFile in directory.GetFiles("*.dll")) - { - var assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyFile.FullName)!); - if(Default.Assemblies.Any(a => a.GetName().Name == assemblyName.Name)) - assemblyFile.Delete(); - } - } - - /// - /// Gets the service used to resolve assembly dependencies - /// - protected AssemblyDependencyResolver AssemblyDependencyResolver { get; } - - /// - protected override Assembly? Load(AssemblyName assemblyName) - { - var assembly = Default.Assemblies.FirstOrDefault(a => a.FullName == assemblyName.FullName); - if (assembly != null) - return assembly; - var assemblyPath = this.AssemblyDependencyResolver.ResolveAssemblyToPath(assemblyName); - if (string.IsNullOrWhiteSpace(assemblyPath)) - return null; - return this.LoadFromAssemblyPath(assemblyPath); - } - - /// - protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) - { - var assemblyPath = this.AssemblyDependencyResolver.ResolveUnmanagedDllToPath(unmanagedDllName); - if (string.IsNullOrWhiteSpace(assemblyPath)) - return IntPtr.Zero; - return this.LoadUnmanagedDllFromPath(assemblyPath); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/PluginBasedRepositoryFactory.cs b/src/core/Synapse.Application/Services/PluginBasedRepositoryFactory.cs deleted file mode 100644 index 731c67b32..000000000 --- a/src/core/Synapse.Application/Services/PluginBasedRepositoryFactory.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure; -using Synapse.Infrastructure.Plugins; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default, -based implementation of the interface - /// - public class PluginBasedRepositoryFactory - : IRepositoryFactory - { - - /// - /// Initializes a new - /// - /// The current - /// A service used to manage the application's s - /// The service used to access the current - public PluginBasedRepositoryFactory(IServiceProvider serviceProvider, IPluginManager pluginManager, IOptions options) - { - this.ServiceProvider = serviceProvider; - this.PluginManager = pluginManager; - this.Options = options.Value; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to manage the application's s - /// - protected IPluginManager PluginManager { get; } - - /// - /// Gets the current - /// - protected SynapseApplicationOptions Options { get; } - - /// - public virtual IRepository CreateRepository(Type entityType, Type keyType, ApplicationModelType modelType) - { - if(entityType == null) - throw new ArgumentNullException(nameof(entityType)); - if (keyType == null) - throw new ArgumentNullException(nameof(keyType)); - var pluginName = modelType switch - { - ApplicationModelType.WriteModel => this.Options.Persistence.DefaultWriteModelRepository?.PluginName, - ApplicationModelType.ReadModel => this.Options.Persistence.DefaultReadModelRepository?.PluginName, - _ => throw new NotSupportedException($"The specified {nameof(ApplicationModelType)} '{modelType}' is not supported") - }; - if (this.Options.Persistence.Repositories.TryGetValue(entityType.FullName!, out var repositoryOptions)) - pluginName = repositoryOptions.PluginName; - if (string.IsNullOrWhiteSpace(pluginName) - || !this.PluginManager.TryGetPlugin(pluginName, out var factory)) - return (IRepository)ActivatorUtilities.CreateInstance(this.ServiceProvider, typeof(DistributedCacheRepository<,>).MakeGenericType(entityType, keyType)); - return factory.CreateRepository(entityType, keyType); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/PluginHandle.cs b/src/core/Synapse.Application/Services/PluginHandle.cs deleted file mode 100644 index b6d2410c1..000000000 --- a/src/core/Synapse.Application/Services/PluginHandle.cs +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure.Plugins; -using Synapse.Integration.Models; -using System.Reflection; -using System.Runtime.Loader; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation - /// - public class PluginHandle - : IPluginHandle - { - - /// - public event EventHandler? Disposed; - - private bool _Disposed; - - /// - /// Initializes a new - /// - /// The current - /// An object used to describe the handled - /// The path to the handled file - public PluginHandle(IServiceProvider serviceProvider, V1PluginMetadata metadata, string metadataFilePath) - { - this.ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - this.Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); - this.MetadataFilePath = metadataFilePath ?? throw new ArgumentNullException(nameof(metadataFilePath)); - this.AssemblyFilePath = Path.Combine(Path.GetDirectoryName(this.MetadataFilePath)!, this.Metadata.AssemblyFileName); - if (!File.Exists(metadataFilePath)) - throw new FileNotFoundException(nameof(metadataFilePath)); - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the 's - /// - protected IServiceScope ServiceScope { get; private set; } = null!; - - /// - public virtual V1PluginMetadata Metadata { get; } - - /// - public virtual bool IsLoaded { get; protected set; } - - /// - /// Gets the path to the 's file - /// - public virtual string MetadataFilePath { get; } - - /// - /// Gets the path to the 's file - /// - protected virtual string AssemblyFilePath { get; } - - /// - /// Gets the 's - /// - protected AssemblyLoadContext? LoadContext { get; set; } - - /// - /// Gets the 's - /// - protected Assembly? Assembly { get; set; } - - /// - /// Gets the loaded , if - /// - protected IPlugin? Plugin { get; set; } - - /// - /// Gets the 's - /// - protected CancellationTokenSource? CancellationTokenSource { get; private set; } - - /// - public virtual async ValueTask LoadAsync(CancellationToken stoppingToken) - { - if (this.IsLoaded) - return; - try - { - this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); - this.LoadContext = new PluginAssemblyLoadContext(this.AssemblyFilePath); - this.Assembly = this.LoadContext.LoadFromAssemblyName(new(Path.GetFileNameWithoutExtension(this.AssemblyFilePath))); - var pluginType = this.Assembly.GetTypes().FirstOrDefault(t => t.IsClass - && !t.IsInterface - && !t.IsGenericTypeDefinition - && !t.IsAbstract - && typeof(IPlugin).IsAssignableFrom(t)); - if (pluginType == null) - throw new TypeLoadException($"Failed to find a valid plugin type in the specified assembly '{this.Assembly.FullName}'"); - this.ServiceScope = this.ServiceProvider.CreateScope(); - this.Plugin = (IPlugin)ActivatorUtilities.CreateInstance(this.ServiceScope.ServiceProvider, pluginType); - await this.Plugin.InitializeAsync(this.CancellationTokenSource.Token); - this.IsLoaded = true; - await ValueTask.CompletedTask; - } - catch - { - this.Unload(); - throw; - } - } - - /// - public virtual IPlugin GetPlugin() - { - if (!this.IsLoaded) - throw new AppDomainUnloadedException(); - return this.Plugin!; - } - - /// - public virtual void Unload() - { - if (!this.IsLoaded) - return; - this.ServiceScope?.Dispose(); - this.ServiceScope = null!; - this.Assembly = null; - this.LoadContext?.Unload(); - this.LoadContext = null; - this.Plugin?.Dispose(); - this.Plugin = null; - this.IsLoaded = true; - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - /// A new awaitable - protected virtual ValueTask DisposeAsync(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.Unload(); - } - this._Disposed = true; - } - return ValueTask.CompletedTask; - } - - /// - public async ValueTask DisposeAsync() - { - await this.DisposeAsync(true); - this.Disposed?.Invoke(this, new()); - this.CancellationTokenSource?.Dispose(); - GC.SuppressFinalize(this); - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.Unload(); - } - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(true); - this.Disposed?.Invoke(this, new()); - this.CancellationTokenSource?.Dispose(); - GC.SuppressFinalize(this); - } - - /// - public override string ToString() - { - return this.Metadata.ToString()!; - } - - } - -} diff --git a/src/core/Synapse.Application/Services/PluginManager.cs b/src/core/Synapse.Application/Services/PluginManager.cs deleted file mode 100644 index 384943010..000000000 --- a/src/core/Synapse.Application/Services/PluginManager.cs +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Serialization; -using Synapse.Infrastructure.Plugins; -using Synapse.Integration.Models; -using System.IO.Compression; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class PluginManager - : BackgroundService, IPluginManager - { - - /// - /// Gets the name of an metadata file - /// - public const string PluginMetadataFileName = "plugin.json"; - - /// - /// Initializes a new - /// - /// The current - /// The service used to perform logging - /// The service to serialize/deserialize to/from JSON - /// The service used to access the current - public PluginManager(IServiceProvider serviceProvider, ILogger logger, IJsonSerializer jsonSerializer, IOptions applicationOptions) - { - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.JsonSerializer = jsonSerializer; - this.ApplicationOptions = applicationOptions.Value; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service to serialize/deserialize to/from JSON - /// - protected IJsonSerializer JsonSerializer { get; } - - /// - /// Gets the current - /// - protected SynapseApplicationOptions ApplicationOptions { get; } - - /// - /// Gets the service used to watch the files - /// - protected FileSystemWatcher FileSystemWatcher { get; private set; } = null!; - - /// - /// Gets the 's - /// - protected CancellationTokenSource CancellationTokenSource { get; private set; } = null!; - - /// - /// Gets an containing the handles to all discovered s - /// - public SynchronizedCollection Plugins { get; } = new(); - - IEnumerable IPluginManager.Plugins => this.Plugins; - - /// - /// Gets the used to wait for the 's - /// - protected TaskCompletionSource StartupTaskCompletionSource { get; } = new(); - - /// - public async ValueTask WaitForStartupAsync(CancellationToken stoppingToken) - { - await this.StartupTaskCompletionSource.Task; - } - - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); - var pluginDirectory = new DirectoryInfo(this.ApplicationOptions.Plugins.Directory); - if(!pluginDirectory.Exists) - pluginDirectory.Create(); - foreach(var packageFile in pluginDirectory.GetFiles("*.tar.gz")) - { - await TarGzPackage.ExtractToDirectoryAsync(packageFile.FullName, packageFile.Directory!.FullName, stoppingToken); - packageFile.Delete(); - } - var plugins = await this.FindPluginsAsync(pluginDirectory.FullName); - foreach (var plugin in plugins) - { - this.Logger.LogInformation("Loading plugin '{plugin}'...", plugin.ToString()); - try - { - await plugin.LoadAsync(this.CancellationTokenSource.Token); - this.Logger.LogInformation("The plugin '{plugin}' has been successfully loaded", plugin.ToString()); - } - catch(Exception ex) - { - this.Logger.LogWarning("An error occured while loading plugin '{plugin}': {ex}", plugin.ToString(), ex.ToString()); - continue; - } - } - this.FileSystemWatcher = new(pluginDirectory.FullName, $"*.*"); - this.FileSystemWatcher.IncludeSubdirectories = true; - this.FileSystemWatcher.Created += this.OnPluginFileCreatedAsync; - this.FileSystemWatcher.Deleted += this.OnPluginFileDeletedAsync; - this.FileSystemWatcher.EnableRaisingEvents = true; - this.StartupTaskCompletionSource.SetResult(); - } - - /// - /// Finds all s in the specified directory - /// - /// The path of the directory to scan for s - /// A new containing all s that have been found - public virtual async Task> FindPluginsAsync(string directoryPath) - { - if (string.IsNullOrWhiteSpace(directoryPath)) - throw new ArgumentNullException(nameof(directoryPath)); - this.Logger.LogInformation("Scanning directory'{directory}' for plugins...", directoryPath); - var directory = new DirectoryInfo(directoryPath); - if (!directory.Exists) - throw new DirectoryNotFoundException($"Failed to find the specified directory '{directoryPath}'"); - var pluginFiles = directory.GetFiles(PluginMetadataFileName, SearchOption.AllDirectories); - this.Logger.LogInformation("Found {results} matching plugin files in directory '{directory}'", pluginFiles.Count(), directoryPath); - var plugins = new List(pluginFiles.Count()); - foreach (var pluginFile in pluginFiles) - { - plugins.Add(await this.FindPluginAsync(pluginFile.FullName)); - } - this.Logger.LogInformation("{pluginCount} plugins have been found in '{directory}' directory", plugins.Count(), directoryPath); - return plugins; - } - - /// - /// Finds the at the specified file path - /// - /// The file path of the to find - /// A new - public virtual async Task FindPluginAsync(string metadataFilePath) - { - if (string.IsNullOrWhiteSpace(metadataFilePath)) - throw new ArgumentNullException(nameof(metadataFilePath)); - var plugin = this.Plugins.FirstOrDefault(p => p.MetadataFilePath == metadataFilePath); - if (plugin != null) - return plugin; - var file = new FileInfo(metadataFilePath); - if (!file.Exists) - throw new DirectoryNotFoundException($"Failed to find the specified file '{metadataFilePath}'"); - if (file.Name != PluginMetadataFileName) - throw new Exception($"The specified file '{metadataFilePath}' is not a valid plugin metadata file"); - using var fileStream = file.OpenRead(); - var pluginMetadata = await this.JsonSerializer.DeserializeAsync(fileStream, this.CancellationTokenSource.Token); - plugin = ActivatorUtilities.CreateInstance(this.ServiceProvider, pluginMetadata, metadataFilePath); - plugin.Disposed += (sender, e) => this.OnPluginHandleDisposed((IPluginHandle)sender!); - this.Plugins.Add(plugin); - return plugin; - } - - /// - public virtual async Task LoadPluginAsync(IPluginHandle pluginHandle) - { - if(pluginHandle == null) - throw new ArgumentNullException(nameof(pluginHandle)); - await pluginHandle.LoadAsync(this.CancellationTokenSource.Token); - return pluginHandle.GetPlugin(); - } - - /// - public virtual async ValueTask UnloadPluginAsync(IPluginHandle pluginHandle) - { - if (pluginHandle == null) - throw new ArgumentNullException(nameof(pluginHandle)); - pluginHandle.Unload(); - await ValueTask.CompletedTask; - } - - /// - public virtual IPlugin? GetPlugin(string name) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullException(nameof(name)); - var pluginHandle = this.Plugins.FirstOrDefault(p => p.Metadata.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - return pluginHandle?.GetPlugin(); - } - - /// - public virtual TPlugin? GetPlugin() - where TPlugin : IPlugin - { - return this.Plugins.OfType() - .FirstOrDefault(); - } - - /// - public virtual IEnumerable GetPlugins() - where TPlugin : IPlugin - { - return this.Plugins.OfType() - .ToList(); - } - - /// - /// Handles the creation of a new file - /// - /// The service used to watch the files - /// The to handle - protected virtual async void OnPluginFileCreatedAsync(object sender, FileSystemEventArgs e) - { - if (e.FullPath.EndsWith(".tar.gz")) - { - var packageFile = new FileInfo(e.FullPath); - do - { - await Task.Delay(250); - } - while (packageFile.IsLocked()); - using var packageFileStream = packageFile.OpenRead(); - await TarGzPackage.ExtractToDirectoryAsync(packageFile.FullName, packageFile.Directory!.FullName); - await packageFileStream.DisposeAsync(); - await this.FindPluginsAsync(packageFile.Directory!.FullName); - packageFile.Delete(); - } - else if(e.FullPath == "plugin.json") - { - await this.FindPluginAsync(e.FullPath); - } - } - - /// - /// Handles the deletion of a new file - /// - /// The service used to watch the files - /// The to handle - protected virtual async void OnPluginFileDeletedAsync(object sender, FileSystemEventArgs e) - { - var pluginHandler = this.Plugins.FirstOrDefault(p => p.MetadataFilePath == e.FullPath); - if (pluginHandler != null) - pluginHandler.Dispose(); - await Task.CompletedTask; - } - - /// - /// Handles the disposal of the specified - /// - /// The that has been disposed of - protected virtual void OnPluginHandleDisposed(IPluginHandle pluginHandle) - { - this.Plugins.Remove(pluginHandle); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/PublishIntegrationEventDelegate.cs b/src/core/Synapse.Application/Services/PublishIntegrationEventDelegate.cs deleted file mode 100644 index c28c67776..000000000 --- a/src/core/Synapse.Application/Services/PublishIntegrationEventDelegate.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Services -{ - - ///

- /// Publishes the specified - /// - /// The current - /// The to publish - /// A new awaitable - public delegate Task PublishIntegrationEventDelegate(IServiceProvider serviceProvider, CloudEvent e); - -} diff --git a/src/core/Synapse.Application/Services/WorkflowDefinitionFileMonitor.cs b/src/core/Synapse.Application/Services/WorkflowDefinitionFileMonitor.cs deleted file mode 100644 index 68da3812f..000000000 --- a/src/core/Synapse.Application/Services/WorkflowDefinitionFileMonitor.cs +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using ServerlessWorkflow.Sdk.Services.IO; -using Synapse.Application.Commands.Workflows; -using Synapse.Infrastructure.Plugins; - -namespace Synapse.Application.Services; - - -///

-/// Represents an used to monitor files -/// -public class WorkflowDefinitionFileMonitor - : BackgroundService -{ - - /// - /// Initializes a new - /// - /// The current - /// The service used to perform logging - /// The service used to access the current - /// The service used to manage s - /// The service used to read s - public WorkflowDefinitionFileMonitor(IServiceProvider serviceProvider, ILogger logger, IOptions options, IPluginManager pluginManager, IWorkflowReader workflowReader) - { - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.Options = options.Value; - this.PluginManager = pluginManager; - this.WorkflowReader = workflowReader; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the current - /// - protected SynapseApplicationOptions Options { get; } - - /// - /// Gets the service used to manage s - /// - protected IPluginManager PluginManager { get; } - - /// - /// Gets the service used to read s - /// - protected IWorkflowReader WorkflowReader { get; } - - /// - /// Gets the 's - /// - protected CancellationTokenSource CancellationTokenSource { get; private set; } = null!; - - /// - /// Gets a service used to watch files - /// - protected FileSystemWatcher FileSystemWatcher { get; private set; } = null!; - - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - await this.PluginManager.WaitForStartupAsync(stoppingToken); - if (string.IsNullOrWhiteSpace(this.Options.DefinitionsDirectory)) - return; - this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); - var directory = new DirectoryInfo(this.Options.DefinitionsDirectory); - if (!directory.Exists) - directory.Create(); - this.FileSystemWatcher = new(directory.FullName) - { - IncludeSubdirectories = true, - NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName, - EnableRaisingEvents = true - }; - this.FileSystemWatcher.Created += this.OnFileCreatedOrChangedAsync; - this.FileSystemWatcher.Changed += this.OnFileCreatedOrChangedAsync; - foreach (var file in directory.GetFiles("*.*", SearchOption.AllDirectories) - .Where(f => f.Extension.ToLower() == ".json" || f.Extension.ToLower() == ".yml" || f.Extension.ToLower() == ".yaml")) - { - await this.ReadAndCreateWorkflowAsync(file.FullName, true); - } - } - - /// - /// Handles the creation of a new file in the definitions directory - /// - /// The sender of the - /// The to handle - protected virtual async void OnFileCreatedOrChangedAsync(object sender, FileSystemEventArgs e) - { - if (e.ChangeType != WatcherChangeTypes.Created - && e.ChangeType != WatcherChangeTypes.Changed) - return; - switch (Path.GetExtension(e.FullPath.ToLower())) - { - case ".json": - case ".yaml": - case ".yml": - break; - default: - return; - } - await this.ReadAndCreateWorkflowAsync(e.FullPath, false); - } - - /// - /// Reads the from the specified file and creates a new , if it does not already exist - /// - /// The path to the file to read - /// A boolean indicating to only import read and create a new if it already exists - /// A new awaitable - protected virtual async Task ReadAndCreateWorkflowAsync(string filePath, bool ifNotExists) - { - try - { - using var stream = File.OpenRead(filePath); - var definition = await this.WorkflowReader.ReadAsync(stream); - using var scope = this.ServiceProvider.CreateScope(); - await scope.ServiceProvider.GetRequiredService().ExecuteAndUnwrapAsync(new V1CreateWorkflowCommand(definition, ifNotExists), this.CancellationTokenSource.Token); - } - catch (IOException ex) when (ex.HResult == -2147024864) { } - catch (Exception ex) - { - this.Logger.LogError("An error occured while reading a valid Serverless Workflow definition from the specified file '{filePath}': {ex}", filePath, ex.ToString()); - return; - } - } - - /// - public override void Dispose() - { - this.CancellationTokenSource?.Dispose(); - this.FileSystemWatcher?.Dispose(); - base.Dispose(); - GC.SuppressFinalize(this); - } - -} - diff --git a/src/core/Synapse.Application/Services/WorkflowProcessBase.cs b/src/core/Synapse.Application/Services/WorkflowProcessBase.cs deleted file mode 100644 index 1f36bf35f..000000000 --- a/src/core/Synapse.Application/Services/WorkflowProcessBase.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Services -{ - - ///

- /// Represents a base class for all implementations of the interface - /// - public abstract class WorkflowProcessBase - : IWorkflowProcess - { - - /// - public event EventHandler? Exited; - /// - public event EventHandler? Disposed; - - private bool _Disposed; - - /// - public abstract string Id { get; } - - /// - public ProcessStatus Status { get; protected set; } - - /// - public abstract IObservable Logs { get; } - - /// - public abstract long? ExitCode { get; } - - /// - public abstract ValueTask StartAsync(CancellationToken cancellationToken = default); - - /// - public abstract ValueTask TerminateAsync(CancellationToken cancellationToken = default); - - /// - /// Handles the 's having exited - /// - protected virtual void OnExited() - { - this.Status = ProcessStatus.Exited; - this.Exited?.Invoke(this, new()); - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - public virtual ValueTask DisposeAsync(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.Disposed?.Invoke(this, new()); - } - this._Disposed = true; - } - return ValueTask.CompletedTask; - } - - /// - public ValueTask DisposeAsync() - { - return this.DisposeAsync(true); - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.Disposed?.Invoke(this, new()); - } - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/WorkflowProcessManager.cs b/src/core/Synapse.Application/Services/WorkflowProcessManager.cs deleted file mode 100644 index 907ace6f2..000000000 --- a/src/core/Synapse.Application/Services/WorkflowProcessManager.cs +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Collections.Concurrent; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class WorkflowProcessManager - : BackgroundService, IWorkflowProcessManager - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to perform logging - /// The used to create es - public WorkflowProcessManager(IServiceProvider serviceProvider, ILogger logger, IWorkflowRuntime runtime) - { - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.Runtime = runtime; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the used to create es - /// - protected IWorkflowRuntime Runtime { get; } - - /// - /// Gets a containing all available es - /// - protected ConcurrentDictionary Processes { get; } = new(); - - /// - /// Gets the 's - /// - protected CancellationTokenSource CancellationTokenSource { get; private set; } = null!; - - /// - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); - return Task.CompletedTask; - } - - /// - public virtual async Task StartProcessAsync(V1Workflow workflow, V1WorkflowInstance workflowInstance, CancellationToken cancellationToken = default) - { - if (workflow == null) - throw new ArgumentNullException(nameof(workflow)); - if (workflowInstance == null) - throw new ArgumentNullException(nameof(workflowInstance)); - var process = await this.Runtime.CreateProcessAsync(workflow, workflowInstance, cancellationToken); - process.Logs.SubscribeAsync(async log => await this.OnProcessLogAsync(process, log)); - process.Exited += (sender, e) => this.OnProcessExitedAsync((IWorkflowProcess)sender!); - process.Disposed += (sender, e) => this.OnProcessDisposed((IWorkflowProcess)sender!); - this.Processes.AddOrUpdate(process.Id, process, (key, existing) => throw new DuplicateWaitObjectException(nameof(key))); - using var scope = this.ServiceProvider.CreateScope(); - var processStates = scope.ServiceProvider.GetRequiredService>(); - await processStates.AddAsync(new(process.Id), this.CancellationTokenSource.Token); - await processStates.SaveChangesAsync(cancellationToken); - await process.StartAsync(cancellationToken); - return process; - } - - /// - public virtual IWorkflowProcess GetProcessById(string id) - { - if (string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - if (!this.Processes.TryGetValue(id, out var process)) - throw new NullReferenceException($"Failed to find a process with the specified id '{id}'"); - return process; - } - - /// - /// Handles logs generated by the specified - /// - /// The that produced the log - /// The log that has been produced - /// A new awaitable - protected virtual async Task OnProcessLogAsync(IWorkflowProcess process, string log) - { - using var scope = this.ServiceProvider.CreateScope(); - var processStates = scope.ServiceProvider.GetRequiredService>(); - while (true) - { - try - { - var processState = await processStates.FindAsync(process.Id); - processState.AppendLog(log); - await processStates.UpdateAsync(processState, this.CancellationTokenSource.Token); - await processStates.SaveChangesAsync(this.CancellationTokenSource.Token); - break; - } - catch (OptimisticConcurrencyException ex) - { - this.Logger.LogWarning("An optimistic concurrency exception has occured while the logs of the process with id '{processId}'. Retrying with latest state: {ex}", process.Id, ex); - continue; - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while processing the logs of the process with id '{processId}': {ex}", process.Id, ex); - } - } - } - - /// - /// Handles the exit of the specified - /// - /// The that has exited - protected virtual async void OnProcessExitedAsync(IWorkflowProcess process) - { - using var scope = this.ServiceProvider.CreateScope(); - var processStates = scope.ServiceProvider.GetRequiredService>(); - while (true) - { - try - { - var processState = await processStates.FindAsync(process.Id); - processState.Exit(process.ExitCode!.Value); - await processStates.UpdateAsync(processState, this.CancellationTokenSource.Token); - await processStates.SaveChangesAsync(this.CancellationTokenSource.Token); - await process.DisposeAsync(); - break; - } - catch (OptimisticConcurrencyException ex) - { - this.Logger.LogWarning("An optimistic concurrency exception has occured while updating the process with id '{processId}'. Retrying with latest state: {ex}", process.Id, ex); - continue; - } - catch (Exception ex) - { - this.Logger.LogError("An error occured while updating the process with id '{processId}': {ex}", process.Id, ex); - } - } - - } - - /// - /// handles the disposal of the specified - /// - /// The to handle the disposal of - protected virtual void OnProcessDisposed(IWorkflowProcess process) - { - this.Processes.Remove(process.Id, out _); - } - - /// - public override void Dispose() - { - this.CancellationTokenSource?.Dispose(); - foreach(var process in this.Processes.Values.ToList()) - { - process.Dispose(); - } - base.Dispose(); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/WorkflowRuntimeBase.cs b/src/core/Synapse.Application/Services/WorkflowRuntimeBase.cs deleted file mode 100644 index f70557fd6..000000000 --- a/src/core/Synapse.Application/Services/WorkflowRuntimeBase.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the base class for all implementations - /// - public abstract class WorkflowRuntimeBase - : BackgroundService, IWorkflowRuntime - { - - /// - /// Initializes a new - /// - /// The service used to create s - protected WorkflowRuntimeBase(ILoggerFactory loggerFactory) - { - this.Logger = loggerFactory.CreateLogger(this.GetType()); - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - public abstract Task CreateProcessAsync(V1Workflow workflow, V1WorkflowInstance workflowInstance, CancellationToken cancellationToken = default); - - /// - async ValueTask IAsyncDisposable.DisposeAsync() - { - await this.DisposeAsync(); - GC.SuppressFinalize(this); - } - - /// - /// Disposes of the - /// - /// A new awaitable - protected virtual ValueTask DisposeAsync() - { - return ValueTask.CompletedTask; - } - - } - -} diff --git a/src/core/Synapse.Application/Services/WorkflowRuntimeProxy.cs b/src/core/Synapse.Application/Services/WorkflowRuntimeProxy.cs deleted file mode 100644 index 1b8583823..000000000 --- a/src/core/Synapse.Application/Services/WorkflowRuntimeProxy.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Serialization; -using Synapse.Apis.Runtime; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation of the - /// - public class WorkflowRuntimeProxy - : IWorkflowRuntimeProxy - { - - /// - public event EventHandler? Disposed; - - private bool _Disposed; - - /// - /// Initializes a new - /// - /// The service used to perform logging - /// The service used to mediate calls - /// The service used to map objects - /// The 's id, which is the same than the id of the executed workflow instance - /// The 's stream - public WorkflowRuntimeProxy(ILogger logger, IMediator mediator, IMapper mapper, string id, IAsyncStreamWriter signalStream) - { - this.Logger = logger; - this.Mediator = mediator ?? throw new ArgumentNullException(nameof(mediator)); - this.Mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - if(string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - this.Id = id; - this.SignalStream = signalStream ?? throw new ArgumentNullException(nameof(signalStream)); - } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to mediate calls - /// - protected IMediator Mediator { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - /// - public string Id { get; } - - /// - /// Gets the 's connection - /// - protected IAsyncStreamWriter SignalStream { get; } - - /// - public virtual async Task CorrelateAsync(V1CorrelationContext context, CancellationToken cancellationToken = default) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - await this.SignalStream.WriteAsync(new(V1RuntimeSignalType.Correlate, Dynamic.FromObject(context)), cancellationToken); - } - - /// - public virtual async Task SuspendAsync(CancellationToken cancellationToken = default) - { - await this.SignalStream.WriteAsync(new(V1RuntimeSignalType.Suspend), cancellationToken); - } - - /// - public virtual async Task CancelAsync(CancellationToken cancellationToken = default) - { - await this.SignalStream.WriteAsync(new(V1RuntimeSignalType.Cancel), cancellationToken); - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - this.Disposed?.Invoke(this, new()); - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/WorkflowRuntimeProxyFactory.cs b/src/core/Synapse.Application/Services/WorkflowRuntimeProxyFactory.cs deleted file mode 100644 index b40d6c71b..000000000 --- a/src/core/Synapse.Application/Services/WorkflowRuntimeProxyFactory.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Apis.Runtime; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class WorkflowRuntimeProxyFactory - : IWorkflowRuntimeProxyFactory - { - - /// - /// Initializes a new - /// - /// The current - public WorkflowRuntimeProxyFactory(IServiceProvider serviceProvider) - { - this.ServiceProvider = serviceProvider; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - public virtual IWorkflowRuntimeProxy CreateProxy(string id, IAsyncStreamWriter signalStream) - { - if(string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return ActivatorUtilities.CreateInstance(this.ServiceProvider, id, signalStream); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/WorkflowRuntimeProxyManager.cs b/src/core/Synapse.Application/Services/WorkflowRuntimeProxyManager.cs deleted file mode 100644 index 477d0e5f6..000000000 --- a/src/core/Synapse.Application/Services/WorkflowRuntimeProxyManager.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Collections.Concurrent; - -namespace Synapse.Application.Services -{ - ///

- /// Represents the default implementation of the interface - /// - public class WorkflowRuntimeProxyManager - : IWorkflowRuntimeProxyManager - { - - /// - /// Gets a containing the workflow id mappings of all active instances - /// - protected ConcurrentDictionary RuntimeProxies { get; } = new(); - - /// - public virtual IWorkflowRuntimeProxy Register(IWorkflowRuntimeProxy runtimeProxy) - { - if(runtimeProxy == null) - throw new ArgumentNullException(nameof(runtimeProxy)); - this.RuntimeProxies.AddOrUpdate(runtimeProxy.Id, runtimeProxy, (key, current) => - { - current.Dispose(); - return runtimeProxy; - }); - runtimeProxy.Disposed += this.OnRuntimeProxyDisposed; - return runtimeProxy; - } - - /// - public virtual IWorkflowRuntimeProxy GetProxy(string id) - { - if(string.IsNullOrWhiteSpace(id)) - throw new ArgumentNullException(nameof(id)); - return this.RuntimeProxies[id]; - } - - /// - public virtual void Unregister(IWorkflowRuntimeProxy runtimeProxy) - { - if (runtimeProxy == null) - throw new ArgumentNullException(nameof(runtimeProxy)); - runtimeProxy.Dispose(); - runtimeProxy.Disposed -= this.OnRuntimeProxyDisposed; - } - - /// - /// Handles the disposal of the specified - /// - /// The disposed - /// The disposal - protected virtual void OnRuntimeProxyDisposed(object? sender, EventArgs e) - { - var runtimeProxy = (IWorkflowRuntimeProxy)sender!; - this.RuntimeProxies.TryRemove(runtimeProxy.Id, out _); - } - - } - -} diff --git a/src/core/Synapse.Application/Services/WorkflowScheduler.cs b/src/core/Synapse.Application/Services/WorkflowScheduler.cs deleted file mode 100644 index d0a93b15e..000000000 --- a/src/core/Synapse.Application/Services/WorkflowScheduler.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure.Plugins; - -namespace Synapse.Application.Services -{ - - ///

- /// Represents a used to schedule all CRON-based s at startup - /// - public class WorkflowScheduler - : BackgroundService - { - - /// - /// Initializes a new - /// - /// The current - /// The service used to perform logging - /// The service used to manage s - /// The service used to manage background jobs - public WorkflowScheduler(IServiceProvider serviceProvider, ILogger logger, IPluginManager pluginManager, IBackgroundJobManager backgroundJobManager) - { - this.ServiceProvider = serviceProvider; - this.Logger = logger; - this.PluginManager = pluginManager; - this.BackgroundJobManager = backgroundJobManager; - } - - /// - /// Gets the current - /// - protected IServiceProvider ServiceProvider { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to manage s - /// - protected IPluginManager PluginManager { get; } - - /// - /// Gets the service used to manage background jobs - /// - protected IBackgroundJobManager BackgroundJobManager { get; } - - /// - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - await this.PluginManager.WaitForStartupAsync(stoppingToken); - using var scope = this.ServiceProvider.CreateScope(); - var mediator = scope.ServiceProvider.GetRequiredService(); - var schedules = scope.ServiceProvider.GetRequiredService>(); - foreach(var schedule in schedules.AsQueryable() - .Where(s => s.Status == V1ScheduleStatus.Active) - .ToList()) - { - await this.BackgroundJobManager.ScheduleJobAsync(schedule, stoppingToken); - } - } - - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Application/Synapse.Application.csproj b/src/core/Synapse.Application/Synapse.Application.csproj deleted file mode 100644 index c289156ee..000000000 --- a/src/core/Synapse.Application/Synapse.Application.csproj +++ /dev/null @@ -1,36 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse application - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - Library - embedded - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/core/Synapse.Application/Utilities/TarGzPackage.cs b/src/core/Synapse.Application/Utilities/TarGzPackage.cs deleted file mode 100644 index 8eafb9894..000000000 --- a/src/core/Synapse.Application/Utilities/TarGzPackage.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.IO.Compression; -using System.Text; - -namespace Synapse -{ - - /// - /// Defines helpers for .tar.gz packages - /// - /// Code taken from under license ALTERNATIVE B - Public Domain (www.unlicense.org) - public static class TarGzPackage - { - - /// - /// Extracts the specified .tar.gz file to the specified output directory - /// - /// The path to the .tar.gz file to extract - /// The directory to extract the specified file to - /// A - /// A new awaitable - public static async Task ExtractToDirectoryAsync(string filePath, string outputDirectory, CancellationToken cancellationToken = default) - { - if(string.IsNullOrEmpty(filePath)) - throw new ArgumentNullException(nameof(filePath)); - if(string.IsNullOrWhiteSpace(outputDirectory)) - throw new ArgumentNullException(nameof(outputDirectory)); - if(!Directory.Exists(outputDirectory)) - Directory.CreateDirectory(outputDirectory); - var file = new FileInfo(filePath); - if (!file.Exists) - throw new FileNotFoundException($"Failed to find the specified .tar.gz file", filePath); - using var stream = file.OpenRead(); - await ExtractToDirectoryAsync(stream, outputDirectory, cancellationToken); - } - - /// - /// Extracts the specified .tar.gz to the specified output directory - /// - /// The the .tar.gz to extract - /// The directory to extract the specified to - /// A - /// A new awaitable - public static async Task ExtractToDirectoryAsync(Stream stream, string outputDirectory, CancellationToken cancellationToken = default) - { - if(stream == null) - throw new ArgumentNullException(nameof(stream)); - if (string.IsNullOrWhiteSpace(outputDirectory)) - throw new ArgumentNullException(nameof(outputDirectory)); - if (!Directory.Exists(outputDirectory)) - Directory.CreateDirectory(outputDirectory); - using var gzip = new GZipStream(stream, CompressionMode.Decompress); - const int chunk = 4096; - using var memoryStream = new MemoryStream(); - int read; - var buffer = new byte[chunk]; - while ((read = await gzip.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) - { - await memoryStream.WriteAsync(buffer, 0, read, cancellationToken); - } - memoryStream.Seek(0, SeekOrigin.Begin); - await ExtractTarToDirectoryAsync(memoryStream, outputDirectory, cancellationToken); - } - - private static async Task ExtractTarToDirectoryAsync(Stream stream, string outputDirectory, CancellationToken cancellationToken) - { - var inBuffer = new byte[100]; - while (true) - { - await stream.ReadAsync(inBuffer, 0, 100, cancellationToken); - var name = Encoding.ASCII.GetString(inBuffer).Trim('\0'); - if (string.IsNullOrWhiteSpace(name)) - break; - stream.Seek(24, SeekOrigin.Current); - await stream.ReadAsync(inBuffer, 0, 12, cancellationToken); - var size = Convert.ToInt64(Encoding.UTF8.GetString(inBuffer, 0, 12).Trim('\0').Trim(), 8); - stream.Seek(376L, SeekOrigin.Current); - var output = Path.Combine(outputDirectory, name); - if (!Directory.Exists(Path.GetDirectoryName(output))) - Directory.CreateDirectory(Path.GetDirectoryName(output)!); - if (!name.EndsWith("/", StringComparison.InvariantCulture)) - { - using (var outputFileStream = File.Open(output, FileMode.OpenOrCreate, FileAccess.Write)) - { - var outBuffer = new byte[size]; - await stream.ReadAsync(outBuffer, 0, outBuffer.Length, cancellationToken); - await outputFileStream.WriteAsync(outBuffer, 0, outBuffer.Length, cancellationToken); - } - } - var pos = stream.Position; - var offset = 512 - (pos % 512); - if (offset == 512) - offset = 0; - stream.Seek(offset, SeekOrigin.Current); - } - } - - } - -} diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Configuration/DockerContainerPlatformOptions.cs b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Configuration/DockerContainerPlatformOptions.cs new file mode 100644 index 000000000..d92cb0d08 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Configuration/DockerContainerPlatformOptions.cs @@ -0,0 +1,27 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Core.Infrastructure.Containers.Configuration; + +/// +/// Represents the options used to configure the +/// +public class DockerContainerPlatformOptions +{ + + /// + /// Gets/sets the Docker runtime host's network name + /// + public virtual string Network { get; set; } = "synapse"; + +} diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs b/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs new file mode 100644 index 000000000..74988c85f --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainer.cs @@ -0,0 +1,106 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Core.Infrastructure.Services; + +namespace Synapse.Core.Infrastructure.Containers; + +/// +/// Represents a Docker +/// +/// The container's ID +/// The service used to interact with the Docker API +public class DockerContainer(string id, IDockerClient dockerClient) + : IContainer +{ + + bool _disposed; + + /// + /// Gets the container's ID + /// + protected virtual string Id { get; } = id; + + /// + /// Gets the service used to interact with the Docker API + /// + protected virtual IDockerClient DockerClient { get; } = dockerClient; + + /// + public virtual StreamReader? StandardOutput { get; protected set; } + + /// + public virtual StreamReader? StandardError { get; protected set; } + + /// + public long? ExitCode { get; protected set; } + + /// + public virtual async Task StartAsync(CancellationToken cancellationToken = default) + { + await this.DockerClient.Containers.StartContainerAsync(this.Id, new() { }, cancellationToken).ConfigureAwait(false); +#pragma warning disable CS0618 // Type or member is obsolete + var standardOutputStream = await this.DockerClient.Containers.GetContainerLogsAsync(this.Id, new() { Follow = true, ShowStdout = true, ShowStderr = true, Timestamps = false }, cancellationToken).ConfigureAwait(false); + var standardErrorStream = await this.DockerClient.Containers.GetContainerLogsAsync(this.Id, new() { Follow = true, ShowStdout = false, ShowStderr = true, Timestamps = false }, cancellationToken).ConfigureAwait(false); +#pragma warning restore CS0618 // Type or member is obsolete + this.StandardOutput = new(standardOutputStream); + this.StandardError = new(standardErrorStream); + } + + /// + public virtual async Task WaitForExitAsync(CancellationToken cancellationToken = default) + { + var response = await this.DockerClient.Containers.WaitContainerAsync(this.Id, cancellationToken).ConfigureAwait(false); + this.ExitCode = response.StatusCode; + } + + /// + public virtual Task StopAsync(CancellationToken cancellationToken = default) => this.DockerClient.Containers.StopContainerAsync(this.Id, new() { }, cancellationToken); + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + /// A new awaitable + protected virtual async ValueTask DisposeAsync(bool disposing) + { + if (!this._disposed) return; + this._disposed = true; + await Task.CompletedTask.ConfigureAwait(false); + } + + /// + public async ValueTask DisposeAsync() + { + await this.DisposeAsync(true).ConfigureAwait(false); + GC.SuppressFinalize(this); + } + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + protected virtual void Dispose(bool disposing) + { + if (!this._disposed) return; + this._disposed = true; + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainerPlatform.cs b/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainerPlatform.cs new file mode 100644 index 000000000..d625f8f27 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/DockerContainerPlatform.cs @@ -0,0 +1,111 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Synapse.Core.Infrastructure.Containers.Configuration; +using Synapse.Core.Infrastructure.Services; + +namespace Synapse.Core.Infrastructure.Containers; + +/// +/// Represents the Docker implementation of the interface +/// +/// The service used to perform logging +/// The current +/// The service used to interact with the Docker API +/// The current +public class DockerContainerPlatform(ILogger logger, IHostEnvironment hostEnvironment, IDockerClient dockerClient, IOptions options) + : IHostedService, IContainerPlatform +{ + + /// + /// Gets the service used to perform logging + /// + protected ILogger Logger { get; } = logger; + + /// + /// Gets the current + /// + protected IHostEnvironment Environment { get; } = hostEnvironment; + + /// + /// Gets the service used to interact with the Docker API + /// + protected IDockerClient DockerClient { get; } = dockerClient; + + /// + /// Gets the current + /// + protected DockerContainerPlatformOptions Options { get; } = options.Value; + + /// + public virtual async Task StartAsync(CancellationToken cancellationToken) + { + if (!this.Environment.RunsInDocker()) return; + var containerShortId = System.Environment.MachineName; + var containerId = (await this.DockerClient.Containers.InspectContainerAsync(containerShortId, cancellationToken)).ID; + var response = null as NetworkResponse; + try + { + response = await this.DockerClient.Networks.InspectNetworkAsync(this.Options.Network, cancellationToken); + } + catch (DockerNetworkNotFoundException) + { + await this.DockerClient.Networks.CreateNetworkAsync(new() { Name = this.Options.Network }, cancellationToken); + } + finally + { + if (response == null || !response!.Containers.ContainsKey(containerId)) + await this.DockerClient.Networks.ConnectNetworkAsync(this.Options.Network, new NetworkConnectParameters() { Container = containerId }, cancellationToken); + } + } + + /// + public virtual async Task CreateAsync(ContainerProcessDefinition definition, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(definition); + try + { + await this.DockerClient.Images.InspectImageAsync(definition.Image, cancellationToken).ConfigureAwait(false); + } + catch (DockerApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound) + { + var downloadProgress = new Progress(); + await this.DockerClient.Images.CreateImageAsync(new() { FromImage = definition.Image }, new(), downloadProgress, cancellationToken).ConfigureAwait(false); + } + var parameters = new CreateContainerParameters() + { + Image = definition.Image, + Cmd = string.IsNullOrWhiteSpace(definition.Command) ? null : ["/bin/sh", "-c", definition.Command], + Env = definition.Environment?.Select(e => $"{e.Key}={e.Value}").ToList(), + HostConfig = new() + { + PortBindings = definition.Ports?.ToDictionary(kvp => kvp.Value.ToString(), kvp => (IList)[new PortBinding() { HostPort = kvp.Key.ToString() }]), + Binds = definition.Volumes?.Select(e => $"{e.Key}={e.Value}")?.ToList() ?? [] + } + }; + var response = await this.DockerClient.Containers.CreateContainerAsync(parameters, cancellationToken).ConfigureAwait(false); + if (this.Environment.RunsInDocker()) await this.DockerClient.Networks.ConnectNetworkAsync(this.Options.Network, new NetworkConnectParameters() { Container = response.ID }, cancellationToken); + foreach (var warning in response.Warnings) + { + this.Logger.LogWarning(warning); + } + return new DockerContainer(response.ID, this.DockerClient); + } + + /// + public virtual Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + +} diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerServiceCollectionExtensions.cs b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerServiceCollectionExtensions.cs new file mode 100644 index 000000000..bed79fb8f --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Extensions/DockerContainerServiceCollectionExtensions.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Core.Infrastructure.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; + +namespace Synapse.Core.Infrastructure.Containers; + +/// +/// Defines extensions for s +/// +public static class DockerContainerServiceCollectionExtensions +{ + + /// + /// Adds and configures a new + /// + /// The to configure + /// The used to configure the to use, if any + /// The configured + public static IServiceCollection AddDockerContainerPlatform(this IServiceCollection services, DockerClientConfiguration? dockerClientConfiguration = null) + { + dockerClientConfiguration ??= new DockerClientConfiguration(); + services.TryAddSingleton(dockerClientConfiguration.CreateClient()); + services.TryAddSingleton(); + services.AddSingleton(provider => provider.GetRequiredService()); + services.AddSingleton(provider => provider.GetRequiredService()); + return services; + } + +} diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Synapse.Core.Infrastructure.Containers.Docker.csproj b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Synapse.Core.Infrastructure.Containers.Docker.csproj new file mode 100644 index 000000000..9c7e08003 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Synapse.Core.Infrastructure.Containers.Docker.csproj @@ -0,0 +1,39 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse core infrastructure containers docker + true + true + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + + + + + + + + + + + + diff --git a/src/core/Synapse.Core.Infrastructure.Containers.Docker/Usings.cs b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Usings.cs new file mode 100644 index 000000000..dd6825338 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure.Containers.Docker/Usings.cs @@ -0,0 +1,17 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using ServerlessWorkflow.Sdk.Models.Processes; +global using Docker.DotNet; +global using Docker.DotNet.Models; +global using System.Net; diff --git a/src/core/Synapse.Core.Infrastructure/AsyncLock.cs b/src/core/Synapse.Core.Infrastructure/AsyncLock.cs new file mode 100644 index 000000000..8b6f90815 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/AsyncLock.cs @@ -0,0 +1,55 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Represents an object used to lock asynchronous processes +/// +/// Code based on +public class AsyncLock +{ + + readonly SemaphoreSlim _semaphore = new(1, 2); + readonly Task _releaser; + + /// + /// Initializes a new + /// + public AsyncLock() + { + this._releaser = Task.FromResult((IDisposable)new Releaser(this)); + } + + /// + /// Locks asynchronously + /// + /// A + /// A new object which releases the lock upon disposal + public Task LockAsync(CancellationToken cancellationToken = default) + { + var waitTask = this._semaphore.WaitAsync(cancellationToken); + return waitTask.IsCompleted + ? this._releaser + : waitTask.ContinueWith((_, state) => (IDisposable)state!, this._releaser.Result, cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + } + + class Releaser(AsyncLock toRelease) + : IDisposable + { + + public void Dispose() => toRelease._semaphore.Release(); + + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/AuthorizationInfo.cs b/src/core/Synapse.Core.Infrastructure/AuthorizationInfo.cs new file mode 100644 index 000000000..50609930f --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/AuthorizationInfo.cs @@ -0,0 +1,78 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Core.Infrastructure.Services; +using ServerlessWorkflow.Sdk; +using Microsoft.Extensions.DependencyInjection; +using System.Text; + +namespace Synapse; + +/// +/// Describes the credentials used to authenticate a user agent with an application +/// +/// The authorization scheme +/// The authorization parameter +public class AuthorizationInfo(string scheme, string parameter) +{ + + /// + /// Gets the authorization scheme + /// + public virtual string Scheme { get; set; } = scheme; + + /// + /// Gets the authorization parameter + /// + public virtual string Parameter { get; set; } = parameter; + + /// + public override string ToString() => $"{Scheme} {Parameter}"; + + /// + /// Creates a new based on the specified + /// + /// The to create a new for + /// The current + /// A + /// A new based on the specified + public static async Task CreateAsync(AuthenticationPolicyDefinition authentication, IServiceProvider serviceProvider, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(nameof(authentication)); + ArgumentNullException.ThrowIfNull(nameof(serviceProvider)); + string scheme, parameter; + switch (authentication.Scheme) + { + case AuthenticationScheme.Basic: + if (authentication.Basic == null) throw new Exception("Missing or invalid configuration of the specified authentication scheme"); + scheme = AuthenticationScheme.Basic; + parameter = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{authentication.Basic.Username}:{authentication.Basic.Password}")); + break; + case AuthenticationScheme.Bearer: + if (authentication.Bearer == null) throw new Exception("Missing or invalid configuration of the specified authentication scheme"); + scheme = AuthenticationScheme.Basic; + parameter = authentication.Bearer.Token; + break; + case AuthenticationScheme.OAuth2: + if (authentication.OAuth2 == null) throw new Exception("Missing or invalid configuration of the specified authentication scheme"); + scheme = AuthenticationScheme.Bearer; + var token = await serviceProvider.GetRequiredService().GetTokenAsync(authentication.OAuth2, cancellationToken).ConfigureAwait(false) ?? throw new NullReferenceException($"Failed to generate an OAUTH2 token"); + parameter = token.AccessToken!; + break; + default: + throw new NotSupportedException($"The specified authentication schema '{authentication.Scheme}' is not supported"); + } + return new(scheme, parameter); + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/Extensions/HttpClientExtensions.cs b/src/core/Synapse.Core.Infrastructure/Extensions/HttpClientExtensions.cs new file mode 100644 index 000000000..3525454b6 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Extensions/HttpClientExtensions.cs @@ -0,0 +1,39 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Net.Http.Headers; + +namespace Synapse; + +/// +/// Defines extensions for s +/// +public static class HttpClientExtensions +{ + + /// + /// Configures the to use the specified authentication mechanism + /// + /// The to configure + /// An object that describes the authentication mechanism to use + /// The current + /// A + /// A new awaitable + public static async Task ConfigureAuthenticationAsync(this HttpClient httpClient, AuthenticationPolicyDefinition? authentication, IServiceProvider serviceProvider, CancellationToken cancellationToken = default) + { + if (authentication == null) return; + var authorization = await AuthorizationInfo.CreateAsync(authentication, serviceProvider, cancellationToken).ConfigureAwait(false); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authorization.Scheme, authorization.Parameter); + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/Extensions/IHostEnvironmentExtensions.cs b/src/core/Synapse.Core.Infrastructure/Extensions/IHostEnvironmentExtensions.cs new file mode 100644 index 000000000..9d338e5f4 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Extensions/IHostEnvironmentExtensions.cs @@ -0,0 +1,38 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Hosting; + +namespace Synapse; + +/// +/// Defines extensions for s +/// +public static class IHostEnvironmentExtensions +{ + + /// + /// Determines whether or not the runs in Docker + /// + /// The to check + /// A boolean indicating whether or not the runs in Docker + public static bool RunsInDocker(this IHostEnvironment env) => File.Exists("/.dockerenv"); + + /// + /// Determines whether or not the runs in Kubernetes + /// + /// The to check + /// A boolean indicating whether or not the runs in Kubernetes + public static bool RunsInKubernetes(this IHostEnvironment env) => !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST")); + +} \ No newline at end of file diff --git a/src/core/Synapse.Core.Infrastructure/Extensions/IServiceCollectionExtensions.cs b/src/core/Synapse.Core.Infrastructure/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 000000000..c983071eb --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,77 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Neuroglia.Data.Infrastructure; +using Neuroglia.Data.Infrastructure.Redis.Services; +using Neuroglia.Data.Infrastructure.ResourceOriented.Redis; +using Neuroglia.Data.Infrastructure.ResourceOriented.Services; +using Neuroglia.Data.Infrastructure.Services; +using Neuroglia.Data.PatchModel.Services; +using Neuroglia.Mediation; +using Neuroglia.Plugins; +using Neuroglia.Security.Services; +using Neuroglia.Serialization; +using ServerlessWorkflow.Sdk.IO; +using Synapse.Core.Infrastructure.Services; +using Synapse.Resources; + +namespace Synapse; + +/// +/// Defines extensions for s +/// +public static class IServiceCollectionExtensions +{ + + /// + /// Adds and configures runtime infrastructure services + /// + /// The to configure + /// The current + /// The configured + public static IServiceCollection AddSynapse(this IServiceCollection services, IConfiguration configuration) + { + services.AddHttpClient(); + services.AddSerialization(); + services.AddJsonSerializer(); + services.AddYamlDotNetSerializer(); + services.AddMediator(); + services.AddScoped(); + services.AddServerlessWorkflowIO(); + services.AddPluginProvider(); + + var redisConnectionString = configuration.GetConnectionString(RedisDatabase.ConnectionStringName); + services.AddPlugin(typeof(IDatabase), string.IsNullOrWhiteSpace(redisConnectionString) ? null : provider => provider.GetRequiredService(), serviceLifetime: ServiceLifetime.Singleton); + + if (!string.IsNullOrWhiteSpace(redisConnectionString)) services.AddRedisDatabase(redisConnectionString, ServiceLifetime.Singleton); + services.AddHostedService(); + + services.AddPlugin(typeof(IRepository), provider => provider.GetRequiredService>(), serviceLifetime: ServiceLifetime.Scoped); + services.AddRedisRepository(lifetime: ServiceLifetime.Scoped); + services.AddSingleton, RedisTextDocumentRepository>(); + services.AddSingleton(provider => provider.GetRequiredService>()); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + return services; + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/OAuth2Token.cs b/src/core/Synapse.Core.Infrastructure/OAuth2Token.cs new file mode 100644 index 000000000..4b42801eb --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/OAuth2Token.cs @@ -0,0 +1,74 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Runtime.Serialization; +using System.Text.Json.Serialization; +using YamlDotNet.Serialization; + +namespace Synapse; + +/// +/// Describes an OAUTH2 token +/// +[DataContract] +public record OAuth2Token +{ + + /// + /// Gets the UTC date and time at which the has been created + /// + public virtual DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// Gets the OAUTH2 token type + /// + [DataMember(Order = 1, Name = "token_type"), JsonPropertyName("token_type"), JsonPropertyOrder(1), YamlMember(Alias = "token_type", Order = 1)] + public virtual string? TokenType { get; set; } + + /// + /// Gets the OAUTH2 token id + /// + [DataMember(Order = 2, Name = "token_id"), JsonPropertyName("token_id"), JsonPropertyOrder(2), YamlMember(Alias = "token_id", Order = 2)] + public virtual string? TokenId { get; set; } + + /// + /// Gets the OAUTH2 access token + /// + [DataMember(Order = 3, Name = "access_token"), JsonPropertyName("access_token"), JsonPropertyOrder(3), YamlMember(Alias = "access_token", Order = 3)] + public virtual string? AccessToken { get; set; } + + /// + /// Gets the OAUTH2 refresh token + /// + [DataMember(Order = 4, Name = "refresh_token"), JsonPropertyName("refresh_token"), JsonPropertyOrder(4), YamlMember(Alias = "refresh_token", Order = 4)] + public virtual string? RefreshToken { get; set; } + + /// + /// Gets the Time To Live, in seconds + /// + [DataMember(Order = 5, Name = "expires_in"), JsonPropertyName("expires_in"), JsonPropertyOrder(5), YamlMember(Alias = "expires_in", Order = 5)] + public virtual int Ttl { get; set; } + + /// + /// Gets the UTC date and time at which the expires + /// + [DataMember(Order = 6, Name = "expires_on"), JsonPropertyName("expires_on"), JsonPropertyOrder(6), YamlMember(Alias = "expires_on", Order = 6)] + public virtual DateTime? ExpiresAt { get; set; } + + /// + /// Gets a boolean indicating whether or not the has expired + /// + [IgnoreDataMember, JsonIgnore, YamlIgnore] + public virtual bool HasExpired => this.ExpiresAt.HasValue ? DateTime.UtcNow > this.ExpiresAt : DateTime.UtcNow > this.CreatedAt.Add(TimeSpan.FromSeconds(this.Ttl)); + +} \ No newline at end of file diff --git a/src/core/Synapse.Core.Infrastructure/Services/ApplicationUserAccessor.cs b/src/core/Synapse.Core.Infrastructure/Services/ApplicationUserAccessor.cs new file mode 100644 index 000000000..64c17ecaa --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/ApplicationUserAccessor.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Security.Services; +using System.Security.Claims; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Represents an implementation used to access the user that represents the executing application +/// +public class ApplicationUserAccessor + : IUserAccessor +{ + + /// + public ClaimsPrincipal? User => new(new ClaimsIdentity()); + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/DatabaseInitializer.cs b/src/core/Synapse.Core.Infrastructure/Services/DatabaseInitializer.cs new file mode 100644 index 000000000..267cad1c5 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/DatabaseInitializer.cs @@ -0,0 +1,56 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Logging; +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Synapse.Resources; +using System.Security.Cryptography; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Represents the service used to initialize the Synapse resource database +/// +/// +public class DatabaseInitializer(ILoggerFactory loggerFactory, IServiceProvider serviceProvider) + : Neuroglia.Data.Infrastructure.ResourceOriented.Services.DatabaseInitializer(loggerFactory, serviceProvider) +{ + + /// + protected override async Task SeedAsync(CancellationToken cancellationToken) + { + foreach (var definition in SynapseDefaults.Resources.Definitions.AsEnumerable()) await this.Database.CreateResourceAsync(definition, cancellationToken: cancellationToken).ConfigureAwait(false); + var keyBytes = new byte[64]; + using var rng = RandomNumberGenerator.Create(); + rng.GetBytes(keyBytes); + var key = Convert.ToBase64String(keyBytes); + await this.Database.CreateResourceAsync(new ServiceAccount() + { + Metadata = new() + { + Namespace = Namespace.DefaultNamespaceName, + Name = ServiceAccount.DefaultServiceAccountName + }, + Spec = new() + { + Key = key, + Claims = new Dictionary() + { + + } + } + }, false, cancellationToken).ConfigureAwait(false); + await this.Database.InitializeAsync(cancellationToken); + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/ExternalResourceProvider.cs b/src/core/Synapse.Core.Infrastructure/Services/ExternalResourceProvider.cs new file mode 100644 index 000000000..e71720111 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/ExternalResourceProvider.cs @@ -0,0 +1,62 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Represents the default implementation of the interface +/// +/// The current +/// The service used to create s +public class ExternalResourceProvider(IServiceProvider serviceProvider, IHttpClientFactory httpClientFactory) + : IExternalResourceProvider +{ + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider { get; } = serviceProvider; + + /// + /// Gets the service used to create s + /// + protected IHttpClientFactory HttpClientFactory { get; } = httpClientFactory; + + /// + public virtual async Task ReadAsync(WorkflowDefinition workflow, ExternalResourceDefinition resource, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(workflow); + ArgumentNullException.ThrowIfNull(resource); + return resource.EndpointUri.Scheme switch + { + "file" => new FileStream(resource.EndpointUri.LocalPath, FileMode.Open), + "http" or "https" => await this.ReadOverHttpAsync(workflow, resource, cancellationToken).ConfigureAwait(false), + _ => throw new NotSupportedException($"Cannot retrieve resource at uri '{resource.EndpointUri}': the scheme '{resource.EndpointUri.Scheme}' is not supported") + }; + } + + /// + /// Reads the specified + /// + /// The in the context of which to read the specified resource + /// A reference to the external resource to read + /// A + /// The specified 's content + protected virtual async Task ReadOverHttpAsync(WorkflowDefinition workflow, ExternalResourceDefinition resource, CancellationToken cancellationToken = default) + { + using var httpClient = this.HttpClientFactory.CreateClient(); + await httpClient.ConfigureAuthenticationAsync(resource.Endpoint.Authentication, this.ServiceProvider, cancellationToken).ConfigureAwait(false); + return await httpClient.GetStreamAsync(resource.EndpointUri, cancellationToken).ConfigureAwait(false); + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainer.cs b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainer.cs new file mode 100644 index 000000000..8e6de93f1 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainer.cs @@ -0,0 +1,59 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Defines the fundamentals of a container +/// +public interface IContainer + : IDisposable, IAsyncDisposable +{ + + /// + /// Gets the container's standard output stream + /// + StreamReader? StandardOutput { get; } + + /// + /// Gets the container's standard error stream + /// + StreamReader? StandardError { get; } + + /// + /// Gets the container's exit code + /// + long? ExitCode { get; } + + /// + /// Starts the container + /// + /// A + /// A new awaitable + Task StartAsync(CancellationToken cancellationToken = default); + + /// + /// Waits for the container to exit + /// + /// A + /// A new awaitable + Task WaitForExitAsync(CancellationToken cancellationToken = default); + + /// + /// Stops the container + /// + /// A + /// A new awaitable + Task StopAsync(CancellationToken cancellationToken = default); + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainerPlatform.cs b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainerPlatform.cs new file mode 100644 index 000000000..4223995fd --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IContainerPlatform.cs @@ -0,0 +1,32 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using ServerlessWorkflow.Sdk.Models.Processes; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Defines the fundamentals of a service used to manage containers +/// +public interface IContainerPlatform +{ + + /// + /// Creates a new container based on the specified + /// + /// The that defines the container to create + /// A + /// A new + Task CreateAsync(ContainerProcessDefinition definition, CancellationToken cancellationToken = default); + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IExternalResourceProvider.cs b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IExternalResourceProvider.cs new file mode 100644 index 000000000..d7cdb6868 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IExternalResourceProvider.cs @@ -0,0 +1,31 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Defines the fundamentals of a service used to provide external resources +/// +public interface IExternalResourceProvider +{ + + /// + /// Reads the specified external resource + /// + /// The in the context of which to read the specified resource + /// The reference to the external resource to get + /// A + /// A used to read the external resource's contents + Task ReadAsync(WorkflowDefinition workflow, ExternalResourceDefinition resource, CancellationToken cancellationToken = default); + +} \ No newline at end of file diff --git a/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IOAuth2TokenManager.cs b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IOAuth2TokenManager.cs new file mode 100644 index 000000000..d80ab41db --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/IOAuth2TokenManager.cs @@ -0,0 +1,32 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using ServerlessWorkflow.Sdk.Models.Authentication; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Defines the fundamentals of a service used to manage s +/// +public interface IOAuth2TokenManager +{ + + /// + /// Gets an + /// + /// The configuration that defines how to generate the to get + /// A + /// An + Task GetTokenAsync(OAuth2AuthenticationSchemeDefinitionBase configuration, CancellationToken cancellationToken = default); + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/Interfaces/ISchemaHandler.cs b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/ISchemaHandler.cs new file mode 100644 index 000000000..e79b14848 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/ISchemaHandler.cs @@ -0,0 +1,40 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Defines the fundamentals of a service used to handle s +/// +public interface ISchemaHandler +{ + + /// + /// Determines whether or not the supports the specified schema format + /// + /// The format to check + /// A boolean indicating whether or not the supports the specified schema format + bool Supports(string format); + + /// + /// Validates an object against the specified schema + /// + /// The object to validate + /// The schema to validate the graph against + /// A + /// An object that describes the validation result + Task ValidateAsync(object graph, SchemaDefinition schema, CancellationToken cancellationToken = default); + +} \ No newline at end of file diff --git a/src/core/Synapse.Core.Infrastructure/Services/Interfaces/ISchemaHandlerProvider.cs b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/ISchemaHandlerProvider.cs new file mode 100644 index 000000000..5b702faa2 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/Interfaces/ISchemaHandlerProvider.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Defines the fundamentals of a service used to provide s +/// +public interface ISchemaHandlerProvider +{ + + /// + /// Gets the first registered that supports the specified schema format + /// + /// The schema format to get an for + /// The first registered , if any, that supports the specified schema format + ISchemaHandler? GetHandler(string format); + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/JsonSchemaHandler.cs b/src/core/Synapse.Core.Infrastructure/Services/JsonSchemaHandler.cs new file mode 100644 index 000000000..6404c3a17 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/JsonSchemaHandler.cs @@ -0,0 +1,62 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Json.Schema; +using Neuroglia; +using Neuroglia.Serialization; +using ServerlessWorkflow.Sdk; +using System.Net; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Represents the default implementation of the interface +/// +/// The service used to serialize/deserialize data to/from JSON +public class JsonSchemaHandler(IJsonSerializer serializer) + : ISchemaHandler +{ + + /// + /// Gets the service used to serialize/deserialize data to/from JSON + /// + protected IJsonSerializer Serializer { get; } = serializer; + + /// + public virtual bool Supports(string format) => format.Equals(SchemaFormat.Json, StringComparison.OrdinalIgnoreCase); + + /// + public virtual async Task ValidateAsync(object graph, SchemaDefinition schema, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(graph); + ArgumentNullException.ThrowIfNull(schema); + if (!this.Supports(schema.Format)) throw new NotSupportedException($"The specified schema format '{schema.Format}' is not supported in this context"); + var json = this.Serializer.SerializeToText(schema.Document); + var jsonSchema = JsonSchema.FromText(json); + var jsonDocument = this.Serializer.SerializeToDocument(graph)!; + var options = new EvaluationOptions() + { + OutputFormat = OutputFormat.List + }; + var results = jsonSchema.Evaluate(jsonDocument, options); + if (results.IsValid) return await Task.FromResult(new OperationResult((int)HttpStatusCode.OK)); + else return new OperationResult((int)HttpStatusCode.BadRequest, null, new Neuroglia.Error() + { + Type = ErrorType.Validation, + Title = ErrorTitle.Validation, + Status = ErrorStatus.Validation, + Errors = new(results.Details.Where(d => d.Errors != null).SelectMany(d => d.Errors!).GroupBy(e => e.Key).Select(e => new KeyValuePair(e.Key, e.Select(e => e.Value).ToArray()))) + }); + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/OAuth2TokenManager.cs b/src/core/Synapse.Core.Infrastructure/Services/OAuth2TokenManager.cs new file mode 100644 index 000000000..90c65af52 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/OAuth2TokenManager.cs @@ -0,0 +1,186 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using IdentityModel.Client; +using Microsoft.Extensions.Logging; +using Microsoft.IdentityModel.JsonWebTokens; +using Microsoft.IdentityModel.Tokens; +using Neuroglia.Serialization; +using ServerlessWorkflow.Sdk; +using ServerlessWorkflow.Sdk.Models.Authentication; +using System.Collections.Concurrent; +using System.Net.Mime; +using System.Security.Claims; +using System.Text; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Represents the default implementation of the interface +/// +/// The service used to perform logging +/// The service used to serialize/deserialize to/from JSON +/// The service used to perform HTTP requests +public class OAuth2TokenManager(ILogger logger, IJsonSerializer jsonSerializer, HttpClient httpClient) + : IOAuth2TokenManager +{ + + /// + /// Gets the service used to perform logging + /// + protected ILogger Logger { get; } = logger; + + /// + /// Gets the service used to serialize/deserialize to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the service used to perform HTTP requests + /// + protected HttpClient HttpClient { get; } = httpClient; + + /// + /// Gets a containing all active s + /// + protected ConcurrentDictionary Tokens { get; } = []; + + /// + public virtual async Task GetTokenAsync(OAuth2AuthenticationSchemeDefinitionBase configuration, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(configuration); + var tokenKey = $"{configuration.Client?.Id}@{configuration.Authority}"; + if (this.Tokens.TryGetValue(tokenKey, out var token) && token != null && !token.HasExpired) return token; + Uri tokenEndpoint; + if (configuration is OpenIDConnectSchemeDefinition) + { + var discoveryDocument = await this.HttpClient.GetDiscoveryDocumentAsync(configuration.Authority.OriginalString, cancellationToken).ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(discoveryDocument.TokenEndpoint)) throw new NullReferenceException("The token endpoint is not documented by the OIDC discovery document"); + tokenEndpoint = new(discoveryDocument.TokenEndpoint!); + } + else if (configuration is OAuth2AuthenticationSchemeDefinition oauth2) tokenEndpoint = oauth2.Endpoints.Token; + else throw new NotSupportedException($"The specified scheme type '{configuration.GetType().FullName}' is not supported in this context"); + var properties = new Dictionary() + { + { "grant_type", configuration.Grant } + }; + switch (configuration.Client?.Authentication) + { + case null: + if(!string.IsNullOrWhiteSpace(configuration.Client?.Id) && !string.IsNullOrWhiteSpace(configuration.Client?.Secret)) + { + properties["client_id"] = configuration.Client.Id!; + properties["client_secret"] = configuration.Client.Secret!; + } + break; + case OAuth2ClientAuthenticationMethod.Post: + this.ThrowIfInvalidClientCredentials(configuration.Client); + properties["client_id"] = configuration.Client.Id!; + properties["client_secret"] = configuration.Client.Secret!; + break; + case OAuth2ClientAuthenticationMethod.JwT: + this.ThrowIfInvalidClientCredentials(configuration.Client); + properties["client_assertion_type"] = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + properties["client_assertion"] = this.CreateClientAssertionJwt(configuration.Client.Id!, tokenEndpoint.OriginalString, new(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration.Client.Secret!)), SecurityAlgorithms.HmacSha256)); + break; + case OAuth2ClientAuthenticationMethod.PrivateKey: + this.ThrowIfInvalidClientCredentials(configuration.Client); + throw new NotImplementedException(); //todo + case OAuth2ClientAuthenticationMethod.Basic: + break; + default: throw new NotSupportedException($"The specified OAUTH2 client authentication method '{configuration.Client?.Authentication}' is not supported"); + } + if (configuration.Scopes?.Count > 0) properties["scope"] = string.Join(" ", configuration.Scopes); + if (configuration.Audiences?.Count > 0) properties["audience"] = string.Join(" ", configuration.Audiences); + if (!string.IsNullOrWhiteSpace(configuration.Username)) properties["username"] = configuration.Username; + if (!string.IsNullOrWhiteSpace(configuration.Password)) properties["password"] = configuration.Password; + if (configuration.Subject != null) + { + properties["subject_token"] = configuration.Subject.Token; + properties["subject_token_type"] = configuration.Subject.Type; + } + if (configuration.Actor != null) + { + properties["actor_token"] = configuration.Actor.Token; + properties["actor_token_type"] = configuration.Actor.Type; + } + if (token != null && token.HasExpired && !string.IsNullOrWhiteSpace(token.RefreshToken)) + { + properties["grant_type"] = "refresh_token"; + properties["refresh_token"] = token.RefreshToken; + } + using var content = configuration.Request.Encoding switch + { + OAuth2RequestEncoding.FormUrl => (HttpContent)new FormUrlEncodedContent(properties), + OAuth2RequestEncoding.Json => new StringContent(this.JsonSerializer.SerializeToText(properties), Encoding.UTF8, MediaTypeNames.Application.Json), + _ => throw new NotSupportedException($"The specified OAUTH2 request encoding '{configuration.Request.Encoding}' is not supported") + }; + using var request = new HttpRequestMessage(HttpMethod.Post, tokenEndpoint) { Content = content }; + if (configuration.Client?.Authentication == OAuth2ClientAuthenticationMethod.Basic) + { + this.ThrowIfInvalidClientCredentials(configuration.Client); + request.Headers.Authorization = new("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{configuration.Client.Id}:{configuration.Client.Secret}"))); + } + using var response = await this.HttpClient.SendAsync(request, cancellationToken).ConfigureAwait(false); + var json = await response.Content?.ReadAsStringAsync(cancellationToken)!; + if (!response.IsSuccessStatusCode) + { + this.Logger.LogError("An error occurred while generating a new JWT token: {details}", json); + response.EnsureSuccessStatusCode(); + } + token = this.JsonSerializer.Deserialize(json)!; + this.Tokens[tokenKey] = token; + return token; + } + + /// + /// Throws a new if the specified client credentials have not been properly configured, as required by the configured authentication method + /// + /// The client credentials to validate + protected virtual void ThrowIfInvalidClientCredentials(OAuth2AuthenticationClientDefinition? client) + { + if(string.IsNullOrWhiteSpace(client?.Id) || string.IsNullOrWhiteSpace(client?.Secret)) throw new NullReferenceException($"The client id and client secret must be configured when using the '{client?.Authentication}' OAUTH2 authentication method"); + } + + /// + /// Creates a JSON Web Token (JWT) for client authentication using the provided client ID, audience and signing credentials. + /// + /// The client ID used as the subject and issuer of the JWT + /// The audience for which the JWT is intended, typically the token endpoint URL + /// The credentials used to signed the JWT + /// A signed JWT in string format, to be used as a client assertion in OAuth 2.0 requests + protected virtual string CreateClientAssertionJwt(string clientId, string audience, SigningCredentials signingCredentials) + { + ArgumentException.ThrowIfNullOrWhiteSpace(clientId); + ArgumentException.ThrowIfNullOrWhiteSpace(audience); + ArgumentNullException.ThrowIfNull(signingCredentials); + var claims = new List + { + new(JwtRegisteredClaimNames.Sub, clientId), + new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) + }; + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(claims), + Issuer = clientId, + Audience = audience, + NotBefore = DateTime.UtcNow, + Expires = DateTime.UtcNow.AddMinutes(5), + SigningCredentials = signingCredentials + }; + var tokenHandler = new JsonWebTokenHandler(); + return tokenHandler.CreateToken(tokenDescriptor); + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/SchemaHandlerProvider.cs b/src/core/Synapse.Core.Infrastructure/Services/SchemaHandlerProvider.cs new file mode 100644 index 000000000..9aca21b69 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/SchemaHandlerProvider.cs @@ -0,0 +1,36 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Represents the default implementation of the interface +/// +/// An containing all registered s +public class SchemaHandlerProvider(IEnumerable schemaHandlers) + : ISchemaHandlerProvider +{ + + /// + /// Gets an containing all registered s + /// + protected IEnumerable Handlers { get; } = schemaHandlers; + + /// + public virtual ISchemaHandler? GetHandler(string format) + { + ArgumentException.ThrowIfNullOrWhiteSpace(format); + return this.Handlers.FirstOrDefault(h => h.Supports(format)); + } + +} diff --git a/src/core/Synapse.Core.Infrastructure/Services/WorkflowInstanceMutator.cs b/src/core/Synapse.Core.Infrastructure/Services/WorkflowInstanceMutator.cs new file mode 100644 index 000000000..30c9b05f1 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Services/WorkflowInstanceMutator.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented.Services; +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Synapse.Resources; +using Neuroglia.Data; + +namespace Synapse.Core.Infrastructure.Services; + +/// +/// Represents the service used to mutate s +/// +public class WorkflowInstanceMutator + : IResourceMutator +{ + + /// + public virtual bool AppliesTo(Operation operation, string group, string version, string plural, string? @namespace = null) => operation == Operation.Create && group == WorkflowInstance.ResourceDefinition.Group && version == WorkflowInstance.ResourceDefinition.Version && plural == WorkflowInstance.ResourceDefinition.Plural; + + /// + public virtual Task MutateAsync(AdmissionReviewRequest context, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(context); + var updated = context.UpdatedState.ConvertTo()!; + updated.Metadata.Labels ??= new Dictionary(); + updated.Metadata.Labels[SynapseDefaults.Resources.Labels.Workflow] = $"{updated.Spec.Definition.Name}.{updated.Spec.Definition.Namespace}"; + updated.Metadata.Labels[SynapseDefaults.Resources.Labels.WorkflowVersion] = updated.Spec.Definition.Version; + var patch = JsonPatchUtility.CreateJsonPatchFromDiff(context.OriginalState, updated); + return Task.FromResult(new AdmissionReviewResponse(context.Uid, true, new(PatchType.JsonPatch, patch))); + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj b/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj new file mode 100644 index 000000000..edbba6aa5 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj @@ -0,0 +1,45 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse core infrastructure + true + true + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + + + + + + + + + + + + + + + + + + diff --git a/src/core/Synapse.Core.Infrastructure/Usings.cs b/src/core/Synapse.Core.Infrastructure/Usings.cs new file mode 100644 index 000000000..29b748506 --- /dev/null +++ b/src/core/Synapse.Core.Infrastructure/Usings.cs @@ -0,0 +1,14 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using ServerlessWorkflow.Sdk.Models; \ No newline at end of file diff --git a/src/core/Synapse.Core/Annotations/SemanticVersionAttribute.cs b/src/core/Synapse.Core/Annotations/SemanticVersionAttribute.cs new file mode 100644 index 000000000..3c258c2f6 --- /dev/null +++ b/src/core/Synapse.Core/Annotations/SemanticVersionAttribute.cs @@ -0,0 +1,26 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace System.ComponentModel.DataAnnotations; + +/// +/// Represents a used to validate semantic versions +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] +public class SemanticVersionAttribute() + : RegularExpressionAttribute(@"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$") +{ + + + +} diff --git a/src/core/Synapse.Core/ConcurrentHashSet.cs b/src/core/Synapse.Core/ConcurrentHashSet.cs new file mode 100644 index 000000000..a9ca33998 --- /dev/null +++ b/src/core/Synapse.Core/ConcurrentHashSet.cs @@ -0,0 +1,66 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections; +using System.Collections.Concurrent; + +namespace Synapse; + +/// +/// Represents a concurrent +/// +/// The type of value contained by the +public class ConcurrentHashSet + : ICollection + where T : notnull +{ + + readonly ConcurrentDictionary _dictionary = new(); + + /// + public virtual int Count => this._dictionary.Count; + + /// + public virtual bool IsReadOnly => false; + + /// + public virtual bool Contains(T item) => this._dictionary.ContainsKey(item); + + /// + public virtual void Add(T item) + { + if (!this._dictionary.TryAdd(item, false)) throw new InvalidOperationException("The specified item already exists in the collection"); + } + + /// + /// Attempts to add the specified item to the hashset + /// + /// The item to add + /// A boolean indicating whether or not the item has been added or if it was already present in the set + public virtual bool TryAdd(T item) => this._dictionary.TryAdd(item, false); + + /// + public virtual bool Remove(T item) => this._dictionary.Remove(item, out _); + + /// + public virtual void Clear() => this._dictionary.Clear(); + + /// + public virtual void CopyTo(T[] array, int arrayIndex) => this._dictionary.Keys.CopyTo(array, arrayIndex); + + /// + public virtual IEnumerator GetEnumerator() => this._dictionary.Keys.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + +} diff --git a/src/core/Synapse.Core/CorrelationContextStatus.cs b/src/core/Synapse.Core/CorrelationContextStatus.cs new file mode 100644 index 000000000..f21904d73 --- /dev/null +++ b/src/core/Synapse.Core/CorrelationContextStatus.cs @@ -0,0 +1,51 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Exposes all default statuses of a correlation context +/// +public static class CorrelationContextStatus +{ + + /// + /// Indicates that the context is currently active and in use. + /// + public const string Active = "active"; + /// + /// Indicates that the context is inactive or paused + /// + public const string Inactive = "inactive"; + /// + /// Indicates that the correlation process has been successfully completed. + /// + public const string Completed = "completed"; + /// + /// Indicates that the correlation process has been cancelled. + /// + public const string Cancelled = "cancelled"; + + /// + /// Gets a new used to enumerate the default correlation context statuses + /// + /// A new used to enumerate the default correlation context statuses + public static IEnumerable AsEnumerable() + { + yield return Active; + yield return Inactive; + yield return Completed; + yield return Cancelled; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/CorrelationOutcomeType.cs b/src/core/Synapse.Core/CorrelationOutcomeType.cs new file mode 100644 index 000000000..6ac338915 --- /dev/null +++ b/src/core/Synapse.Core/CorrelationOutcomeType.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Exposes default correlation outcome types +/// +public static class CorrelationOutcomeType +{ + + /// + /// Indicates that the correlation correlates to an existing workflow instance + /// + public const string Correlate = "correlate"; + /// + /// Indicates that the correlation starts a new instance of a workflow + /// + public const string Start = "start"; + + /// + /// Gets an containing default correlation outcome types + /// + /// A new containing default correlation outcome types + public static IEnumerable AsEnumerable() + { + yield return Start; + yield return Correlate; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/CorrelationStatusPhase.cs b/src/core/Synapse.Core/CorrelationStatusPhase.cs new file mode 100644 index 000000000..a96798e67 --- /dev/null +++ b/src/core/Synapse.Core/CorrelationStatusPhase.cs @@ -0,0 +1,56 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Exposes all default correlation status phases +/// +public static class CorrelationStatusPhase +{ + + /// + /// Indicates that the correlation is pending processing by a correlator + /// + public const string Pending = "pending"; + /// + /// Indicates that the correlation has been picked up by a correlator and is actively correlating ingested cloud events + /// + public const string Active = "active"; + /// + /// Indicates that the correlation is inactive and is not correlating events + /// + public const string Inactive = "inactive"; + /// + /// Indicates that an ephemeral correlation has been completed + /// + public const string Completed = "completed"; + /// + /// Indicates that the correlation has been cancelled + /// + public const string Cancelled = "cancelled"; + + /// + /// Gets an containing default correlation status phases + /// + /// A new containing default correlation status phases + public static IEnumerable AsEnumerable() + { + yield return Pending; + yield return Active; + yield return Inactive; + yield return Completed; + yield return Cancelled; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/CorrelatorStatusPhase.cs b/src/core/Synapse.Core/CorrelatorStatusPhase.cs new file mode 100644 index 000000000..540003f90 --- /dev/null +++ b/src/core/Synapse.Core/CorrelatorStatusPhase.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Exposes all default correlator status phases +/// +public static class CorrelatorStatusPhase +{ + + /// + /// Indicates that the correlator is running + /// + public const string Running = "running"; + /// + /// Indicates that the correlator has been stopped and is not running + /// + public const string Stopped = "stopped"; + + /// + /// Gets an containing default correlator status phases + /// + /// A new containing default correlator status phases + public static IEnumerable AsEnumerable() + { + yield return Running; + yield return Stopped; + } + +} diff --git a/src/core/Synapse.Core/Error.cs b/src/core/Synapse.Core/Error.cs new file mode 100644 index 000000000..6734ea040 --- /dev/null +++ b/src/core/Synapse.Core/Error.cs @@ -0,0 +1,121 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Represents an object used to describe an error or problem, as defined by RFC 7807 +/// +[DataContract] +public record Error + : IExtensible +{ + + /// + /// Gets/sets an uri that reference the type of the described problem. + /// + [DataMember(Order = 1, Name = "type"), JsonPropertyName("type"), JsonPropertyOrder(1), YamlMember(Alias = "type", Order = 1)] + public required virtual Uri Type { get; set; } + + /// + /// Gets/sets a short, human-readable summary of the problem type.It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization. + /// + [DataMember(Order = 2, Name = "title"), JsonPropertyName("title"), JsonPropertyOrder(2), YamlMember(Alias = "title", Order = 2)] + public required virtual string Title { get; set; } + + /// + /// Gets/sets the status code produced by the described problem + /// + [DataMember(Order = 3, Name = "status"), JsonPropertyName("status"), JsonPropertyOrder(3), YamlMember(Alias = "status", Order = 3)] + public required virtual ushort Status { get; set; } + + /// + /// Gets/sets a human-readable explanation specific to this occurrence of the problem. + /// + [DataMember(Order = 4, Name = "detail"), JsonPropertyName("detail"), JsonPropertyOrder(4), YamlMember(Alias = "detail", Order = 4)] + public virtual string? Detail { get; set; } + + /// + /// Gets/sets a reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. + /// + [DataMember(Order = 5, Name = "instance"), JsonPropertyName("instance"), JsonPropertyOrder(5), YamlMember(Alias = "instance", Order = 5)] + public virtual Uri? Instance { get; set; } + + /// + /// Gets/sets a mapping containing problem details extension data, if any + /// + [DataMember(Name = "extensionData", Order = 6), JsonExtensionData] + public virtual IDictionary? ExtensionData { get; set; } + + /// + /// Creates a new communication + /// + /// The source + /// The 's status + /// The detail, if any + /// A new communication + public static Error Communication(Uri instance, ushort status = ErrorStatus.Communication, string? detail = null) => new() + { + Status = status, + Type = ErrorType.Communication, + Title = ErrorTitle.Communication, + Detail = detail, + Instance = instance + }; + + /// + /// Creates a new communication + /// + /// The source + /// The detail, if any + /// A new communication + public static Error Configuration(Uri instance, string? detail = null) => new() + { + Status = ErrorStatus.Configuration, + Type = ErrorType.Configuration, + Title = ErrorTitle.Configuration, + Detail = detail, + Instance = instance + }; + + /// + /// Creates a new runtime + /// + /// The source + /// The detail, if any + /// A new communication + public static Error Runtime(Uri instance, string? detail = null) => new() + { + Status = ErrorStatus.Runtime, + Type = ErrorType.Runtime, + Title = ErrorTitle.Runtime, + Detail = detail, + Instance = instance + }; + + /// + /// Creates a new validation + /// + /// The source + /// The detail, if any + /// A new communication + public static Error Validation(Uri instance, string? detail = null) => new() + { + Status = ErrorStatus.Validation, + Type = ErrorType.Validation, + Title = ErrorTitle.Validation, + Detail = detail, + Instance = instance + }; + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Extensions/CorrelationContextExtensions.cs b/src/core/Synapse.Core/Extensions/CorrelationContextExtensions.cs new file mode 100644 index 000000000..7b21f3701 --- /dev/null +++ b/src/core/Synapse.Core/Extensions/CorrelationContextExtensions.cs @@ -0,0 +1,40 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse; + +/// +/// Defines extensions for s +/// +public static class CorrelationContextExtensions +{ + + /// + /// Determines whether or not the satisfies the filter(s) defined by specified + /// + /// The to check + /// The that configures the s to satisfy + /// A boolean indicating whether or not the satisfies the filter(s) defined by specified + public static bool Satisfies(this CorrelationContext context, EventConsumptionStrategyDefinition eventConsumptionStrategy) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(eventConsumptionStrategy); + if (eventConsumptionStrategy.All != null && context.Events.Count == eventConsumptionStrategy.All.Count) return true; + else if(eventConsumptionStrategy.Any != null && context.Events.Count > 0) return true; + else if (eventConsumptionStrategy.One != null && context.Events.Count > 0) return true; + return false; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Extensions/ExceptionExtensions.cs b/src/core/Synapse.Core/Extensions/ExceptionExtensions.cs new file mode 100644 index 000000000..818bc72d7 --- /dev/null +++ b/src/core/Synapse.Core/Extensions/ExceptionExtensions.cs @@ -0,0 +1,53 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Net; + +namespace Synapse; + +/// +/// Defines extensions for s +/// +public static class ExceptionExtensions +{ + + /// + /// Converts the into a new + /// + /// The to convert + /// The , if any, that references the instance to which the to create applies + /// A new based on the specified + public static Error ToError(this Exception ex, Uri? instance = null) + { + return ex switch + { + HttpRequestException httpEx => new() + { + Status = (ushort)(httpEx.StatusCode ?? HttpStatusCode.InternalServerError), + Type = ErrorType.Communication, + Title = ErrorTitle.Communication, + Instance = instance, + Detail = httpEx.Message + }, + _ => new() + { + Status = ErrorStatus.Runtime, + Type = ErrorType.Runtime, + Title = ErrorTitle.Runtime, + Instance = instance, + Detail = ex.Message + } + }; + } + +} diff --git a/src/core/Synapse.Core/Extensions/TaskDefinitionMapExtensions.cs b/src/core/Synapse.Core/Extensions/TaskDefinitionMapExtensions.cs new file mode 100644 index 000000000..caa689217 --- /dev/null +++ b/src/core/Synapse.Core/Extensions/TaskDefinitionMapExtensions.cs @@ -0,0 +1,58 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse; + +/// +/// Defines extensions for of s +/// +public static class TaskDefinitionMapExtensions +{ + + /// + /// Gets the task, if any, that should be executed after the specified one + /// + /// The map that defines the to get the next for + /// The name of the to get the next for + /// A name/definition mapping of the next , if any + public static MapEntry? GetTaskAfter(this Map tasks, string taskName) + { + ArgumentNullException.ThrowIfNull(tasks); + ArgumentException.ThrowIfNullOrWhiteSpace(taskName); + var index = tasks.Select(t => t.Key).ToList().IndexOf(taskName); + if (index == -1 || index + 1 >= tasks.Count) return null; + index++; + return tasks.ElementAt(index); + } + + /// + /// Gets the task, if any, that should be executed after the specified one + /// + /// The map that defines the to get the next for + /// The to get the next for + /// A name/definition mapping of the next , if any + public static MapEntry? GetTaskAfter(this Map tasks, TaskInstance task) + { + ArgumentNullException.ThrowIfNull(tasks); + ArgumentNullException.ThrowIfNull(task); + return task.Next switch + { + FlowDirective.Continue or null => tasks.GetTaskAfter(task.Name!), + FlowDirective.End or FlowDirective.Exit => null, + _ => tasks.GetEntry(task.Next) + }; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Extensions/WorkflowDefinitionEnumerableExtensions.cs b/src/core/Synapse.Core/Extensions/WorkflowDefinitionEnumerableExtensions.cs new file mode 100644 index 000000000..7250f5f0d --- /dev/null +++ b/src/core/Synapse.Core/Extensions/WorkflowDefinitionEnumerableExtensions.cs @@ -0,0 +1,39 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Semver; + +namespace Synapse; + +/// +/// Defines extensions for instances that contains s +/// +public static class WorkflowDefinitionEnumerableExtensions +{ + + /// + /// Gets the latest version of the + /// + /// An containing the s to get the latest of + /// The latest + public static WorkflowDefinition GetLatest(this IEnumerable definitions) => definitions.OrderByDescending(wf => SemVersion.Parse(wf.Document.Version, SemVersionStyles.Strict)).First(); + + /// + /// Gets the specified version + /// + /// An containing the s to get the specified version from + /// The version of the to get + /// The with the specified version, if any + public static WorkflowDefinition? Get(this IEnumerable definitions, string version) => definitions.FirstOrDefault(wf => wf.Document.Version == version); + +} diff --git a/src/core/Synapse.Core/Extensions/WorkflowDefinitionExtensions.cs b/src/core/Synapse.Core/Extensions/WorkflowDefinitionExtensions.cs new file mode 100644 index 000000000..f2a0e0f1d --- /dev/null +++ b/src/core/Synapse.Core/Extensions/WorkflowDefinitionExtensions.cs @@ -0,0 +1,60 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Runtime.CompilerServices; + +namespace Synapse; + +/// +/// Defines extensions for s +/// +public static class WorkflowDefinitionExtensions +{ + + /// + /// Gets the task, if any, that should be executed after the specified one + /// + /// The that defines the specified + /// The name/definition mapping of the to get the following of + /// A reference to the component that defines the next + /// A name/definition mapping of the next , if any + public static MapEntry? GetTaskAfter(this WorkflowDefinition workflow, MapEntry task, string parentReference) + { + ArgumentNullException.ThrowIfNull(workflow); + ArgumentNullException.ThrowIfNull(task); + ArgumentException.ThrowIfNullOrWhiteSpace(parentReference); + var taskMap = workflow.GetComponent>(parentReference) ?? throw new NullReferenceException($"Failed to find the component at '{parentReference}'"); + var taskIndex = taskMap.Select(e => e.Key).ToList().IndexOf(task.Key); + var nextIndex = taskIndex < 0 ? -1 : taskIndex + 1; + if (nextIndex < 0 || nextIndex >= taskMap.Count) return null; + return taskMap.ElementAt(nextIndex); + } + + /// + /// Gets the index of the specified task within its defining container + /// + /// The extended + /// The name of the task to get the index of + /// A reference to the parent of the task to get the index of + /// The index of the specified task + public static int IndexOf(this WorkflowDefinition workflow, string taskName, string parentReference) + { + ArgumentNullException.ThrowIfNull(workflow); + ArgumentException.ThrowIfNullOrWhiteSpace(taskName); + ArgumentException.ThrowIfNullOrWhiteSpace(parentReference); + var taskMap = workflow.GetComponent>(parentReference) ?? throw new NullReferenceException($"Failed to find the component at '{parentReference}'"); + if (taskMap.TryGetValue(taskName, out var task) && task != null) return taskMap.Select(e => e.Value).ToList().IndexOf(task); + else return -1; + } + +} diff --git a/src/core/Synapse.Core/OperatorRuntimeMode.cs b/src/core/Synapse.Core/OperatorRuntimeMode.cs new file mode 100644 index 000000000..325f05b05 --- /dev/null +++ b/src/core/Synapse.Core/OperatorRuntimeMode.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Enumerates all default operator runtime modes +/// +public static class OperatorRuntimeMode +{ + + /// + /// Gets the native operator runtime mode + /// + public const string Native = "native"; + /// + /// Gets the containerized operator runtime mode + /// + public const string Containerized = "containerized"; + + /// + /// Gets a new containing all default operator runtime modes + /// + /// A new containing all default operator runtime modes + public static IEnumerable AsEnumerable() + { + yield return Native; + yield return Containerized; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/OperatorStatusPhase.cs b/src/core/Synapse.Core/OperatorStatusPhase.cs new file mode 100644 index 000000000..676ca38ef --- /dev/null +++ b/src/core/Synapse.Core/OperatorStatusPhase.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Exposes all default operator status phases +/// +public static class OperatorStatusPhase +{ + + /// + /// Indicates that the operator is running + /// + public const string Running = "running"; + /// + /// Indicates that the operator has been stopped and is not running + /// + public const string Stopped = "stopped"; + + /// + /// Gets an containing default operator status phases + /// + /// A new containing default operator status phases + public static IEnumerable AsEnumerable() + { + yield return Running; + yield return Stopped; + } + +} diff --git a/src/core/Synapse.Core/Resources/CertificateValidationStrategyDefinition.cs b/src/core/Synapse.Core/Resources/CertificateValidationStrategyDefinition.cs new file mode 100644 index 000000000..0f4c92a93 --- /dev/null +++ b/src/core/Synapse.Core/Resources/CertificateValidationStrategyDefinition.cs @@ -0,0 +1,32 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; + +namespace Synapse.Resources; + +/// +/// Represents the configuration of a certificate validation strategy +/// +[DataContract] +public record CertificateValidationStrategyDefinition +{ + + /// + /// Gets/sets a boolean indicating whether or not to validate certificates when performing requests + /// + [DefaultValue(true)] + [DataMember(Order = 1, Name = "validate"), JsonPropertyOrder(1), JsonPropertyName("validate"), YamlMember(Order = 1, Alias = "validate")] + public virtual bool Validate { get; set; } = true; + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/CorrelateWorkflowOutcomeDefinition.cs b/src/core/Synapse.Core/Resources/CorrelateWorkflowOutcomeDefinition.cs new file mode 100644 index 000000000..aa3c17da7 --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelateWorkflowOutcomeDefinition.cs @@ -0,0 +1,36 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the definition of a correlation outcome used to correlation an existing workflow instance +/// +[DataContract] +public record CorrelateWorkflowOutcomeDefinition +{ + + /// + /// Gets/sets a '{name}.{namespace}' reference to the workflow instance to correlate + /// + [Required] + [DataMember(Name = "instance", Order = 1), JsonPropertyName("instance"), JsonPropertyOrder(1), YamlMember(Alias = "instance", Order = 1)] + public required virtual string Instance { get; set; } + + /// + /// Gets/sets the name of the task that consumes the correlated events + /// + [DataMember(Name = "task", Order = 2), JsonPropertyName("task"), JsonPropertyOrder(2), YamlMember(Alias = "task", Order = 2)] + public required virtual string Task { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/Correlation.cs b/src/core/Synapse.Core/Resources/Correlation.cs new file mode 100644 index 000000000..639b276b2 --- /dev/null +++ b/src/core/Synapse.Core/Resources/Correlation.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Resources; + +/// +/// Represents a resource used to describe and configure a correlation +/// +[DataContract] +public record Correlation + : Resource +{ + + /// + /// Gets the 's resource type + /// + public static readonly ResourceDefinitionInfo ResourceDefinition = new CorrelationResourceDefinition()!; + + /// + public Correlation() : base(ResourceDefinition) { this.Status = new(); } + + /// + public Correlation(ResourceMetadata metadata, CorrelationSpec spec) : base(ResourceDefinition, metadata, spec, new()) { } + +} diff --git a/src/core/Synapse.Core/Resources/Correlation.yaml b/src/core/Synapse.Core/Resources/Correlation.yaml new file mode 100644 index 000000000..74b362b12 --- /dev/null +++ b/src/core/Synapse.Core/Resources/Correlation.yaml @@ -0,0 +1,31 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: correlations.synapse.io +spec: + scope: Namespaced + group: synapse.io + names: + plural: correlations + singular: correlation + kind: Correlation + shortNames: + - op + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Configures the Synapse Correlation + properties: {} #todo + status: + type: object + required: + - spec + subresources: + status: {} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/CorrelationContext.cs b/src/core/Synapse.Core/Resources/CorrelationContext.cs new file mode 100644 index 000000000..a46ba1a56 --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelationContext.cs @@ -0,0 +1,49 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Eventing.CloudEvents; + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe the context of a correlation +/// +[DataContract] +public record CorrelationContext +{ + + /// + /// Gets/sets the context's unique identifier + /// + [DataMember(Name = "id", Order = 1), JsonPropertyName("id"), JsonPropertyOrder(1), YamlMember(Alias = "id", Order = 1)] + public required virtual string Id { get; set; } + + /// + /// Gets/sets the context's status + /// + [DataMember(Name = "status", Order = 2), JsonPropertyName("status"), JsonPropertyOrder(2), YamlMember(Alias = "status", Order = 2)] + public virtual string Status { get; set; } = CorrelationContextStatus.Active; + + /// + /// Gets a key/value mapping of the context's correlation keys + /// + [DataMember(Name = "keys", Order = 2), JsonPropertyName("keys"), JsonPropertyOrder(2), YamlMember(Alias = "keys", Order = 2)] + public virtual EquatableDictionary Keys { get; set; } = []; + + /// + /// Gets a key/value mapping of all correlated events, with the key being the index of the matched correlation filter + /// + [DataMember(Name = "events", Order = 3), JsonPropertyName("events"), JsonPropertyOrder(3), YamlMember(Alias = "events", Order = 3)] + public virtual EquatableDictionary Events { get; set; } = []; + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/CorrelationLifetime.cs b/src/core/Synapse.Core/Resources/CorrelationLifetime.cs new file mode 100644 index 000000000..209f672ed --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelationLifetime.cs @@ -0,0 +1,42 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Enumerates default correlation lifetimes +/// +public static class CorrelationLifetime +{ + + /// + /// Indicates a durable correlation + /// + public const string Durable = "durable"; + + /// + /// Indicates an ephemeral correlation, which is a single use correlation + /// + public const string Ephemeral = "ephemeral"; + + /// + /// Gets all supported correlation lifetime + /// + /// An supporting all default correlation lifetime + public static IEnumerable AsEnumerable() + { + yield return Durable; + yield return Ephemeral; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/CorrelationOutcomeDefinition.cs b/src/core/Synapse.Core/Resources/CorrelationOutcomeDefinition.cs new file mode 100644 index 000000000..36e0ed7f7 --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelationOutcomeDefinition.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the correlation's outcome +/// +[DataContract] +public record CorrelationOutcomeDefinition +{ + + /// + /// Gets the type of the correlation outcome + /// + public virtual string Type => this.Start != null ? CorrelationOutcomeType.Start : this.Correlate != null ? CorrelationOutcomeType.Correlate : throw new NotSupportedException($"The specified correlation outcome type is not supported"); + + /// + /// Gets/sets an object used to configure the outcome, if any, used to start a new workflow instance,. Is mutually exclusive to + /// + + [DataMember(Name = "start", Order = 1), JsonPropertyName("start"), JsonPropertyOrder(1), YamlMember(Alias = "start", Order = 1)] + public virtual StartWorkflowOutcomeDefinition? Start { get; set; } + + /// + /// Gets/sets an object used to configure the outcome, if any, used to correlate a correlation context to an existing workflow instance. Is mutually exclusive to + /// + [DataMember(Name = "correlate", Order = 2), JsonPropertyName("correlate"), JsonPropertyOrder(2), YamlMember(Alias = "correlate", Order = 2)] + public virtual CorrelateWorkflowOutcomeDefinition? Correlate { get; set; } + +} diff --git a/src/core/Synapse.Core/Resources/CorrelationResourceDefinition.cs b/src/core/Synapse.Core/Resources/CorrelationResourceDefinition.cs new file mode 100644 index 000000000..00c03b5ab --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelationResourceDefinition.cs @@ -0,0 +1,64 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Neuroglia.Serialization.Yaml; + +namespace Synapse.Resources; + +/// +/// Represents the definition of an +/// +[DataContract] +public record CorrelationResourceDefinition + : ResourceDefinition +{ + + /// + /// Gets the definition of s + /// + public static new ResourceDefinition Instance { get; set; } + /// + /// Gets/sets the group resource definitions belong to + /// + public static new string ResourceGroup { get; set; } + /// + /// Gets/sets the resource version of resource definitions + /// + public static new string ResourceVersion { get; set; } + /// + /// Gets/sets the plural name of resource definitions + /// + public static new string ResourcePlural { get; set; } + /// + /// Gets/sets the kind of resource definitions + /// + public static new string ResourceKind { get; set; } + + static CorrelationResourceDefinition() + { + using var stream = typeof(Workflow).Assembly.GetManifestResourceStream($"{typeof(Correlation).Namespace}.{nameof(Correlation)}.yaml")!; + using var streamReader = new StreamReader(stream); + Instance = YamlSerializer.Default.Deserialize(streamReader.ReadToEnd())!; + ResourceGroup = Instance.Spec.Group; + ResourceVersion = Instance.Spec.Versions.Last().Name; + ResourcePlural = Instance.Spec.Names.Plural; + ResourceKind = Instance.Spec.Names.Kind; + } + + /// + /// Initializes a new + /// + public CorrelationResourceDefinition() : base(Instance) { } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/CorrelationSpec.cs b/src/core/Synapse.Core/Resources/CorrelationSpec.cs new file mode 100644 index 000000000..fe56f5063 --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelationSpec.cs @@ -0,0 +1,55 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Resources; + +/// +/// Represents an object used to configure a correlation +/// +[DataContract] +public record CorrelationSpec +{ + + /// + /// Gets/sets the correlation's source (user or workflow) + /// + [DataMember(Name = "source", Order = 1), JsonPropertyName("source"), JsonPropertyOrder(1), YamlMember(Alias = "source", Order = 1)] + public virtual ResourceReference? Source { get; set; } + + /// + /// Gets/sets the correlation's lifetime + /// + [DataMember(Name = "lifetime", Order = 2), JsonPropertyName("lifetime"), JsonPropertyOrder(2), YamlMember(Alias = "lifetime", Order = 2)] + public virtual string Lifetime { get; set; } = CorrelationLifetime.Ephemeral; + + /// + /// Gets/sets an object used to configure the runtime expressions used to correlate events + /// + [DataMember(Name = "expressions", Order = 3), JsonPropertyName("expressions"), JsonPropertyOrder(3), YamlMember(Alias = "expressions", Order = 3)] + public virtual RuntimeExpressionEvaluationConfiguration Expressions { get; set; } = new(); + + /// + /// Gets/sets an object used to define the events to correlate + /// + [DataMember(Name = "events", Order = 4), JsonPropertyName("events"), JsonPropertyOrder(4), YamlMember(Alias = "events", Order = 4)] + public virtual EventConsumptionStrategyDefinition Events { get; set; } = null!; + + /// + /// Gets/sets an object used to configure the correlation's outcome + /// + [DataMember(Name = "outcome", Order = 5), JsonPropertyName("outcome"), JsonPropertyOrder(5), YamlMember(Alias = "outcome", Order = 5)] + public virtual CorrelationOutcomeDefinition Outcome { get; set; } = null!; + +} diff --git a/src/core/Synapse.Core/Resources/CorrelationStatus.cs b/src/core/Synapse.Core/Resources/CorrelationStatus.cs new file mode 100644 index 000000000..67fc3da8d --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelationStatus.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe the status of a correlation +/// +[DataContract] +public record CorrelationStatus +{ + + /// + /// Gets/sets the correlation's status phase + /// + [DataMember(Name = "phase", Order = 1), JsonPropertyName("phase"), JsonPropertyOrder(1), YamlMember(Alias = "phase", Order = 1)] + public virtual string? Phase { get; set; } + + /// + /// Gets/sets the date and time the correlation was last modified at + /// + [DataMember(Name = "lastModified", Order = 2), JsonPropertyName("lastModified"), JsonPropertyOrder(2), YamlMember(Alias = "lastModified", Order = 2)] + public virtual DateTimeOffset? LastModified { get; set; } + + /// + /// Gets/sets a list containing the contexts that have been created for the described correlation + /// + [DataMember(Name = "contexts", Order = 3), JsonPropertyName("contexts"), JsonPropertyOrder(3), YamlMember(Alias = "contexts", Order = 3)] + public virtual EquatableList Contexts { get; set; } = []; + +} diff --git a/src/core/Synapse.Core/Resources/Correlator.cs b/src/core/Synapse.Core/Resources/Correlator.cs new file mode 100644 index 000000000..2ab8b150b --- /dev/null +++ b/src/core/Synapse.Core/Resources/Correlator.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Resources; + +/// +/// Represents the resource used to describe and configure a correlator +/// +[DataContract] +public record Correlator + : Resource +{ + + /// + /// Gets the 's resource type + /// + public static readonly ResourceDefinitionInfo ResourceDefinition = new CorrelatorResourceDefinition()!; + + /// + public Correlator() : base(ResourceDefinition) { } + + /// + public Correlator(ResourceMetadata metadata, CorrelatorSpec spec) : base(ResourceDefinition, metadata, spec) { } + +} diff --git a/src/core/Synapse.Core/Resources/Correlator.yaml b/src/core/Synapse.Core/Resources/Correlator.yaml new file mode 100644 index 000000000..13ddf81c6 --- /dev/null +++ b/src/core/Synapse.Core/Resources/Correlator.yaml @@ -0,0 +1,31 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: correlators.synapse.io +spec: + scope: Namespaced + group: synapse.io + names: + plural: correlators + singular: correlator + kind: Correlator + shortNames: + - op + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Configures the Synapse Correlator + properties: {} #todo + status: + type: object + required: + - spec + subresources: + status: {} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/CorrelatorResourceDefinition.cs b/src/core/Synapse.Core/Resources/CorrelatorResourceDefinition.cs new file mode 100644 index 000000000..564f7124d --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelatorResourceDefinition.cs @@ -0,0 +1,64 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Neuroglia.Serialization.Yaml; + +namespace Synapse.Resources; + +/// +/// Represents the definition of an +/// +[DataContract] +public record CorrelatorResourceDefinition + : ResourceDefinition +{ + + /// + /// Gets the definition of s + /// + public static new ResourceDefinition Instance { get; set; } + /// + /// Gets/sets the group resource definitions belong to + /// + public static new string ResourceGroup { get; set; } + /// + /// Gets/sets the resource version of resource definitions + /// + public static new string ResourceVersion { get; set; } + /// + /// Gets/sets the plural name of resource definitions + /// + public static new string ResourcePlural { get; set; } + /// + /// Gets/sets the kind of resource definitions + /// + public static new string ResourceKind { get; set; } + + static CorrelatorResourceDefinition() + { + using var stream = typeof(Workflow).Assembly.GetManifestResourceStream($"{typeof(Correlator).Namespace}.{nameof(Correlator)}.yaml")!; + using var streamReader = new StreamReader(stream); + Instance = YamlSerializer.Default.Deserialize(streamReader.ReadToEnd())!; + ResourceGroup = Instance.Spec.Group; + ResourceVersion = Instance.Spec.Versions.Last().Name; + ResourcePlural = Instance.Spec.Names.Plural; + ResourceKind = Instance.Spec.Names.Kind; + } + + /// + /// Initializes a new + /// + public CorrelatorResourceDefinition() : base(Instance) { } + +} diff --git a/src/core/Synapse.Core/Resources/CorrelatorSpec.cs b/src/core/Synapse.Core/Resources/CorrelatorSpec.cs new file mode 100644 index 000000000..c24d72a71 --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelatorSpec.cs @@ -0,0 +1,30 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the object used to configure the desired state of an +/// +[DataContract] +public record CorrelatorSpec +{ + + /// + /// Gets/sets a key/value mapping of the labels to select correlations by. + /// If not set, the broker will attempt to pick up all unclaimed workflows and workflow instances + /// + [DataMember(Order = 1, Name = "selector"), JsonPropertyOrder(1), JsonPropertyName("selector"), YamlMember(Order = 1, Alias = "selector")] + public virtual IDictionary? Selector { get; set; } + +} diff --git a/src/core/Synapse.Core/Resources/CorrelatorStatus.cs b/src/core/Synapse.Core/Resources/CorrelatorStatus.cs new file mode 100644 index 000000000..667e30e61 --- /dev/null +++ b/src/core/Synapse.Core/Resources/CorrelatorStatus.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe the status of an correlator +/// +[DataContract] +public record CorrelatorStatus +{ + + /// + /// Gets/sets the correlator's current status phase + /// + [DataMember(Name = "phase", Order = 1), JsonPropertyName("phase"), JsonPropertyOrder(1), YamlMember(Alias = "phase", Order = 1)] + public virtual string? Phase { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/Document.cs b/src/core/Synapse.Core/Resources/Document.cs new file mode 100644 index 000000000..c98a9a59d --- /dev/null +++ b/src/core/Synapse.Core/Resources/Document.cs @@ -0,0 +1,47 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents a document +/// +[DataContract] +public record Document + : IIdentifiable +{ + + /// + [DataMember(Name = "id", Order = 1), JsonPropertyName("id"), JsonPropertyOrder(1), YamlMember(Alias = "id", Order = 1)] + public string Id { get; set; } = Guid.NewGuid().ToString("N")[..15]; + + /// + /// Gets/sets the document's name + /// + [Required] + [DataMember(Name = "name", Order = 2), JsonPropertyName("name"), JsonPropertyOrder(2), YamlMember(Alias = "name", Order = 2)] + public required virtual string Name { get; set; } = null!; + + /// + /// Gets the document's content + /// + [Required] + [DataMember(Name = "content", Order = 3), JsonPropertyName("content"), JsonPropertyOrder(3), YamlMember(Alias = "content", Order = 3)] + public required virtual object Content { get; set; } = null!; + + [IgnoreDataMember, JsonIgnore, YamlIgnore] + object IIdentifiable.Id => this.Name; + + bool IEquatable>.Equals(IIdentifiable? other) => other != null && this.Name.Equals(other?.Id); + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/NativeRunnerProcessDefinition.cs b/src/core/Synapse.Core/Resources/NativeRunnerProcessDefinition.cs new file mode 100644 index 000000000..41161ca52 --- /dev/null +++ b/src/core/Synapse.Core/Resources/NativeRunnerProcessDefinition.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the definition of a native workflow runner process +/// +[DataContract] +public record NativeRunnerProcessDefinition +{ + + /// + /// Gets/sets the path to the file to execute to run a workflow instance + /// + [DataMember(Order = 1, Name = "executable"), JsonPropertyOrder(1), JsonPropertyName("executable"), YamlMember(Order = 1, Alias = "executable")] + public virtual string Executable { get; set; } = "Synapse.Runner"; + + /// + /// Gets/sets the working directory + /// + [DataMember(Order = 2, Name = "directory"), JsonPropertyOrder(2), JsonPropertyName("directory"), YamlMember(Order = 2, Alias = "directory")] + public virtual string Directory { get; set; } = Path.Combine(AppContext.BaseDirectory, "bin", "runner"); + +} diff --git a/src/core/Synapse.Core/Resources/Operator.cs b/src/core/Synapse.Core/Resources/Operator.cs new file mode 100644 index 000000000..506bc9793 --- /dev/null +++ b/src/core/Synapse.Core/Resources/Operator.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Resources; + +/// +/// Represents the resource used to describe and configure an operator +/// +[DataContract] +public record Operator + : Resource +{ + + /// + /// Gets the 's resource type + /// + public static readonly ResourceDefinitionInfo ResourceDefinition = new OperatorResourceDefinition()!; + + /// + public Operator() : base(ResourceDefinition) { } + + /// + public Operator(ResourceMetadata metadata, OperatorSpec spec) : base(ResourceDefinition, metadata, spec) { } + +} diff --git a/src/core/Synapse.Core/Resources/Operator.yaml b/src/core/Synapse.Core/Resources/Operator.yaml new file mode 100644 index 000000000..e1f823f56 --- /dev/null +++ b/src/core/Synapse.Core/Resources/Operator.yaml @@ -0,0 +1,31 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: operators.synapse.io +spec: + scope: Namespaced + group: synapse.io + names: + plural: operators + singular: operator + kind: Operator + shortNames: + - op + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Configures the Synapse Operator + properties: {} #todo + status: + type: object + required: + - spec + subresources: + status: {} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/OperatorResourceDefinition.cs b/src/core/Synapse.Core/Resources/OperatorResourceDefinition.cs new file mode 100644 index 000000000..fc6321da5 --- /dev/null +++ b/src/core/Synapse.Core/Resources/OperatorResourceDefinition.cs @@ -0,0 +1,64 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Neuroglia.Serialization.Yaml; + +namespace Synapse.Resources; + +/// +/// Represents the definition of an +/// +[DataContract] +public record OperatorResourceDefinition + : ResourceDefinition +{ + + /// + /// Gets the definition of s + /// + public static new ResourceDefinition Instance { get; set; } + /// + /// Gets/sets the group resource definitions belong to + /// + public static new string ResourceGroup { get; set; } + /// + /// Gets/sets the resource version of resource definitions + /// + public static new string ResourceVersion { get; set; } + /// + /// Gets/sets the plural name of resource definitions + /// + public static new string ResourcePlural { get; set; } + /// + /// Gets/sets the kind of resource definitions + /// + public static new string ResourceKind { get; set; } + + static OperatorResourceDefinition() + { + using var stream = typeof(Workflow).Assembly.GetManifestResourceStream($"{typeof(Operator).Namespace}.{nameof(Operator)}.yaml")!; + using var streamReader = new StreamReader(stream); + Instance = YamlSerializer.Default.Deserialize(streamReader.ReadToEnd())!; + ResourceGroup = Instance.Spec.Group; + ResourceVersion = Instance.Spec.Versions.Last().Name; + ResourcePlural = Instance.Spec.Names.Plural; + ResourceKind = Instance.Spec.Names.Kind; + } + + /// + /// Initializes a new + /// + public OperatorResourceDefinition() : base(Instance) { } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/OperatorSpec.cs b/src/core/Synapse.Core/Resources/OperatorSpec.cs new file mode 100644 index 000000000..4a1ccbad0 --- /dev/null +++ b/src/core/Synapse.Core/Resources/OperatorSpec.cs @@ -0,0 +1,39 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the object used to configure the desired state of an +/// +[DataContract] +public record OperatorSpec +{ + + /// + /// Gets/sets the options used to configure the runtime used by the Synapse Operator to spawn workflow instance processes + /// + [DataMember(Order = 1, Name = "runner"), JsonPropertyOrder(1), JsonPropertyName("runner"), YamlMember(Order = 1, Alias = "runner")] + public virtual RunnerDefinition Runner { get; set; } = new() + { + Runtime = new() + }; + + /// + /// Gets/sets a key/value mapping of the labels to select both workflows and workflow instances by. + /// If not set, the broker will attempt to pick up all unclaimed workflows and workflow instances + /// + [DataMember(Order = 2, Name = "selector"), JsonPropertyOrder(2), JsonPropertyName("selector"), YamlMember(Order = 2, Alias = "selector")] + public virtual IDictionary? Selector { get; set; } + +} diff --git a/src/core/Synapse.Core/Resources/OperatorStatus.cs b/src/core/Synapse.Core/Resources/OperatorStatus.cs new file mode 100644 index 000000000..ace72ea29 --- /dev/null +++ b/src/core/Synapse.Core/Resources/OperatorStatus.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe the status of an operator +/// +[DataContract] +public record OperatorStatus +{ + + /// + /// Gets/sets the operator's current status phase + /// + [DataMember(Name = "phase", Order = 1), JsonPropertyName("phase"), JsonPropertyOrder(1), YamlMember(Alias = "phase", Order = 1)] + public virtual string? Phase { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/RetryAttempt.cs b/src/core/Synapse.Core/Resources/RetryAttempt.cs new file mode 100644 index 000000000..fab817242 --- /dev/null +++ b/src/core/Synapse.Core/Resources/RetryAttempt.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe a retry attempt +/// +[DataContract] +public record RetryAttempt +{ + + /// + /// Gets/sets the retry attempt number + /// + [Required] + [DataMember(Name = "number", Order = 1), JsonPropertyName("number"), JsonPropertyOrder(1), YamlMember(Alias = "number", Order = 1)] + public required virtual uint Number { get; set; } + + /// + /// Gets/sets the date and time at which the retry attempt was performed + /// + [DataMember(Name = "time", Order = 2), JsonPropertyName("time"), JsonPropertyOrder(2), YamlMember(Alias = "time", Order = 2)] + public virtual DateTimeOffset Time { get; set; } = DateTimeOffset.Now; + + /// + /// Gets/sets the that is the cause of the try attempt + /// + [Required] + [DataMember(Name = "cause", Order = 3), JsonPropertyName("cause"), JsonPropertyOrder(3), YamlMember(Alias = "cause", Order = 3)] + public required virtual Error Cause { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/RunnerDefinition.cs b/src/core/Synapse.Core/Resources/RunnerDefinition.cs new file mode 100644 index 000000000..5acb894a2 --- /dev/null +++ b/src/core/Synapse.Core/Resources/RunnerDefinition.cs @@ -0,0 +1,50 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the definition of a Synapse workflow runner +/// +[DataContract] +public record RunnerDefinition +{ + + /// + /// Gets/sets the endpoint that references the base address and authentication policy for the Synapse API used by runners + /// + [Required] + [DataMember(Order = 1, Name = "api"), JsonPropertyOrder(1), JsonPropertyName("api"), YamlMember(Order = 1, Alias = "api")] + public virtual EndpointDefinition Api { get; set; } = null!; + + /// + /// Gets/sets the options used to configure the runtime used by the Synapse Operator to spawn workflow instance processes + /// + [Required] + [DataMember(Order = 2, Name = "runtime"), JsonPropertyOrder(2), JsonPropertyName("runtime"), YamlMember(Order = 2, Alias = "runtime")] + public virtual RuntimeDefinition Runtime { get; set; } = new() + { + Container = new() + { + Image = "ghcr.io/serverlessworkflow/synapse/runner" + } + }; + + /// + /// Gets/sets the endpoint that references the base address and authentication policy for the Synapse API used by runners + /// + [Required] + [DataMember(Order = 3, Name = "certificates"), JsonPropertyOrder(3), JsonPropertyName("certificates"), YamlMember(Order = 3, Alias = "certificates")] + public virtual CertificateValidationStrategyDefinition Certificates { get; set; } = null!; + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/RuntimeDefinition.cs b/src/core/Synapse.Core/Resources/RuntimeDefinition.cs new file mode 100644 index 000000000..4513ca0a5 --- /dev/null +++ b/src/core/Synapse.Core/Resources/RuntimeDefinition.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using ServerlessWorkflow.Sdk.Models.Processes; + +namespace Synapse.Resources; + +/// +/// Represents an object used to configure the runtime used to spawn workflow instance processes +/// +[DataContract] +public record RuntimeDefinition +{ + + /// + /// Gets/sets the options used to configure the runtime, when using the native mode + /// + [DataMember(Order = 1, Name = "native"), JsonPropertyOrder(1), JsonPropertyName("native"), YamlMember(Order = 1, Alias = "native")] + public virtual NativeRunnerProcessDefinition? Native { get; set; } + + /// + /// Gets/sets the options used to configure the runtime, when using the container mode + /// + [DataMember(Order = 2, Name = "container"), JsonPropertyOrder(2), JsonPropertyName("container"), YamlMember(Order = 2, Alias = "container")] + public virtual ContainerProcessDefinition? Container { get; set; } + + /// + /// Gets the runtime mode + /// + [IgnoreDataMember, JsonIgnore, YamlIgnore] + public virtual string Mode => this.Native != null ? OperatorRuntimeMode.Native : this.Container != null ? OperatorRuntimeMode.Containerized : throw new Exception("The runtime mode must be set"); + +} diff --git a/src/core/Synapse.Core/Resources/ServiceAccount.cs b/src/core/Synapse.Core/Resources/ServiceAccount.cs new file mode 100644 index 000000000..4c2cf2ac8 --- /dev/null +++ b/src/core/Synapse.Core/Resources/ServiceAccount.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Resources; + +/// +/// Represents a resource used to configure a service security account +/// +[DataContract] +public record ServiceAccount + : Resource +{ + + /// + /// Gets the name of the default service account + /// + public const string DefaultServiceAccountName = "default"; + + /// + /// Gets the 's resource type + /// + public static readonly ResourceDefinitionInfo ResourceDefinition = new ServiceAccountResourceDefinition()!; + + /// + public ServiceAccount() : base(ResourceDefinition) { } + + /// + public ServiceAccount(ResourceMetadata metadata, ServiceAccountSpec spec) : base(ResourceDefinition, metadata, spec) { } + + +} diff --git a/src/core/Synapse.Core/Resources/ServiceAccount.yaml b/src/core/Synapse.Core/Resources/ServiceAccount.yaml new file mode 100644 index 000000000..4f4cf38a1 --- /dev/null +++ b/src/core/Synapse.Core/Resources/ServiceAccount.yaml @@ -0,0 +1,36 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: service-accounts.synapse.io +spec: + scope: Namespaced + group: synapse.io + names: + plural: service-accounts + singular: service-account + kind: ServiceAccount + shortNames: + - sa + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + description: Configures the Synapse Service Account + properties: + key: + type: string + description: The symmetric key used for authentication purposes + claims: + type: object + description: A type/value mapping of the claims associated to the service account + additionalProperties: + type: string + required: [key, claims] + required: + - spec \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/ServiceAccountResourceDefinition.cs b/src/core/Synapse.Core/Resources/ServiceAccountResourceDefinition.cs new file mode 100644 index 000000000..6ca166c01 --- /dev/null +++ b/src/core/Synapse.Core/Resources/ServiceAccountResourceDefinition.cs @@ -0,0 +1,64 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Neuroglia.Serialization.Yaml; + +namespace Synapse.Resources; + +/// +/// Represents the definition of an +/// +[DataContract] +public record ServiceAccountResourceDefinition + : ResourceDefinition +{ + + /// + /// Gets the definition of s + /// + public static new ResourceDefinition Instance { get; set; } + /// + /// Gets/sets the group resource definitions belong to + /// + public static new string ResourceGroup { get; set; } + /// + /// Gets/sets the resource version of resource definitions + /// + public static new string ResourceVersion { get; set; } + /// + /// Gets/sets the plural name of resource definitions + /// + public static new string ResourcePlural { get; set; } + /// + /// Gets/sets the kind of resource definitions + /// + public static new string ResourceKind { get; set; } + + static ServiceAccountResourceDefinition() + { + using var stream = typeof(Workflow).Assembly.GetManifestResourceStream($"{typeof(ServiceAccount).Namespace}.{nameof(ServiceAccount)}.yaml")!; + using var streamReader = new StreamReader(stream); + Instance = YamlSerializer.Default.Deserialize(streamReader.ReadToEnd())!; + ResourceGroup = Instance.Spec.Group; + ResourceVersion = Instance.Spec.Versions.Last().Name; + ResourcePlural = Instance.Spec.Names.Plural; + ResourceKind = Instance.Spec.Names.Kind; + } + + /// + /// Initializes a new + /// + public ServiceAccountResourceDefinition() : base(Instance) { } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/ServiceAccountSpec.cs b/src/core/Synapse.Core/Resources/ServiceAccountSpec.cs new file mode 100644 index 000000000..7e3f75adc --- /dev/null +++ b/src/core/Synapse.Core/Resources/ServiceAccountSpec.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the object used to configure a service account +/// +[DataContract] +public record ServiceAccountSpec +{ + + /// + /// Gets/sets the service account's key + /// + [DataMember(Name = "key", Order = 1), JsonPropertyName("key"), JsonPropertyOrder(1), YamlMember(Alias = "key", Order = 1)] + public virtual string Key { get; set; } = null!; + + /// + /// Gets/sets the claims associated to the service account + /// + [DataMember(Name = "claims", Order = 2), JsonPropertyName("claims"), JsonPropertyOrder(2), YamlMember(Alias = "claims", Order = 2)] + public virtual IDictionary Claims { get; set; } = new Dictionary(); + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/StartWorkflowOutcomeDefinition.cs b/src/core/Synapse.Core/Resources/StartWorkflowOutcomeDefinition.cs new file mode 100644 index 000000000..5fac824b8 --- /dev/null +++ b/src/core/Synapse.Core/Resources/StartWorkflowOutcomeDefinition.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the definition of a correlation outcome used to start a new instance of a workflow +/// +[DataContract] +public record StartWorkflowOutcomeDefinition +{ + + /// + /// Gets/sets the workflow to start upon correlation + /// + [Required] + [DataMember(Name = "workflow", Order = 1), JsonPropertyName("workflow"), JsonPropertyOrder(1), YamlMember(Alias = "workflow", Order = 1)] + public required virtual WorkflowDefinitionReference Workflow { get; set; } + + /// + /// Gets/sets a key/value mapping of the input of the workflow to start upon correlation + /// + [Required] + [DataMember(Name = "input", Order = 2), JsonPropertyName("input"), JsonPropertyOrder(2), YamlMember(Alias = "input", Order = 2)] + public virtual IDictionary? Input { get; set; } + +} diff --git a/src/core/Synapse.Core/Resources/TaskInstance.cs b/src/core/Synapse.Core/Resources/TaskInstance.cs new file mode 100644 index 000000000..9e76f6488 --- /dev/null +++ b/src/core/Synapse.Core/Resources/TaskInstance.cs @@ -0,0 +1,134 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the resource used to describe the instance of a task +/// +[DataContract] +public record TaskInstance +{ + + /// + /// Gets the task's id + /// + [DataMember(Name = "id", Order = 1), JsonPropertyName("id"), JsonPropertyOrder(1), YamlMember(Alias = "id", Order = 1)] + public virtual string Id { get; set; } = Guid.NewGuid().ToString("N")[..15]; + + /// + /// Gets the task's name, if any + /// + [DataMember(Name = "name", Order = 2), JsonPropertyName("name"), JsonPropertyOrder(2), YamlMember(Alias = "name", Order = 2)] + public virtual string? Name { get; set; } + + /// + /// Gets/sets a relative uri that reference to the task's definition + /// + [DataMember(Name = "reference", Order = 3), JsonPropertyName("reference"), JsonPropertyOrder(3), YamlMember(Alias = "reference", Order = 3)] + public required virtual Uri Reference { get; set; } + + /// + /// Gets/sets a boolean indicating whether or not the task is part of an extension + /// + [DataMember(Name = "isExtension", Order = 4), JsonPropertyName("isExtension"), JsonPropertyOrder(4), YamlMember(Alias = "isExtension", Order = 4)] + public virtual bool IsExtension { get; set; } + + /// + /// Gets/sets the id of the task's parent, if any + /// + [DataMember(Name = "parentId", Order = 5), JsonPropertyName("parentId"), JsonPropertyOrder(5), YamlMember(Alias = "parentId", Order = 5)] + public virtual string? ParentId { get; set; } + + /// + /// Gets/sets the date and time the task was created at + /// + [DataMember(Name = "createdAt", Order = 6), JsonPropertyName("createdAt"), JsonPropertyOrder(6), YamlMember(Alias = "createdAt", Order = 6)] + public virtual DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.Now; + + /// + /// Gets/sets the date and time the task has been started at, if applicable + /// + [DataMember(Name = "startedAt", Order = 7), JsonPropertyName("startedAt"), JsonPropertyOrder(7), YamlMember(Alias = "startedAt", Order = 7)] + public virtual DateTimeOffset? StartedAt { get; set; } + + /// + /// Gets/sets the date and time the task has ended, if applicable + /// + [DataMember(Name = "endedAt", Order = 8), JsonPropertyName("endedAt"), JsonPropertyOrder(8), YamlMember(Alias = "endedAt", Order = 8)] + public virtual DateTimeOffset? EndedAt { get; set; } + + /// + /// Gets the task's status + /// + /// View + [DataMember(Name = "status", Order = 9), JsonPropertyName("status"), JsonPropertyOrder(9), YamlMember(Alias = "status", Order = 9)] + public virtual string? Status { get; set; } + + /// + /// Gets the reason, if any, why the task is in its actual status + /// + [DataMember(Name = "statusReason", Order = 10), JsonPropertyName("statusReason"), JsonPropertyOrder(10), YamlMember(Alias = "statusReason", Order = 10)] + public virtual string? StatusReason { get; set; } + + /// + /// Gets/sets a list that contains the task's runs, if any + /// + [DataMember(Name = "runs", Order = 11), JsonPropertyName("runs"), JsonPropertyOrder(11), YamlMember(Alias = "runs", Order = 11)] + public virtual EquatableList? Runs { get; set; } + + /// + /// Gets/sets a list that contains the task's retry attempts, if any + /// + [DataMember(Name = "retries", Order = 12), JsonPropertyName("retries"), JsonPropertyOrder(12), YamlMember(Alias = "retries", Order = 12)] + public virtual EquatableList? Retries { get; set; } + + /// + /// Gets/sets the error, if any, that has occurred during the task's execution + /// + [DataMember(Name = "error", Order = 13), JsonPropertyName("error"), JsonPropertyOrder(13), YamlMember(Alias = "error", Order = 13)] + public virtual Error? Error { get; set; } + + /// + /// Gets/sets a reference to the task's input data + /// + [Required] + [DataMember(Name = "inputReference", Order = 14), JsonPropertyName("inputReference"), JsonPropertyOrder(14), YamlMember(Alias = "inputReference", Order = 14)] + public virtual string? InputReference { get; set; } + + /// + /// Gets/sets a reference to the task's context data, if any + /// + [Required] + [DataMember(Name = "contextReference", Order = 15), JsonPropertyName("contextReference"), JsonPropertyOrder(15), YamlMember(Alias = "contextReference", Order = 15)] + public virtual string? ContextReference { get; set; } + + /// + /// Gets/sets a reference to the task's output data, if any, in case the task ran to completion + /// + [DataMember(Name = "outputReference", Order = 16), JsonPropertyName("outputReference"), JsonPropertyOrder(16), YamlMember(Alias = "outputReference", Order = 16)] + public virtual string? OutputReference { get; set; } + + /// + /// Gets/sets the that must be performed next, in case the task ran to completion + /// + [DataMember(Name = "next", Order = 17), JsonPropertyName("next"), JsonPropertyOrder(17), YamlMember(Alias = "next", Order = 17)] + public virtual string? Next { get; set; } + + /// + /// Gets a value indicating whether the task is in an operative state, meaning it is pending, running, or suspended, and can potentially resume execution. + /// + [IgnoreDataMember, JsonIgnore, YamlIgnore] + public virtual bool IsOperative => this.Status == TaskInstanceStatus.Pending || this.Status == TaskInstanceStatus.Running || this.Status == TaskInstanceStatus.Suspended; + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/TaskInstanceStatus.cs b/src/core/Synapse.Core/Resources/TaskInstanceStatus.cs new file mode 100644 index 000000000..b5a4bd532 --- /dev/null +++ b/src/core/Synapse.Core/Resources/TaskInstanceStatus.cs @@ -0,0 +1,65 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Exposes default task statuses +/// +public static class TaskInstanceStatus +{ + + /// + /// Indicates that the task has been created and is pending execution + /// + public const string Pending = "pending"; + /// + /// Indicates that the task is running + /// + public const string Running = "running"; + /// + /// Indicates that the task encountered an error or exception during execution + /// + public const string Faulted = "faulted"; + /// + /// Indicates that the task has been explicitly omitted or bypassed during execution, probably because that task failed the condition defined by its `if` property + /// + public const string Skipped = "skipped"; + /// + /// Indicates that the task was suspended + /// + public const string Suspended = "suspended"; + /// + /// Indicates that the task was terminated or aborted before completion + /// + public const string Cancelled = "cancelled"; + /// + /// Indicates that the task ran to completion + /// + public const string Completed = "completed"; + + /// + /// Gets an containing default task statuses + /// + /// A new containing default task statuses + public static IEnumerable AsEnumerable() + { + yield return Pending; + yield return Running; + yield return Faulted; + yield return Skipped; + yield return Cancelled; + yield return Completed; + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/TaskRun.cs b/src/core/Synapse.Core/Resources/TaskRun.cs new file mode 100644 index 000000000..491bf2241 --- /dev/null +++ b/src/core/Synapse.Core/Resources/TaskRun.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents a single run of a task, including start and end times +/// +[DataContract] +public record TaskRun +{ + + /// + /// Gets/sets the start time of the run + /// + [DataMember(Name = "startedAt", Order = 1), JsonPropertyName("startedAt"), JsonPropertyOrder(1), YamlMember(Alias = "startedAt", Order = 1)] + public required virtual DateTimeOffset StartedAt { get; set; } + + /// + /// Gets/sets the end time of the run, if the task has completed + /// + [DataMember(Name = "endedAt", Order = 2), JsonPropertyName("endedAt"), JsonPropertyOrder(2), YamlMember(Alias = "endedAt", Order = 2)] + public virtual DateTimeOffset? EndedAt { get; set; } + + /// + /// Gets/sets the run's outcome or, in other words, the status of the task when the run ended + /// + [DataMember(Name = "outcome", Order = 3), JsonPropertyName("outcome"), JsonPropertyOrder(2), YamlMember(Alias = "outcome", Order = 3)] + public virtual string? Outcome { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/Workflow.cs b/src/core/Synapse.Core/Resources/Workflow.cs new file mode 100644 index 000000000..7c6bfd34b --- /dev/null +++ b/src/core/Synapse.Core/Resources/Workflow.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Resources; + +/// +/// Represents the resource used to describe and configure a workflow +/// +[DataContract] +public record Workflow + : Resource +{ + + /// + /// Gets the 's resource type + /// + public static readonly ResourceDefinitionInfo ResourceDefinition = new WorkflowResourceDefinition()!; + + /// + public Workflow() : base(ResourceDefinition) { this.Status = new(); } + + /// + public Workflow(ResourceMetadata metadata, WorkflowSpec spec) : base(ResourceDefinition, metadata, spec, new()) { } + +} diff --git a/src/core/Synapse.Core/Resources/Workflow.yaml b/src/core/Synapse.Core/Resources/Workflow.yaml new file mode 100644 index 000000000..760abf728 --- /dev/null +++ b/src/core/Synapse.Core/Resources/Workflow.yaml @@ -0,0 +1,26 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: workflows.synapse.io +spec: + scope: Namespaced + group: synapse.io + names: + plural: workflows + singular: workflow + kind: Workflow + shortNames: + - wf + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: {} #todo + required: + - spec \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/WorkflowDefinitionReference.cs b/src/core/Synapse.Core/Resources/WorkflowDefinitionReference.cs new file mode 100644 index 000000000..55738c012 --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowDefinitionReference.cs @@ -0,0 +1,102 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents a reference to a +/// +[DataContract] +public record WorkflowDefinitionReference +{ + + /// + /// Gets/sets the name of the referenced workflow definition + /// + [Required, MinLength(1)] + [DataMember(Name = "name", Order = 1), JsonPropertyName("name"), JsonPropertyOrder(1), YamlMember(Alias = "name", Order = 1)] + public required virtual string Name { get; set; } + + /// + /// Gets/sets the namespace of the referenced workflow definition + /// + [Required, MinLength(1)] + [DataMember(Name = "namespace", Order = 2), JsonPropertyName("namespace"), JsonPropertyOrder(2), YamlMember(Alias = "namespace", Order = 2)] + public required virtual string Namespace { get; set; } + + /// + /// Gets/sets the semantic version of the referenced workflow definition + /// + [Required, MinLength(1)] + [DataMember(Name = "version", Order = 3), JsonPropertyName("version"), JsonPropertyOrder(3), YamlMember(Alias = "version", Order = 3)] + public required virtual string Version { get; set; } + + /// + public override string ToString() => $"{this.Name}.{this.Namespace}:{this.Version}"; + + /// + /// Parses the specified input into a new + /// + /// The input to parse + /// The parsed + public static WorkflowDefinitionReference Parse(string input) + { + ArgumentException.ThrowIfNullOrWhiteSpace(input); + var components = input.Trim().Split(':', StringSplitOptions.RemoveEmptyEntries); + var qualifiedName = components[0]; + var version = components[1]; + components = qualifiedName.Split('.', StringSplitOptions.RemoveEmptyEntries); + var @namespace = components[0]; + var name = components[1]; + return new() + { + Name = name, + Namespace = @namespace, + Version = version + }; + } + + /// + /// Attempts to parse the specified input into a new + /// + /// The input to parse + /// The The parsed , if any + /// A boolean indicating whether or not the specified input could be parsed into a new + public static bool TryParse(string input, out WorkflowDefinitionReference? reference) + { + ArgumentException.ThrowIfNullOrWhiteSpace(input); + reference = null; + try + { + reference = Parse(input); + return true; + } + catch + { + return false; + } + } + + /// + /// Implicitly converts the specified reference into string + /// + /// The reference to convert + public static implicit operator string(WorkflowDefinitionReference reference) => reference.ToString(); + + /// + /// Implicitly parses the specified string into a new reference + /// + /// The string to parse + public static implicit operator WorkflowDefinitionReference(string reference) => Parse(reference); + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/WorkflowInstance.cs b/src/core/Synapse.Core/Resources/WorkflowInstance.cs new file mode 100644 index 000000000..f470e7f59 --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowInstance.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; + +namespace Synapse.Resources; + +/// +/// Represents the resource used to describe a workflow instance +/// +[DataContract] +public record WorkflowInstance + : Resource +{ + + /// + /// Gets the 's resource type + /// + public static readonly ResourceDefinitionInfo ResourceDefinition = new WorkflowInstanceResourceDefinition()!; + + /// + public WorkflowInstance() : base(ResourceDefinition) { } + + /// + public WorkflowInstance(ResourceMetadata metadata, WorkflowInstanceSpec spec) : base(ResourceDefinition, metadata, spec) { } + + /// + /// Gets a value indicating whether the workflow is in an operative state, meaning it is pending, running, or suspended, and can potentially resume execution. + /// + [IgnoreDataMember, JsonIgnore, YamlIgnore] + public virtual bool IsOperative => this.Status?.Phase == TaskInstanceStatus.Pending || this.Status?.Phase == TaskInstanceStatus.Running || this.Status?.Phase == TaskInstanceStatus.Suspended; + +} diff --git a/src/core/Synapse.Core/Resources/WorkflowInstance.yaml b/src/core/Synapse.Core/Resources/WorkflowInstance.yaml new file mode 100644 index 000000000..147738fe9 --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowInstance.yaml @@ -0,0 +1,32 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: workflow-instances.synapse.io +spec: + scope: Namespaced + group: synapse.io + names: + plural: workflow-instances + singular: workflow-instance + kind: WorkflowInstance + shortNames: + - wfi + versions: + - name: v1 + served: true + storage: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + input: + type: object + status: + type: object + required: + - spec + subresources: + status: {} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/WorkflowInstanceCorrelationStatus.cs b/src/core/Synapse.Core/Resources/WorkflowInstanceCorrelationStatus.cs new file mode 100644 index 000000000..4fb8ff0af --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowInstanceCorrelationStatus.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe the status of a workflow instance correlation +/// +[DataContract] +public record WorkflowInstanceCorrelationStatus +{ + + /// + /// Gets/sets a name/value containing the workflow instance's correlation keys + /// + [DataMember(Order = 1, Name = "keys"), JsonPropertyName("keys"), JsonPropertyOrder(1), YamlMember(Alias = "keys", Order = 1)] + public virtual EquatableDictionary? Keys { get; set; } + + /// + /// Gets/sets a name/value containing the workflow instance's correlation contexts pending processing + /// + [DataMember(Order = 2, Name = "contexts"), JsonPropertyName("contexts"), JsonPropertyOrder(2), YamlMember(Alias = "contexts", Order = 2)] + public virtual EquatableDictionary? Contexts { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/WorkflowInstanceResourceDefinition.cs b/src/core/Synapse.Core/Resources/WorkflowInstanceResourceDefinition.cs new file mode 100644 index 000000000..8df4ec33b --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowInstanceResourceDefinition.cs @@ -0,0 +1,64 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Neuroglia.Serialization.Yaml; + +namespace Synapse.Resources; + +/// +/// Represents the definition of a +/// +[DataContract] +public record WorkflowInstanceResourceDefinition + : ResourceDefinition +{ + + /// + /// Gets the definition of s + /// + public static new ResourceDefinition Instance { get; set; } + /// + /// Gets/sets the group resource definitions belong to + /// + public static new string ResourceGroup { get; set; } + /// + /// Gets/sets the resource version of resource definitions + /// + public static new string ResourceVersion { get; set; } + /// + /// Gets/sets the plural name of resource definitions + /// + public static new string ResourcePlural { get; set; } + /// + /// Gets/sets the kind of resource definitions + /// + public static new string ResourceKind { get; set; } + + static WorkflowInstanceResourceDefinition() + { + using var stream = typeof(WorkflowInstance).Assembly.GetManifestResourceStream($"{typeof(WorkflowInstance).Namespace}.{nameof(WorkflowInstance)}.yaml")!; + using var streamReader = new StreamReader(stream); + Instance = YamlSerializer.Default.Deserialize(streamReader.ReadToEnd())!; + ResourceGroup = Instance.Spec.Group; + ResourceVersion = Instance.Spec.Versions.Last().Name; + ResourcePlural = Instance.Spec.Names.Plural; + ResourceKind = Instance.Spec.Names.Kind; + } + + /// + /// Initializes a new + /// + public WorkflowInstanceResourceDefinition() : base(Instance) { } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/WorkflowInstanceSpec.cs b/src/core/Synapse.Core/Resources/WorkflowInstanceSpec.cs new file mode 100644 index 000000000..b5eaa5947 --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowInstanceSpec.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the configuration of a workflow instance resource +/// +[DataContract] +public record WorkflowInstanceSpec +{ + + /// + /// Gets/sets the definition of the workflow to run + /// + [Required] + [DataMember(Name = "definition", Order = 1), JsonPropertyName("definition"), JsonPropertyOrder(1), YamlMember(Alias = "definition", Order = 1)] + public virtual WorkflowDefinitionReference Definition { get; set; } = null!; + + /// + /// Gets/sets a name/value mapping of the workflow's input data + /// + [Required] + [DataMember(Name = "input", Order = 2), JsonPropertyName("input"), JsonPropertyOrder(2), YamlMember(Alias = "input", Order = 2)] + public virtual EquatableDictionary? Input { get; set; } + +} diff --git a/src/core/Synapse.Core/Resources/WorkflowInstanceStatus.cs b/src/core/Synapse.Core/Resources/WorkflowInstanceStatus.cs new file mode 100644 index 000000000..06b52549e --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowInstanceStatus.cs @@ -0,0 +1,79 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the status of a workflow instance resource +/// +[DataContract] +public record WorkflowInstanceStatus +{ + + /// + /// Gets/sets the current phase of the workflow + /// + [DataMember(Order = 1, Name = "phase"), JsonPropertyName("phase"), JsonPropertyOrder(1), YamlMember(Alias = "phase", Order = 1)] + public virtual string? Phase { get; set; } + + /// + /// Gets/sets the date and time the task has been started at, if applicable + /// + [DataMember(Name = "startedAt", Order = 2), JsonPropertyName("startedAt"), JsonPropertyOrder(2), YamlMember(Alias = "startedAt", Order = 2)] + public virtual DateTimeOffset? StartedAt { get; set; } + + /// + /// Gets/sets the date and time the task has ended, if applicable + /// + [DataMember(Name = "endedAt", Order = 3), JsonPropertyName("endedAt"), JsonPropertyOrder(3), YamlMember(Alias = "endedAt", Order = 3)] + public virtual DateTimeOffset? EndedAt { get; set; } + + /// + /// Gets/sets a list containing the tasks that are being performed -or already have been performed- by the workflow + /// + [DataMember(Order = 4, Name = "tasks"), JsonPropertyName("tasks"), JsonPropertyOrder(4), YamlMember(Alias = "tasks", Order = 4)] + public virtual EquatableList? Tasks { get; set; } + + /// + /// Gets/sets a list that contains the workflow's runs, if any + /// + [DataMember(Order = 5, Name = "runs"), JsonPropertyName("runs"), JsonPropertyOrder(5), YamlMember(Alias = "runs", Order = 5)] + public virtual EquatableList? Runs { get; set; } + + /// + /// Gets/sets a name/context map that contains the workflow's pending correlations + /// + [DataMember(Order = 6, Name = "correlation"), JsonPropertyName("correlation"), JsonPropertyOrder(6), YamlMember(Alias = "correlation", Order = 6)] + public virtual WorkflowInstanceCorrelationStatus? Correlation { get; set; } + + /// + /// Gets/sets the error, if any, that has occurred during the workflow's execution + /// + [DataMember(Name = "error", Order = 7), JsonPropertyName("error"), JsonPropertyOrder(7), YamlMember(Alias = "error", Order = 7)] + public virtual Error? Error { get; set; } + + /// + /// Gets/sets a reference to the workflow's context data, if any + /// + [Required, MinLength(1)] + [DataMember(Order = 8, Name = "contextReference"), JsonPropertyName("contextReference"), JsonPropertyOrder(8), YamlMember(Alias = "contextReference", Order = 8)] + public virtual string ContextReference { get; set; } = null!; + + /// + /// Gets/sets a reference to the workflow's context data, if any + /// + [Required, MinLength(1)] + [DataMember(Order = 9, Name = "outputReference"), JsonPropertyName("outputReference"), JsonPropertyOrder(9), YamlMember(Alias = "outputReference", Order = 9)] + public virtual string? OutputReference { get; set; } + +} diff --git a/src/core/Synapse.Core/Resources/WorkflowResourceDefinition.cs b/src/core/Synapse.Core/Resources/WorkflowResourceDefinition.cs new file mode 100644 index 000000000..c2d0b4e27 --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowResourceDefinition.cs @@ -0,0 +1,64 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Data.Infrastructure.ResourceOriented; +using Neuroglia.Serialization.Yaml; + +namespace Synapse.Resources; + +/// +/// Represents the definition of a +/// +[DataContract] +public record WorkflowResourceDefinition + : ResourceDefinition +{ + + /// + /// Gets the definition of s + /// + public static new ResourceDefinition Instance { get; set; } + /// + /// Gets/sets the group resource definitions belong to + /// + public static new string ResourceGroup { get; set; } + /// + /// Gets/sets the resource version of resource definitions + /// + public static new string ResourceVersion { get; set; } + /// + /// Gets/sets the plural name of resource definitions + /// + public static new string ResourcePlural { get; set; } + /// + /// Gets/sets the kind of resource definitions + /// + public static new string ResourceKind { get; set; } + + static WorkflowResourceDefinition() + { + using var stream = typeof(Workflow).Assembly.GetManifestResourceStream($"{typeof(WorkflowInstance).Namespace}.{nameof(Workflow)}.yaml")!; + using var streamReader = new StreamReader(stream); + Instance = YamlSerializer.Default.Deserialize(streamReader.ReadToEnd())!; + ResourceGroup = Instance.Spec.Group; + ResourceVersion = Instance.Spec.Versions.Last().Name; + ResourcePlural = Instance.Spec.Names.Plural; + ResourceKind = Instance.Spec.Names.Kind; + } + + /// + /// Initializes a new + /// + public WorkflowResourceDefinition() : base(Instance) { } + +} diff --git a/src/core/Synapse.Core/Resources/WorkflowRun.cs b/src/core/Synapse.Core/Resources/WorkflowRun.cs new file mode 100644 index 000000000..7bc375c2a --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowRun.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents a single run of a workflow, including start and end times +/// +[DataContract] +public record WorkflowRun +{ + + /// + /// Gets/sets the start time of the run + /// + [DataMember(Name = "startedAt", Order = 1), JsonPropertyName("startedAt"), JsonPropertyOrder(1), YamlMember(Alias = "startedAt", Order = 1)] + public required virtual DateTimeOffset StartedAt { get; set; } + + /// + /// Gets/sets the end time of the run, if the workflow has completed + /// + [DataMember(Name = "endedAt", Order = 2), JsonPropertyName("endedAt"), JsonPropertyOrder(2), YamlMember(Alias = "endedAt", Order = 2)] + public virtual DateTimeOffset? EndedAt { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/WorkflowSpec.cs b/src/core/Synapse.Core/Resources/WorkflowSpec.cs new file mode 100644 index 000000000..6c798746e --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowSpec.cs @@ -0,0 +1,30 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents the configuration of a workflow +/// +[DataContract] +public record WorkflowSpec +{ + + /// + /// Gets/sets the versions of the configured workflow + /// + [Required, MinLength(1)] + [DataMember(Name = "versions", Order = 1), JsonPropertyName("versions"), JsonPropertyOrder(1), YamlMember(Alias = "versions", Order = 1)] + public virtual EquatableList Versions { get; set; } = null!; + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Resources/WorkflowStatus.cs b/src/core/Synapse.Core/Resources/WorkflowStatus.cs new file mode 100644 index 000000000..ef292f3f5 --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowStatus.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe the status of a workflow +/// +[DataContract] +public record WorkflowStatus +{ + + /// + /// Gets/sets a key/value mapping of the status of the workflow's versions + /// + [DataMember(Order = 1, Name = "versions"), JsonPropertyOrder(1), JsonPropertyName("versions"), YamlMember(Order = 1, Alias = "versions")] + public virtual EquatableDictionary Versions { get; set; } = []; + +} diff --git a/src/core/Synapse.Core/Resources/WorkflowVersionStatus.cs b/src/core/Synapse.Core/Resources/WorkflowVersionStatus.cs new file mode 100644 index 000000000..eb71dcca6 --- /dev/null +++ b/src/core/Synapse.Core/Resources/WorkflowVersionStatus.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Resources; + +/// +/// Represents an object used to describe the status of a workflow version +/// +[DataContract] +public record WorkflowVersionStatus +{ + + /// + /// Gets/sets the total instance count + /// + [DataMember(Name = "totalInstances", Order = 1), JsonPropertyName("totalInstances"), JsonPropertyOrder(1), YamlMember(Alias = "totalInstances", Order = 1)] + public virtual int TotalInstances { get; set; } + + /// + /// Gets/sets the date and time at which the last instance, if any, has started + /// + [DataMember(Name = "lastStartedAt", Order = 2), JsonPropertyName("lastStartedAt"), JsonPropertyOrder(2), YamlMember(Alias = "lastStartedAt", Order = 2)] + public virtual DateTimeOffset? LastStartedAt { get; set; } + + /// + /// Gets/sets the date and time at which the last instance, if any, has been executed + /// + [DataMember(Name = "lastEndedAt", Order = 3), JsonPropertyName("lastEndedAt"), JsonPropertyOrder(3), YamlMember(Alias = "lastEndedAt", Order = 3)] + public virtual DateTimeOffset? LastEndedAt { get; set; } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/Synapse.Core.csproj b/src/core/Synapse.Core/Synapse.Core.csproj new file mode 100644 index 000000000..1cf1679ad --- /dev/null +++ b/src/core/Synapse.Core/Synapse.Core.csproj @@ -0,0 +1,56 @@ + + + + net8.0 + enable + enable + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse core + true + true + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + $(MSBuildProjectName.Replace(" ", "_").Replace(".Core", "")) + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core/Synapse.Core/SynapseDefaults.cs b/src/core/Synapse.Core/SynapseDefaults.cs new file mode 100644 index 000000000..a1ff12eb2 --- /dev/null +++ b/src/core/Synapse.Core/SynapseDefaults.cs @@ -0,0 +1,418 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; +using Neuroglia.Data.Infrastructure.ResourceOriented; +using System.Diagnostics; + +namespace Synapse; + +/// +/// Exposes defaults about Synapse +/// +public static class SynapseDefaults +{ + + /// + /// Gets the default group for Synapse resources + /// + public const string ResourceGroup = "synapse.io"; + + /// + /// Exposes Synapse audiences + /// + public static class Audiences + { + + /// + /// Gets the Synapse API audience + /// + public const string Api = "synapse-api"; + + } + + /// + /// Exposes Synapse environment variables + /// + public static class EnvironmentVariables + { + + /// + /// Gets the prefix for all Synapse environment variables + /// + public const string Prefix = "SYNAPSE_"; + + /// + /// Gets the environment variable used to configure Synapse to skip certificate validation by default, for example when performing HTTP requests + /// + public const string SkipCertificateValidation = Prefix + "SKIP_CERTIFICATE_VALIDATION"; + + /// + /// Exposes constants about API-related environment variables + /// + public static class Api + { + + /// + /// Gets the prefix for all API related environment variables + /// + public const string Prefix = EnvironmentVariables.Prefix + "API_"; + + /// + /// Gets the environment variable used to configure the base uri of the Synapse API to use + /// + public const string Uri = Prefix + "URI"; + /// + /// Gets the token to use to connect to the Synapse API + /// + public const string Token = Prefix + "TOKEN"; + + /// + /// Exposes constants about environment variables related to API authentication + /// + public static class Authentication + { + + /// + /// Gets the prefix for all API related environment variables + /// + public const string Prefix = Api.Prefix + "AUTH_"; + + /// + /// Gets the name of the environment variables used to specify the YAML file that defines the users to generate a static token for + /// + public const string File = Prefix + "TOKEN_FILE"; + + /// + /// Exposes constants about environment variables related to the API's JWT Bearer authentication scheme, if any + /// + public static class Jwt + { + + /// + /// Gets the prefix for all JWT Bearer related environment variables + /// + public const string Prefix = Authentication.Prefix + "JWT_"; + + /// + /// Gets the name of the environment variables used to specify the JWT Bearer authority to use + /// + public const string Authority = Prefix + "AUTHORITY"; + /// + /// Gets the name of the environment variables used to specify the JWT Bearer audience + /// + public const string Audience = Prefix + "AUDIENCE"; + + } + + /// + /// Exposes constants about environment variables related to the API's OIDC authentication scheme, if any + /// + public static class Oidc + { + + /// + /// Gets the prefix for all OIDC related environment variables + /// + public const string Prefix = Authentication.Prefix + "OIDC_"; + + /// + /// Gets the name of the environment variables used to specify the OIDC authority to use + /// + public const string Authority = Prefix + "AUTHORITY"; + /// + /// Gets the name of the environment variables used to specify the OIDC client id + /// + public const string ClientId = Prefix + "CLIENT_ID"; + /// + /// Gets the name of the environment variables used to specify the OIDC client secret + /// + public const string ClientSecret = Prefix + "CLIENT_SECRET"; + /// + /// Gets the name of the environment variables used to define the comma-separated OIDC scope(s) to use + /// + public const string Scope = Prefix + "SCOPE"; + /// + /// Gets the name of the environment variables used to define the key used by the OIDC authority to sign tokens + /// + public const string SigningKey = Prefix + "SIGNING_KEY"; + + } + + } + + } + + /// + /// Exposes constants about correlator-related environment variables + /// + public static class Correlator + { + + /// + /// Gets the prefix for all correlator related environment variables + /// + public const string Prefix = EnvironmentVariables.Prefix + "CORRELATOR_"; + + /// + /// Gets the environment variable used to configure the correlator's namespace + /// + public const string Namespace = Prefix + "NAMESPACE"; + /// + /// Gets the environment variable used to configure the correlator's name + /// + public const string Name = Prefix + "NAME"; + + } + + /// + /// Exposes constants about dashboard-related environment variables + /// + public static class Dashboard + { + + /// + /// Gets the prefix for all dashboard related environment variables + /// + public const string Prefix = EnvironmentVariables.Prefix + "DASHBOARD_"; + + /// + /// Gets the environment variable used to determine whether or not to serve the dashboard + /// + public const string Serve = Prefix + "SERVE"; + + } + + /// + /// Exposes constants about operator-related environment variables + /// + public static class Operator + { + + /// + /// Gets the prefix for all operator related environment variables + /// + public const string Prefix = EnvironmentVariables.Prefix + "OPERATOR_"; + + /// + /// Gets the environment variable used to configure the operator's namespace + /// + public const string Namespace = Prefix + "NAMESPACE"; + /// + /// Gets the environment variable used to configure the operator's name + /// + public const string Name = Prefix + "NAME"; + + /// + /// Exposes constants about an operator's runner-related environment variables + /// + public static class Runner + { + + /// + /// Gets the prefix for all operator runner related environment variables + /// + public const string Prefix = Operator.Prefix + "RUNNER_"; + + /// + /// Gets the environment variable used to configure the API used by runners spawned by the operator + /// + public const string Api = Prefix + "API"; + + } + + } + + /// + /// Exposes constants about service account related environment variables + /// + public static class ServiceAccount + { + + /// + /// Gets the prefix for all operator related environment variables + /// + public const string Prefix = EnvironmentVariables.Prefix + "SERVICEACCOUNT_"; + + /// + /// Gets the environment variable used to configure the service account name of a Synapse Runner + /// + public const string Name = Prefix + "NAME"; + /// + /// Gets the environment variable used to configure the key of a Synapse Runner's service account + /// + public const string Key = Prefix + "KEY"; + + } + + /// + /// Exposes constants about workflow-related environment variables + /// + public static class Workflow + { + + /// + /// Gets the prefix for all api related environment variables + /// + public const string Prefix = EnvironmentVariables.Prefix + "WORKFLOW_"; + + /// + /// Gets the environment variable that holds the qualified name of workflow instance to run + /// + public const string Instance = Prefix + "INSTANCE"; + + } + + } + + /// + /// Exposes Synapse default resources + /// + public static class Resources + { + + /// + /// Exposes Synapse resource definitions + /// + public static class Definitions + { + + /// + /// Gets the definition of Correlation resources + /// + public static ResourceDefinition Correlation { get; } = new CorrelationResourceDefinition(); + + /// + /// Gets the definition of Correlator resources + /// + public static ResourceDefinition Correlator { get; } = new CorrelatorResourceDefinition(); + + /// + /// Gets the definition of Operator resources + /// + public static ResourceDefinition Operator { get; } = new OperatorResourceDefinition(); + + /// + /// Gets the definition of ServiceAccount resources + /// + public static ResourceDefinition ServiceAccount { get; } = new ServiceAccountResourceDefinition(); + + /// + /// Gets the definition of Workflow resources + /// + public static ResourceDefinition Workflow { get; } = new WorkflowResourceDefinition(); + + /// + /// Gets the definition of WorkflowInstance resources + /// + public static ResourceDefinition WorkflowInstance { get; } = new WorkflowInstanceResourceDefinition(); + + /// + /// Gets a new containing Synapse default resource definitions + /// + /// + public static IEnumerable AsEnumerable() + { + yield return Correlation; + yield return Correlator; + yield return Operator; + yield return ServiceAccount; + yield return Workflow; + yield return WorkflowInstance; + } + + } + + /// + /// Exposes the resource labels used by Synapse + /// + public static class Labels + { + + /// + /// Gets the prefix of all Synapse labels + /// + public const string Prefix = "synapse.io/"; + + /// + /// Gets the label used by Synapse correlators to claim correlations + /// + public const string Correlator = Prefix + "correlator"; + /// + /// Gets the label used by Synapse operators to claim workflows or workflow instances + /// + public const string Operator = Prefix + "operator"; + /// + /// Gets the label used by Synapse to indicate the qualified name of the workflow used by a workflow instance + /// + public const string Workflow = Prefix + "workflow"; + /// + /// Gets the label used by Synapse to indicate the version of the workflow used by a workflow instance + /// + public const string WorkflowVersion = Prefix + "workflow/version"; + /// + /// Gets the label used by Synapse to indicate the qualified name of the workflow instance that owns a concept, such as a correlation + /// + public const string WorkflowInstance = Prefix + "workflow-instance"; + + } + + } + + /// + /// Exposes constants about Synapse tasks + /// + public static class Tasks + { + + /// + /// Exposes Synapse task extension properties + /// + public static class ExtensionProperties + { + + /// + /// Exposes constants about the extension property used to determine whether or not to prefix the path of child tasks with the task's keyword (ex: `do`) + /// + public static class PathPrefix + { + + /// + /// Gets the name of the 'PathPrefix' property + /// + public const string Name = "PathPrefix"; + /// + /// Gets the type of the 'PathPrefix' property + /// + public static readonly Type Type = typeof(bool); + + } + + } + + } + + /// + /// Exposes constants about Synapse application telemetry + /// + public static class Telemetry + { + + /// + /// Exposes the Synapse application's + /// + public static ActivitySource ActivitySource { get; set; } = null!; + + } + +} \ No newline at end of file diff --git a/src/core/Synapse.Core/TaskInstanceStatus.cs b/src/core/Synapse.Core/TaskInstanceStatus.cs new file mode 100644 index 000000000..e26bd54df --- /dev/null +++ b/src/core/Synapse.Core/TaskInstanceStatus.cs @@ -0,0 +1,65 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Exposes default task instance statuses +/// +public static class TaskInstanceStatus +{ + + /// + /// Indicates that the task has been created and is pending execution + /// + public const string Pending = "pending"; + /// + /// Indicates that the task is running + /// + public const string Running = "running"; + /// + /// Indicates that the task encountered an error or exception during execution + /// + public const string Faulted = "faulted"; + /// + /// Indicates that the task has been explicitly omitted or bypassed during execution, probably because that task failed the condition defined by its `if` property + /// + public const string Skipped = "skipped"; + /// + /// Indicates that the task was suspended + /// + public const string Suspended = "suspended"; + /// + /// Indicates that the task was terminated or aborted before completion + /// + public const string Cancelled = "cancelled"; + /// + /// Indicates that the task ran to completion + /// + public const string Completed = "completed"; + + /// + /// Gets an containing default task statuses + /// + /// A new containing default task statuses + public static IEnumerable AsEnumerable() + { + yield return Pending; + yield return Running; + yield return Faulted; + yield return Skipped; + yield return Cancelled; + yield return Completed; + } + +} diff --git a/src/core/Synapse.Core/Usings.cs b/src/core/Synapse.Core/Usings.cs new file mode 100644 index 000000000..1d151b267 --- /dev/null +++ b/src/core/Synapse.Core/Usings.cs @@ -0,0 +1,20 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using ServerlessWorkflow.Sdk; +global using ServerlessWorkflow.Sdk.Models; +global using Neuroglia; +global using System.ComponentModel.DataAnnotations; +global using System.Runtime.Serialization; +global using System.Text.Json.Serialization; +global using YamlDotNet.Serialization; diff --git a/src/core/Synapse.Core/WorkflowInstanceStatusPhase.cs b/src/core/Synapse.Core/WorkflowInstanceStatusPhase.cs new file mode 100644 index 000000000..bee4172c6 --- /dev/null +++ b/src/core/Synapse.Core/WorkflowInstanceStatusPhase.cs @@ -0,0 +1,47 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse; + +/// +/// Exposes the default status phases that a workflow can enter in Synapse. +/// +public static class WorkflowInstanceStatusPhase +{ + + /// + /// Indicates that the workflow is pending execution + /// + public const string Pending = "pending"; + /// + /// Indicates that the workflow is being executed + /// + public const string Running = "running"; + /// + /// Indicates that the workflow ran to completion + /// + public const string Completed = "completed"; + /// + /// Indicates that the workflow's execution is waiting for user or event input + /// + public const string Waiting = "waiting"; + /// + /// Indicates that the workflow's execution has been cancelled + /// + public const string Cancelled = "cancelled"; + /// + /// Indicates that the workflow encountered an unhandled error during its execution and consequently faulted + /// + public const string Faulted = "faulted"; + +} \ No newline at end of file diff --git a/src/core/Synapse.Domain/Events/AuthenticationDefinitionCollections/v1/V1AuthenticationDefinitionCollectionCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/AuthenticationDefinitionCollections/v1/V1AuthenticationDefinitionCollectionCreatedDomainEvent.cs deleted file mode 100644 index 113485027..000000000 --- a/src/core/Synapse.Domain/Events/AuthenticationDefinitionCollections/v1/V1AuthenticationDefinitionCollectionCreatedDomainEvent.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.AuthenticationDefinitionCollections; - -namespace Synapse.Domain.Authentications.AuthenticationDefinitionCollections -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1AuthenticationDefinitionCollectionCreatedIntegrationEvent))] - public class V1AuthenticationDefinitionCollectionCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1AuthenticationDefinitionCollectionCreatedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The 's name - /// The 's version - /// The 's description - /// An containing the s the is made out of - public V1AuthenticationDefinitionCollectionCreatedDomainEvent(string id, string name, string version, string? description, IEnumerable functions) - : base(id) - { - this.Name = name; - this.Version = version; - this.Description = description; - this.Authentications = functions.ToList().AsReadOnly(); - } - - /// - /// Gets the 's name - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the 's version - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the 's description - /// - public virtual string? Description { get; protected set; } - - /// - /// Gets an containing the s the is made out of - /// - public virtual IReadOnlyCollection Authentications { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/AuthenticationDefinitionCollections/v1/V1AuthenticationDefinitionCollectionDeletedDomainEvent.cs b/src/core/Synapse.Domain/Events/AuthenticationDefinitionCollections/v1/V1AuthenticationDefinitionCollectionDeletedDomainEvent.cs deleted file mode 100644 index df6c89bbc..000000000 --- a/src/core/Synapse.Domain/Events/AuthenticationDefinitionCollections/v1/V1AuthenticationDefinitionCollectionDeletedDomainEvent.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.AuthenticationDefinitionCollections; - -namespace Synapse.Domain.Authentications.AuthenticationDefinitionCollections -{ - - ///

- /// Represents the fired whenever a new has been deleted - /// - [DataTransferObjectType(typeof(V1AuthenticationDefinitionCollectionDeletedIntegrationEvent))] - public class V1AuthenticationDefinitionCollectionDeletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1AuthenticationDefinitionCollectionDeletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the deleted - public V1AuthenticationDefinitionCollectionDeletedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Correlations/v1/V1ContextAddedToCorrelationDomainEvent.cs b/src/core/Synapse.Domain/Events/Correlations/v1/V1ContextAddedToCorrelationDomainEvent.cs deleted file mode 100644 index 09f00d4d6..000000000 --- a/src/core/Synapse.Domain/Events/Correlations/v1/V1ContextAddedToCorrelationDomainEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Correlations; - -namespace Synapse.Domain.Events.Correlations -{ - - ///

- /// Represents the fired whenever a new has been added to an existing - /// - [DataTransferObjectType(typeof(V1ContextAddedToCorrelationIntegrationEvent))] - public class V1ContextAddedToCorrelationDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ContextAddedToCorrelationDomainEvent() - { - this.Context = null!; - } - - /// - /// Initializes a new - /// - /// The id of a has been added to - /// The that has been added to the - public V1ContextAddedToCorrelationDomainEvent(string id, Models.V1CorrelationContext context) - : base(id) - { - this.Context = context; - } - - /// - /// Gets the that has been added to the - /// - public virtual Models.V1CorrelationContext Context { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelatedEventReleasedDomainEvent.cs b/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelatedEventReleasedDomainEvent.cs deleted file mode 100644 index 31bb98dc8..000000000 --- a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelatedEventReleasedDomainEvent.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Correlations; - -namespace Synapse.Domain.Events.Correlations -{ - ///

- /// Represents the fired whenever a has been released by a - /// - [DataTransferObjectType(typeof(V1CorrelatedEventReleasedIntegrationEvent))] - public class V1CorrelatedEventReleasedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1CorrelatedEventReleasedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the performed correlation - /// The id of the the has been released from - /// The id of the that has been released - public V1CorrelatedEventReleasedDomainEvent(string id, string contextId, string eventId) - : base(id) - { - this.ContextId = contextId; - this.EventId = eventId; - } - - /// - /// Gets the id of the the has been released from - /// - public virtual string ContextId { get; protected set; } = null!; - - /// - /// Gets the id of the that has been released - /// - public virtual string EventId { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationContextReleasedDomainEvent.cs b/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationContextReleasedDomainEvent.cs deleted file mode 100644 index cc7bec1dc..000000000 --- a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationContextReleasedDomainEvent.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Correlations; - -namespace Synapse.Domain.Events.Correlations -{ - - ///

- /// Represents the fired whenever a has been released by a - /// - [DataTransferObjectType(typeof(V1CorrelationContextReleasedIntegrationEvent))] - public class V1CorrelationContextReleasedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1CorrelationContextReleasedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the performed correlation - /// The id of the context that has been released - public V1CorrelationContextReleasedDomainEvent(string id, string contextId) - : base(id) - { - this.ContextId = contextId; - } - - /// - /// Gets the id of the context that has been released - /// - public virtual string ContextId { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationCreatedDomainEvent.cs deleted file mode 100644 index 1c2335c67..000000000 --- a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationCreatedDomainEvent.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Correlations; - -namespace Synapse.Domain.Events.Correlations -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1CorrelationCreatedIntegrationEvent))] - public class V1CorrelationCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1CorrelationCreatedDomainEvent() - { - this.Conditions = null!; - this.Outcome = null!; - } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The newly created 's activation type - /// The lifetime of the newly created - /// The type of evaluation the newly created should use - /// An containing all s the newly created is made out of - /// The of the newly created - /// The initial of the newly created - public V1CorrelationCreatedDomainEvent(string id, V1CorrelationActivationType activationType, V1CorrelationLifetime lifetime, V1CorrelationConditionType conditionType, - IEnumerable conditions, Models.V1CorrelationOutcome outcome, Models.V1CorrelationContext? context) - : base(id) - { - this.ActivationType = activationType; - this.Lifetime = lifetime; - this.ConditionType = conditionType; - this.Conditions = conditions; - this.Outcome = outcome; - this.Context = context; - } - - /// - /// Gets the newly created 's activation type - /// - public virtual V1CorrelationActivationType ActivationType { get; protected set; } - - /// - /// Gets the lifetime of the newly created - /// - public virtual V1CorrelationLifetime Lifetime { get; protected set; } - - /// - /// Gets the type of evaluation the newly created should use - /// - public virtual V1CorrelationConditionType ConditionType { get; protected set; } - - /// - /// Gets an containing all s the newly created is made out of - /// - public virtual IEnumerable Conditions { get; protected set; } - - /// - /// Gets the of the newly created - /// - public virtual Models.V1CorrelationOutcome Outcome { get; protected set; } - - /// - /// Gets the initial of the newly created - /// - public virtual Models.V1CorrelationContext? Context { get; protected set; } - - - } - -} diff --git a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationDeletedDomainEvent.cs b/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationDeletedDomainEvent.cs deleted file mode 100644 index ee12d3c90..000000000 --- a/src/core/Synapse.Domain/Events/Correlations/v1/V1CorrelationDeletedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Correlations; - -namespace Synapse.Domain.Events.Correlations -{ - ///

- /// Represents the fired whenever a new has been deleted - /// - [DataTransferObjectType(typeof(V1CorrelationCreatedIntegrationEvent))] - public class V1CorrelationDeletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1CorrelationDeletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has been deleted - public V1CorrelationDeletedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Correlations/v1/V1EventCorrelatedDomainEvent.cs b/src/core/Synapse.Domain/Events/Correlations/v1/V1EventCorrelatedDomainEvent.cs deleted file mode 100644 index efbb56a60..000000000 --- a/src/core/Synapse.Domain/Events/Correlations/v1/V1EventCorrelatedDomainEvent.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Correlations; - -namespace Synapse.Domain.Events.Correlations -{ - - ///

- /// Represents the fired whenever a new has been correlated - /// - [DataTransferObjectType(typeof(V1EventCorrelatedIntegrationEvent))] - public class V1EventCorrelatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1EventCorrelatedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the performed correlation - /// The id of the context in which the correlation has been performed - /// The that has been correlated - /// An containing the keys of the mappings used to correlate the - public V1EventCorrelatedDomainEvent(string id, string contextId, Models.V1Event e, IEnumerable mappings) - : base(id) - { - this.ContextId = contextId; - this.Event = e; - this.Mappings = mappings; - } - - /// - /// Gets the id of the context in which the correlation has been performed - /// - public virtual string ContextId { get; protected set; } = null!; - - /// - /// Gets the that has been correlated - /// - public virtual Models.V1Event Event { get; protected set; } = null!; - - /// - /// Gets an containing the keys of the mappings used to correlate the - /// - public virtual IEnumerable Mappings { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/EventDefinitionCollections/v1/V1EventDefinitionCollectionCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/EventDefinitionCollections/v1/V1EventDefinitionCollectionCreatedDomainEvent.cs deleted file mode 100644 index 4ba691b65..000000000 --- a/src/core/Synapse.Domain/Events/EventDefinitionCollections/v1/V1EventDefinitionCollectionCreatedDomainEvent.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.EventDefinitionCollections; - -namespace Synapse.Domain.Events.EventDefinitionCollections -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1EventDefinitionCollectionCreatedIntegrationEvent))] - public class V1EventDefinitionCollectionCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1EventDefinitionCollectionCreatedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The 's name - /// The 's version - /// The 's description - /// An containing the s the is made out of - public V1EventDefinitionCollectionCreatedDomainEvent(string id, string name, string version, string? description, IEnumerable functions) - : base(id) - { - this.Name = name; - this.Version = version; - this.Description = description; - this.Events = functions.ToList().AsReadOnly(); - } - - /// - /// Gets the 's name - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the 's version - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the 's description - /// - public virtual string? Description { get; protected set; } - - /// - /// Gets an containing the s the is made out of - /// - public virtual IReadOnlyCollection Events { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/EventDefinitionCollections/v1/V1EventDefinitionCollectionDeletedDomainEvent.cs b/src/core/Synapse.Domain/Events/EventDefinitionCollections/v1/V1EventDefinitionCollectionDeletedDomainEvent.cs deleted file mode 100644 index fed8ae0c7..000000000 --- a/src/core/Synapse.Domain/Events/EventDefinitionCollections/v1/V1EventDefinitionCollectionDeletedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.EventDefinitionCollections; - -namespace Synapse.Domain.Events.EventDefinitionCollections -{ - ///

- /// Represents the fired whenever a new has been deleted - /// - [DataTransferObjectType(typeof(V1EventDefinitionCollectionDeletedIntegrationEvent))] - public class V1EventDefinitionCollectionDeletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1EventDefinitionCollectionDeletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the deleted - public V1EventDefinitionCollectionDeletedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/FunctionDefinitionCollections/v1/V1FunctionDefinitionCollectionCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/FunctionDefinitionCollections/v1/V1FunctionDefinitionCollectionCreatedDomainEvent.cs deleted file mode 100644 index 5b2a85eca..000000000 --- a/src/core/Synapse.Domain/Events/FunctionDefinitionCollections/v1/V1FunctionDefinitionCollectionCreatedDomainEvent.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.FunctionDefinitionCollections; - -namespace Synapse.Domain.Events.FunctionDefinitionCollections -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1FunctionDefinitionCollectionCreatedIntegrationEvent))] - public class V1FunctionDefinitionCollectionCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1FunctionDefinitionCollectionCreatedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The 's name - /// The 's version - /// The 's description - /// An containing the s the is made out of - public V1FunctionDefinitionCollectionCreatedDomainEvent(string id, string name, string version, string? description, IEnumerable functions) - : base(id) - { - this.Name = name; - this.Version = version; - this.Description = description; - this.Functions = functions.ToList().AsReadOnly(); - } - - /// - /// Gets the 's name - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the 's version - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the 's description - /// - public virtual string? Description { get; protected set; } - - /// - /// Gets an containing the s the is made out of - /// - public virtual IReadOnlyCollection Functions { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/FunctionDefinitionCollections/v1/V1FunctionDefinitionCollectionDeletedDomainEvent.cs b/src/core/Synapse.Domain/Events/FunctionDefinitionCollections/v1/V1FunctionDefinitionCollectionDeletedDomainEvent.cs deleted file mode 100644 index 9f8a6034a..000000000 --- a/src/core/Synapse.Domain/Events/FunctionDefinitionCollections/v1/V1FunctionDefinitionCollectionDeletedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.FunctionDefinitionCollections; - -namespace Synapse.Domain.Events.FunctionDefinitionCollections -{ - ///

- /// Represents the fired whenever a new has been deleted - /// - [DataTransferObjectType(typeof(V1FunctionDefinitionCollectionDeletedIntegrationEvent))] - public class V1FunctionDefinitionCollectionDeletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1FunctionDefinitionCollectionDeletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the deleted - public V1FunctionDefinitionCollectionDeletedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleCreatedDomainEvent.cs deleted file mode 100644 index b899c5cb0..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleCreatedDomainEvent.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1ScheduleCreatedIntegrationEvent))] - public class V1ScheduleCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleCreatedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The activation type of the newly created - /// The definition of the newly created - /// The id of the scheduled by the newly created - /// The date and time at which the will next occur - public V1ScheduleCreatedDomainEvent(string id, V1ScheduleActivationType activationType, ScheduleDefinition definition, string workflowId, DateTimeOffset? nextOccurenceAt) - : base(id) - { - this.ActivationType = activationType; - this.Definition = definition; - this.WorkflowId = workflowId; - this.NextOccurenceAt = nextOccurenceAt; - } - - /// - /// Gets the activation type of the newly created - /// - public virtual V1ScheduleActivationType ActivationType { get; protected set; } - - /// - /// Gets the definition of the newly created - /// - public virtual ScheduleDefinition Definition { get; protected set; } = null!; - - /// - /// Gets the id of the scheduled by the newly created - /// - public virtual string WorkflowId { get; protected set; } = null!; - - /// - /// Gets the 's next occurence - /// - public virtual DateTimeOffset? NextOccurenceAt { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleDefinitionChangedDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleDefinitionChangedDomainEvent.cs deleted file mode 100644 index 90b9b6e88..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleDefinitionChangedDomainEvent.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever the of a has changed - /// - [DataTransferObjectType(typeof(V1ScheduleDefinitionChangedIntegrationEvent))] - public class V1ScheduleDefinitionChangedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleDefinitionChangedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the which's has changed - /// The 's updated - /// The date and time at which the will next occur - public V1ScheduleDefinitionChangedDomainEvent(string id, ScheduleDefinition definition, DateTimeOffset? nextOccurenceAt) - : base(id) - { - this.Definition = definition; - this.NextOccurenceAt = nextOccurenceAt; - } - - /// - /// Gets the 's updated - /// - public virtual ScheduleDefinition Definition { get; protected set; } = null!; - - /// - /// Gets the 's next occurence - /// - public virtual DateTimeOffset? NextOccurenceAt { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleDeletedDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleDeletedDomainEvent.cs deleted file mode 100644 index 2e1968316..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleDeletedDomainEvent.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever a has been deleted - /// - [DataTransferObjectType(typeof(V1ScheduleDeletedIntegrationEvent))] - public class V1ScheduleDeletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleDeletedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the deleted - public V1ScheduleDeletedDomainEvent(string id) : base(id) { } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleObsolitedDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleObsolitedDomainEvent.cs deleted file mode 100644 index ba6b854b7..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleObsolitedDomainEvent.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever a has been made obsolete - /// - [DataTransferObjectType(typeof(V1ScheduleObsolitedIntegrationEvent))] - public class V1ScheduleObsolitedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleObsolitedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the that has been made obsolete - public V1ScheduleObsolitedDomainEvent(string id) : base(id) { } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleOccuredDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleOccuredDomainEvent.cs deleted file mode 100644 index 297c9ea58..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleOccuredDomainEvent.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever a has occured - /// - [DataTransferObjectType(typeof(V1ScheduleOccuredIntegrationEvent))] - public class V1ScheduleOccuredDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleOccuredDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the that has occured - /// The id of the that has been created as the result of the 's occurence - /// The date and time at which the will next occur - public V1ScheduleOccuredDomainEvent(string id, string workflowInstanceId, DateTimeOffset? nextOccurenceAt) - : base(id) - { - this.WorkflowInstanceId = workflowInstanceId; - this.NextOccurenceAt = nextOccurenceAt; - } - - /// - /// Gets the id of the that has been created as the result of the 's occurence - /// - public virtual string WorkflowInstanceId { get; protected set; } = null!; - - /// - /// Gets the 's next occurence - /// - public virtual DateTimeOffset? NextOccurenceAt { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleOccurenceCompletedDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleOccurenceCompletedDomainEvent.cs deleted file mode 100644 index 02217dab5..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleOccurenceCompletedDomainEvent.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever a scheduled has been executed - /// - [DataTransferObjectType(typeof(V1ScheduleOccurenceCompletedIntegrationEvent))] - public class V1ScheduleOccurenceCompletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleOccurenceCompletedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the which's occurence has been completed - /// The id of the 's occurence that has been executed - /// The date and time at which the will next occur - public V1ScheduleOccurenceCompletedDomainEvent(string id, string workflowInstanceId, DateTimeOffset? nextOccurenceAt) - : base(id) - { - this.WorkflowInstanceId = workflowInstanceId; - this.NextOccurenceAt = nextOccurenceAt; - } - - /// - /// Gets the id of the 's occurence that has been executed - /// - public virtual string WorkflowInstanceId { get; protected set; } = null!; - - /// - /// Gets the 's next occurence - /// - public virtual DateTimeOffset? NextOccurenceAt { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleResumedDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleResumedDomainEvent.cs deleted file mode 100644 index d98fa0389..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleResumedDomainEvent.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever a has been resumed - /// - [DataTransferObjectType(typeof(V1ScheduleResumedIntegrationEvent))] - public class V1ScheduleResumedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleResumedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the resumed - /// The 's next occurence - public V1ScheduleResumedDomainEvent(string id, DateTimeOffset? nextOccurenceAt) - : base(id) - { - this.NextOccurenceAt = nextOccurenceAt; - } - - /// - /// Gets the 's next occurence - /// - public virtual DateTimeOffset? NextOccurenceAt { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleRetiredDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleRetiredDomainEvent.cs deleted file mode 100644 index f006981b1..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleRetiredDomainEvent.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever a has been retired - /// - [DataTransferObjectType(typeof(V1ScheduleRetiredIntegrationEvent))] - public class V1ScheduleRetiredDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleRetiredDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the retired - public V1ScheduleRetiredDomainEvent(string id) : base(id) { } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleSuspendedDomainEvent.cs b/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleSuspendedDomainEvent.cs deleted file mode 100644 index da87dccfb..000000000 --- a/src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleSuspendedDomainEvent.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Synapse.Integration.Events.Schedules; - -namespace Synapse.Domain.Events.Schedules -{ - ///

- /// Represents the fired whenever a has been suspended - /// - [DataTransferObjectType(typeof(V1ScheduleSuspendedIntegrationEvent))] - public class V1ScheduleSuspendedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1ScheduleSuspendedDomainEvent() { } - - /// - /// Initializes a new - /// - /// The id of the suspended - public V1ScheduleSuspendedDomainEvent(string id) : base(id) { } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCancelledDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCancelledDomainEvent.cs deleted file mode 100644 index 07863b0b5..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCancelledDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever the execution of a has been cancelled - /// - [DataTransferObjectType(typeof(V1WorkflowActivityCancelledIntegrationEvent))] - public class V1WorkflowActivityCancelledDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityCancelledDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has been cancelled - public V1WorkflowActivityCancelledDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompensatedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompensatedDomainEvent.cs deleted file mode 100644 index e4f469b39..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompensatedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever a has been compensated - /// - [DataTransferObjectType(typeof(V1WorkflowActivityCompensatedIntegrationEvent))] - public class V1WorkflowActivityCompensatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityCompensatedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which has been compensated - public V1WorkflowActivityCompensatedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompensatingDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompensatingDomainEvent.cs deleted file mode 100644 index db12fc4cb..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompensatingDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever a is being compensated - /// - [DataTransferObjectType(typeof(V1WorkflowActivityCompensatingIntegrationEvent))] - public class V1WorkflowActivityCompensatingDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityCompensatingDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which is being compensated - public V1WorkflowActivityCompensatingDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompletedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompletedDomainEvent.cs deleted file mode 100644 index 2bb2e9bb4..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCompletedDomainEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - - ///

- /// Represents the fired whenever the execution of a has been completed - /// - [DataTransferObjectType(typeof(V1WorkflowActivityCompletedIntegrationEvent))] - public class V1WorkflowActivityCompletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityCompletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has started - /// The 's output, if any - public V1WorkflowActivityCompletedDomainEvent(string id, object? output) - : base(id) - { - this.Output = output; - } - - /// - /// Gets the 's output, if any - /// - public virtual object? Output { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCreatedDomainEvent.cs deleted file mode 100644 index 86ac7ff73..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityCreatedDomainEvent.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1WorkflowActivityCreatedIntegrationEvent))] - public class V1WorkflowActivityCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityCreatedDomainEvent() - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The id of the the newly created belongs to - /// The type of the newly created - /// The data of the 's data - /// An that contains the newly created 's metadata - /// The id of the newly created 's parent, if any - public V1WorkflowActivityCreatedDomainEvent(string id, string workflowInstanceId, V1WorkflowActivityType type, object? input, IDictionary? metadata, string? parentId = null) - : base(id) - { - this.WorkflowInstanceId = workflowInstanceId; - this.Type = type; - this.Input = input; - this.Metadata = metadata; - this.ParentId = parentId; - } - - /// - /// Gets the id of the the newly created belongs to - /// - public virtual string WorkflowInstanceId { get; protected set; } - - /// - /// Gets the newly created 's type - /// - public virtual V1WorkflowActivityType Type { get; protected set; } - - /// - /// Gets the newly created 's data - /// - public virtual object? Input { get; protected set; } - - /// - /// Gets the newly created 's metadata - /// - public virtual IDictionary? Metadata { get; protected set; } - - /// - /// Gets the id of the newly created 's parent, if any - /// - public virtual string? ParentId { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityExecutedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityExecutedDomainEvent.cs deleted file mode 100644 index 5aeb7e3f4..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityExecutedDomainEvent.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever a has been executed - /// - [DataTransferObjectType(typeof(V1WorkflowActivityExecutedIntegrationEvent))] - public class V1WorkflowActivityExecutedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityExecutedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has been executed - /// The of the when it finished executing - /// The error that has occured during the 's execution - public V1WorkflowActivityExecutedDomainEvent(string id, V1WorkflowActivityStatus status, Neuroglia.Error? error = null) - : base(id) - { - this.Status = status; - this.Error = error; - } - - /// - /// Initializes a new - /// - /// The id of the that has been executed - /// The of the when it finished executing - /// The 's output - public V1WorkflowActivityExecutedDomainEvent(string id, V1WorkflowActivityStatus status, object? output) - : base(id) - { - this.Status = status; - this.Output = output; - } - - /// - /// Initializes a new - /// - /// The id of the that has been executed - /// The of the when it finished executing - public V1WorkflowActivityExecutedDomainEvent(string id, V1WorkflowActivityStatus status) - : base(id) - { - this.Status = status; - } - - /// - /// Gets the of the when it finished executing - /// - public virtual V1WorkflowActivityStatus Status { get; protected set; } - - /// - /// Gets the 's error, if any - /// - public virtual Neuroglia.Error? Error { get; protected set; } - - /// - /// Gets the 's output, if any - /// - public virtual object? Output { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityFaultedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityFaultedDomainEvent.cs deleted file mode 100644 index ea2f211b5..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityFaultedDomainEvent.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever the execution of a has faulted - /// - [DataTransferObjectType(typeof(V1WorkflowActivityFaultedIntegrationEvent))] - public class V1WorkflowActivityFaultedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityFaultedDomainEvent() - { - this.Error = null!; - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has faulted - /// The due to which the has faulted - public V1WorkflowActivityFaultedDomainEvent(string id, Neuroglia.Error error) - : base(id) - { - this.Error = error; - } - - /// - /// Gets the due to which the has faulted - /// - public virtual Neuroglia.Error Error { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityMetadataChangedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityMetadataChangedDomainEvent.cs deleted file mode 100644 index 88ace820d..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityMetadataChangedDomainEvent.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever the metadata of a has changed - /// - [DataTransferObjectType(typeof(V1WorkflowActivityMetadataChangedIntegrationEvent))] - public class V1WorkflowActivityMetadataChangedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityMetadataChangedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's metadata has changed - /// The 's metadata - public V1WorkflowActivityMetadataChangedDomainEvent(string id, IDictionary? metadata) - : base(id) - { - this.Metadata = metadata; - } - - /// - /// Gets the 's metadata - /// - public virtual IDictionary? Metadata { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityResumedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityResumedDomainEvent.cs deleted file mode 100644 index d51807841..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityResumedDomainEvent.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - - ///

- /// Represents the fired whenever the execution of a has been suspended - /// - [DataTransferObjectType(typeof(V1WorkflowActivityResumedIntegrationEvent))] - public class V1WorkflowActivityResumedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityResumedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has been resumed - public V1WorkflowActivityResumedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivitySkippedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivitySkippedDomainEvent.cs deleted file mode 100644 index 013cfc367..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivitySkippedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever a new has been skipped - /// - [DataTransferObjectType(typeof(V1WorkflowActivitySkippedIntegrationEvent))] - public class V1WorkflowActivitySkippedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivitySkippedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the skipped - public V1WorkflowActivitySkippedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityStartedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityStartedDomainEvent.cs deleted file mode 100644 index f3528e06b..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivityStartedDomainEvent.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - - ///

- /// Represents the fired whenever the execution of a has started - /// - [DataTransferObjectType(typeof(V1WorkflowActivityStartedIntegrationEvent))] - public class V1WorkflowActivityStartedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivityStartedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has started - public V1WorkflowActivityStartedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivitySuspendedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivitySuspendedDomainEvent.cs deleted file mode 100644 index 2b2f6e13d..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowActivities/v1/V1WorkflowActivitySuspendedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowActivities; - -namespace Synapse.Domain.Events.WorkflowActivities -{ - ///

- /// Represents the fired whenever the execution of a has been suspended - /// - [DataTransferObjectType(typeof(V1WorkflowActivitySuspendedIntegrationEvent))] - public class V1WorkflowActivitySuspendedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivitySuspendedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has been suspended - public V1WorkflowActivitySuspendedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowCorrelationContextChangedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowCorrelationContextChangedDomainEvent.cs deleted file mode 100644 index c94137011..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowCorrelationContextChangedDomainEvent.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the of an existing has changed - /// - [DataTransferObjectType(typeof(V1WorkflowCorrelationContextChangedIntegrationEvent))] - public class V1WorkflowCorrelationContextChangedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowCorrelationContextChangedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has faulted - /// The 's new - public V1WorkflowCorrelationContextChangedDomainEvent(string id, Models.V1CorrelationContext correlationContext) - : base(id) - { - this.CorrelationContext = correlationContext; - } - - /// - /// Gets the 's new - /// - public virtual Models.V1CorrelationContext CorrelationContext { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowCorrelationMappingSetDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowCorrelationMappingSetDomainEvent.cs deleted file mode 100644 index c6dc14309..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowCorrelationMappingSetDomainEvent.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - - ///

- /// Represents the fired whenever a 's correlation mapping has been set - /// - [DataTransferObjectType(typeof(V1WorkflowCorrelationMappingSetIntegrationEvent))] - public class V1WorkflowCorrelationMappingSetDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowCorrelationMappingSetDomainEvent() - { - this.Key = null!; - this.Value = null!; - } - - /// - /// Initializes a new - /// - /// The id of the the correlation mapping that has been set belongs to - /// The key of the 's correlation mapping that has been set - /// The value of the 's correlation mapping that has been set - public V1WorkflowCorrelationMappingSetDomainEvent(string id, string key, string value) - : base(id) - { - this.Key = key; - this.Value = value; - } - - /// - /// Gets the key of the 's correlation mapping that has been set - /// - public virtual string Key { get; protected set; } - - /// - /// Gets the value of the 's correlation mapping that has been set - /// - public virtual string Value { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCancelledDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCancelledDomainEvent.cs deleted file mode 100644 index 11a932a00..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCancelledDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a has been cancelled - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceCancelledIntegrationEvent))] - public class V1WorkflowInstanceCancelledDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceCancelledDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has been cancelled - public V1WorkflowInstanceCancelledDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCancellingDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCancellingDomainEvent.cs deleted file mode 100644 index ed24f430b..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCancellingDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a is being cancelled - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceCancellingIntegrationEvent))] - public class V1WorkflowInstanceCancellingDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceCancellingDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution is being cancelled - public V1WorkflowInstanceCancellingDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCompletedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCompletedDomainEvent.cs deleted file mode 100644 index 8de2efd18..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCompletedDomainEvent.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a completes - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceCompletedIntegrationEvent))] - public class V1WorkflowInstanceCompletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceCompletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that ran to completion - /// The 's output - public V1WorkflowInstanceCompletedDomainEvent(string id, object? output) - : base(id) - { - this.Output = output; - } - - /// - /// Gets the 's output - /// - public virtual object? Output { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCreatedDomainEvent.cs deleted file mode 100644 index 292ec6b95..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceCreatedDomainEvent.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceCreatedIntegrationEvent))] - public class V1WorkflowInstanceCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceCreatedDomainEvent() - { - this.Key = null!; - this.WorkflowId = null!; - this.Input = null!; - this.CorrelationContext = null!; - } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The id of the instanciated - /// The key of the newly created - /// The type of the 's activation - /// The newly created 's input data - /// The newly created 's - /// The id of the newly created 's parent, if any - public V1WorkflowInstanceCreatedDomainEvent(string id, string workflowId, string key, V1WorkflowInstanceActivationType activationType, object? input, Models.V1CorrelationContext correlationContext, string? parentId) - : base(id) - { - this.Key = key; - this.WorkflowId = workflowId; - this.ActivationType = activationType; - this.Input = input; - this.CorrelationContext = correlationContext; - this.ParentId = parentId; - } - - /// - /// Gets the id of the instanciated - /// - public virtual string WorkflowId { get; protected set; } - - /// - /// Gets the key of the newly created - /// - public virtual string Key { get; protected set; } - - /// - /// Gets the type of the 's activation - /// - public virtual V1WorkflowInstanceActivationType ActivationType { get; protected set; } - - /// - /// Gets the newly created 's input data - /// - public virtual object? Input { get; protected set; } - - /// - /// Gets the newly created 's - /// - public virtual Models.V1CorrelationContext CorrelationContext { get; protected set; } - - /// - /// Gets the id of the newly created 's parent, if any - /// - public virtual string? ParentId { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceDeletedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceDeletedDomainEvent.cs deleted file mode 100644 index ada211089..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceDeletedDomainEvent.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - - ///

- /// Represents the fired whenever a has been deleted - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceDeletedIntegrationEvent))] - public class V1WorkflowInstanceDeletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceDeletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the newly created - public V1WorkflowInstanceDeletedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceExecutedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceExecutedDomainEvent.cs deleted file mode 100644 index 6f96356a6..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceExecutedDomainEvent.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever a has been executed - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceExecutedIntegrationEvent))] - public class V1WorkflowInstanceExecutedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceExecutedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has been executed - /// The of the when it finished executing - /// The error that has occured during the 's execution - public V1WorkflowInstanceExecutedDomainEvent(string id, V1WorkflowInstanceStatus status, Neuroglia.Error? error = null) - : base(id) - { - this.Status = status; - this.Error = error; - } - - /// - /// Initializes a new - /// - /// The id of the that has been executed - /// The of the when it finished executing - /// The 's output - public V1WorkflowInstanceExecutedDomainEvent(string id, V1WorkflowInstanceStatus status, object? output) - : base(id) - { - this.Status = status; - this.Output = output; - } - - /// - /// Initializes a new - /// - /// The id of the that has been executed - /// The of the when it finished executing - public V1WorkflowInstanceExecutedDomainEvent(string id, V1WorkflowInstanceStatus status) - : base(id) - { - this.Status = status; - } - - /// - /// Gets the of the when it finished executing - /// - public virtual V1WorkflowInstanceStatus Status { get; protected set; } - - /// - /// Gets the 's error, if any - /// - public virtual Neuroglia.Error? Error { get; protected set; } - - /// - /// Gets the 's output, if any - /// - public virtual object? Output { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceFaultedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceFaultedDomainEvent.cs deleted file mode 100644 index 3a78eec56..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceFaultedDomainEvent.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a has faulted - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceFaultedIntegrationEvent))] - public class V1WorkflowInstanceFaultedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceFaultedDomainEvent() - { - this.Error = null!; - } - - /// - /// Initializes a new - /// - /// The id of the that has faulted - /// The that caused the to fault - public V1WorkflowInstanceFaultedDomainEvent(string id, Neuroglia.Error error) - : base(id) - { - this.Error = error; - } - - /// - /// Gets the that caused the to fault - /// - public virtual Neuroglia.Error Error { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceResumedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceResumedDomainEvent.cs deleted file mode 100644 index 3ffa691e2..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceResumedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a has been resumed - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceResumedIntegrationEvent))] - public class V1WorkflowInstanceResumedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceResumedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has been resumed - public V1WorkflowInstanceResumedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceResumingDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceResumingDomainEvent.cs deleted file mode 100644 index 22de9ad85..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceResumingDomainEvent.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a is resuming - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceResumingIntegrationEvent))] - public class V1WorkflowInstanceResumingDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceResumingDomainEvent() - { - this.ProcessId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the which's execution is resuming - /// A string used to uniquely identify the resuming 's process - public V1WorkflowInstanceResumingDomainEvent(string id, string processId) - : base(id) - { - this.ProcessId = processId; - } - - /// - /// Gets a string used to uniquely identify the resuming 's process - /// - public virtual string ProcessId { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceScheduledDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceScheduledDomainEvent.cs deleted file mode 100644 index 8080ba90d..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceScheduledDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a has been scheduled - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceScheduledIntegrationEvent))] - public class V1WorkflowInstanceScheduledDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceScheduledDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has been scheduled - public V1WorkflowInstanceScheduledDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSchedulingDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSchedulingDomainEvent.cs deleted file mode 100644 index fe5eb8ab5..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSchedulingDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a is being scheduled - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceSchedulingIntegrationEvent))] - public class V1WorkflowInstanceSchedulingDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceSchedulingDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution is being scheduled - public V1WorkflowInstanceSchedulingDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceStartedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceStartedDomainEvent.cs deleted file mode 100644 index 32cb8be9f..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceStartedDomainEvent.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - - ///

- /// Represents the fired whenever the execution of a has started - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceStartedIntegrationEvent))] - public class V1WorkflowInstanceStartedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceStartedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has started - public V1WorkflowInstanceStartedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceStartingDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceStartingDomainEvent.cs deleted file mode 100644 index 4b8775ea1..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceStartingDomainEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - - ///

- /// Represents the fired whenever the execution of a is starting - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceStartingIntegrationEvent))] - public class V1WorkflowInstanceStartingDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceStartingDomainEvent() - { - this.ProcessId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the which's execution is starting - /// The string used to uniquely identify the 's process - public V1WorkflowInstanceStartingDomainEvent(string id, string processId) - : base(id) - { - this.ProcessId = processId; - } - - /// - /// Gets the string used to uniquely identify the 's process - /// - public virtual string ProcessId { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSuspendedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSuspendedDomainEvent.cs deleted file mode 100644 index 2e2073ac2..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSuspendedDomainEvent.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - - ///

- /// Represents the fired whenever the execution of a has been suspended - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceSuspendedIntegrationEvent))] - public class V1WorkflowInstanceSuspendedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceSuspendedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution has been suspended - public V1WorkflowInstanceSuspendedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSuspendingDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSuspendingDomainEvent.cs deleted file mode 100644 index 6b09ec899..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowInstances/v1/V1WorkflowInstanceSuspendingDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.WorkflowInstances; - -namespace Synapse.Domain.Events.WorkflowInstances -{ - ///

- /// Represents the fired whenever the execution of a is being suspended - /// - [DataTransferObjectType(typeof(V1WorkflowInstanceSuspendingIntegrationEvent))] - public class V1WorkflowInstanceSuspendingDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanceSuspendingDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the which's execution is being suspended - public V1WorkflowInstanceSuspendingDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessExitedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessExitedDomainEvent.cs deleted file mode 100644 index 9b25d95c1..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessExitedDomainEvent.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Domain.Events.V1WorkflowProcesses -{ - - ///

- /// Represents the fired whenever a has exited - /// - [DataTransferObjectType(typeof(Integration.Events.WorkflowProcesses.V1WorkflowProcessExitedIntegrationEvent))] - public class V1WorkflowProcessExitedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowProcessExitedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has exited - /// The 's exit code - public V1WorkflowProcessExitedDomainEvent(string id, long exitCode) - : base(id) - { - this.ExitCode = exitCode; - } - - /// - /// Gets the id of the 's exit code - /// - public virtual long ExitCode { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessLogOutputDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessLogOutputDomainEvent.cs deleted file mode 100644 index faf893e51..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessLogOutputDomainEvent.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Domain.Events.V1WorkflowProcesses -{ - - ///

- /// Represents the fired whenever a new log has been outputed to a - /// - [DataTransferObjectType(typeof(Integration.Events.WorkflowProcesses.V1WorkflowProcessLogOutputIntegrationEvent))] - public class V1WorkflowProcessLogOutputDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowProcessLogOutputDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the that has outputted the log - /// The log outputed by the - public V1WorkflowProcessLogOutputDomainEvent(string id, string log) - : base(id) - { - this.Log = log; - } - - /// - /// Gets the log outputed by the - /// - public virtual string Log { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessStartedDomainEvent.cs b/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessStartedDomainEvent.cs deleted file mode 100644 index 1a5afe28a..000000000 --- a/src/core/Synapse.Domain/Events/WorkflowProcesses/v1/V1WorkflowProcessStartedDomainEvent.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Domain.Events.V1WorkflowProcesses -{ - - ///

- /// Represents the fired whenever a has started - /// - [DataTransferObjectType(typeof(Integration.Events.WorkflowProcesses.V1WorkflowProcessStartedIntegrationEvent))] - public class V1WorkflowProcessStartedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowProcessStartedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the newly started - public V1WorkflowProcessStartedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowCreatedDomainEvent.cs b/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowCreatedDomainEvent.cs deleted file mode 100644 index aba221ce3..000000000 --- a/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowCreatedDomainEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Workflows; - -namespace Synapse.Domain.Events.Workflows -{ - - ///

- /// Represents the fired whenever a new has been created - /// - [DataTransferObjectType(typeof(V1WorkflowCreatedIntegrationEvent))] - public class V1WorkflowCreatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowCreatedDomainEvent() - { - this.Definition = null!; - } - - /// - /// Initializes a new - /// - /// The id of the newly created - /// The newly created 's - public V1WorkflowCreatedDomainEvent(string id, WorkflowDefinition definition) - : base(id) - { - this.Definition = definition; - } - - /// - /// Gets the newly created 's - /// - public virtual WorkflowDefinition Definition { get; protected set; } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowDeletedDomainEvent.cs b/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowDeletedDomainEvent.cs deleted file mode 100644 index 98ece05c6..000000000 --- a/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowDeletedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Workflows; - -namespace Synapse.Domain.Events.Workflows -{ - ///

- /// Represents the fired whenever a has been deleted - /// - [DataTransferObjectType(typeof(V1WorkflowDeletedIntegrationEvent))] - public class V1WorkflowDeletedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowDeletedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the deleted - public V1WorkflowDeletedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowInstanciatedDomainEvent.cs b/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowInstanciatedDomainEvent.cs deleted file mode 100644 index d0dbfe92c..000000000 --- a/src/core/Synapse.Domain/Events/Workflows/v1/V1WorkflowInstanciatedDomainEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Events.Workflows; - -namespace Synapse.Domain.Events.Workflows -{ - ///

- /// Represents the fired whenever a has been instanciated - /// - [DataTransferObjectType(typeof(V1WorkflowInstanciatedIntegrationEvent))] - public class V1WorkflowInstanciatedDomainEvent - : DomainEvent - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstanciatedDomainEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the instanciated - public V1WorkflowInstanciatedDomainEvent(string id) - : base(id) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/IDeletable.cs b/src/core/Synapse.Domain/IDeletable.cs deleted file mode 100644 index 688d50b71..000000000 --- a/src/core/Synapse.Domain/IDeletable.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Domain -{ - - ///

- /// Defines the fundamentals of an object that can be deleted - /// - public interface IDeletable - { - - /// - /// Deletes the objects - /// - void Delete(); - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1AuthenticationDefinitionCollection.cs b/src/core/Synapse.Domain/Models/v1/V1AuthenticationDefinitionCollection.cs deleted file mode 100644 index 48cb1c3f1..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1AuthenticationDefinitionCollection.cs +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; -using Synapse.Domain.Authentications.AuthenticationDefinitionCollections; - -namespace Synapse.Domain.Models -{ - ///

- /// Represents a managed collection - /// - [DataTransferObjectType(typeof(Integration.Models.V1AuthenticationDefinitionCollection))] - public class V1AuthenticationDefinitionCollection - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1AuthenticationDefinitionCollection() - : base(null!) - { - - } - - /// - /// Initializes a new - /// - /// The 's name - /// The 's version - /// The 's description - /// An array containg the s the is made out of - public V1AuthenticationDefinitionCollection(string name, string version, string? description = null, params AuthenticationDefinition[] authentications) - : base(BuildId(name, version)) - { - if (string.IsNullOrEmpty(name)) throw DomainException.ArgumentNullOrWhitespace(nameof(name)); - if (string.IsNullOrEmpty(version)) throw DomainException.ArgumentNullOrWhitespace(nameof(version)); - if (authentications == null) throw DomainException.ArgumentNull(nameof(authentications)); - this.On(this.RegisterEvent(new V1AuthenticationDefinitionCollectionCreatedDomainEvent(this.Id, name, version, description, authentications))); - } - - /// - /// Gets the 's name - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the 's version - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the 's description - /// - public virtual string? Description { get; protected set; } - - [Newtonsoft.Json.JsonProperty(nameof(Authentications))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Authentications))] - private List _Authentications = new(); - /// - /// Gets an containing the s the is made out of - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyCollection Authentications => this._Authentications.AsReadOnly(); - - /// - /// Deletes the - /// - public virtual void Delete() - { - this.On(this.RegisterEvent(new V1AuthenticationDefinitionCollectionDeletedDomainEvent(this.Id))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1AuthenticationDefinitionCollectionCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.Name = e.Name; - this.Version = e.Version; - this.Description = e.Description; - this._Authentications = e.Authentications.ToList(); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1AuthenticationDefinitionCollectionDeletedDomainEvent e) - { - - } - - /// - /// Builds a new id for the specified name and version - /// - /// The name of the to create a new id for - /// The version of the to create a new id for - /// A new id for the specified name and version - /// - public static string BuildId(string name, string version) - { - if (string.IsNullOrEmpty(name)) throw DomainException.ArgumentNullOrWhitespace(nameof(name)); - if (string.IsNullOrEmpty(version)) throw DomainException.ArgumentNullOrWhitespace(nameof(version)); - if (!SemVersion.TryParse(version, SemVersionStyles.Any, out _)) - throw new DomainArgumentException($"The specified value '{version}' is not a valid semantic version", nameof(version)); - return $"{name.ToLowerInvariant().Slugify("-")}:{version}"; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1Correlation.cs b/src/core/Synapse.Domain/Models/v1/V1Correlation.cs deleted file mode 100644 index c36573922..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1Correlation.cs +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.Correlations; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents an event correlation - /// - [Patchable] - [DataTransferObjectType(typeof(Integration.Models.V1Correlation))] - public class V1Correlation - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1Correlation() - : base(default!) - { - - } - - /// - /// Initializes a new - /// - /// The 's activation type - /// The 's lifetime - /// A value determining the type of the 's evaluation - /// An containing the 's conditions - /// The outcome of the - /// The initial - public V1Correlation(V1CorrelationActivationType activationType, V1CorrelationLifetime lifetime, V1CorrelationConditionType conditionType, IEnumerable conditions, V1CorrelationOutcome outcome, V1CorrelationContext? context = null) - : base(Guid.NewGuid().ToString()) - { - if(conditions == null || !conditions.Any()) throw DomainException.ArgumentNull(nameof(conditions)); - if(outcome == null) throw DomainException.ArgumentNull(nameof(outcome)); - this.On(this.RegisterEvent(new V1CorrelationCreatedDomainEvent(this.Id, activationType, lifetime, conditionType, conditions, outcome, context))); - } - - /// - /// Gets the 's activation type - /// - public virtual V1CorrelationActivationType ActivationType { get; protected set; } - - /// - /// Gets the 's lifetime - /// - public virtual V1CorrelationLifetime Lifetime { get; protected set; } - - /// - /// Gets a value determining the type of the 's evaluation - /// - public virtual V1CorrelationConditionType ConditionType { get; protected set; } - - [Newtonsoft.Json.JsonProperty(nameof(Conditions))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Conditions))] - private List _Conditions = new(); - /// - /// Gets an containing the 's conditions - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public IReadOnlyCollection Conditions => this._Conditions.AsReadOnly(); - - /// - /// Gets the outcome of the - /// - public virtual V1CorrelationOutcome Outcome { get; protected set; } = null!; - - [Newtonsoft.Json.JsonProperty(nameof(Contexts))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Contexts))] - private List _Contexts = new(); - /// - /// Gets an containing the s affected by the - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyCollection Contexts => this._Contexts.AsReadOnly(); - - /// - /// Adds a new to the - /// - /// The to add - public virtual void AddContext(V1CorrelationContext context) - { - if (context == null) - throw DomainException.ArgumentNull(nameof(context)); - this.On(this.RegisterEvent(new V1ContextAddedToCorrelationDomainEvent(this.Id, context))); - } - - /// - /// Determines whether or not the specified matches one of the 's conditions - /// - /// The to check - /// A boolean indicating whether or not the specified matches one of the 's conditions - public virtual bool AppliesTo(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.GetMatchingConditionFor(e) != null; - } - - /// - /// Gets the first matching for the specified - /// - /// The to get the for - /// The first matching for the specified , if any - public virtual V1CorrelationCondition? GetMatchingConditionFor(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.Conditions.FirstOrDefault(c => c.Matches(e)); - } - - /// - /// Correlates the specified - /// - /// The to correlate - /// An containing the keys of the mappings used to correlate the specified - public virtual void Correlate(V1Event e, IEnumerable mappings) - { - if (e == null) - throw DomainException.ArgumentNull(nameof(e)); - if (mappings == null) - throw DomainException.ArgumentNull(nameof(mappings)); - switch (this.Lifetime) - { - case V1CorrelationLifetime.Singleton: - var exclusiveContext = this.Contexts.Single(); - if (exclusiveContext == null || !exclusiveContext.CorrelatesTo(e)) - throw new DomainNullReferenceException($"Failed to find the exclusive correlation's context"); - this.On(this.RegisterEvent(new V1EventCorrelatedDomainEvent(this.Id, exclusiveContext.Id, e, mappings))); - break; - case V1CorrelationLifetime.Transient: - foreach(var context in this.Contexts - .Where(c => c.CorrelatesTo(e))) - { - this.On(this.RegisterEvent(new V1EventCorrelatedDomainEvent(this.Id, context.Id, e, mappings))); - } - break; - default: - throw new NotSupportedException($"The specified {nameof(V1CorrelationLifetime)} '{this.Lifetime}' is not supported"); - } - } - - /// - /// Releases the specified - /// - /// The to release - public virtual void ReleaseContext(V1CorrelationContext context) - { - if (context == null) - throw DomainException.ArgumentNull(nameof(context)); - if (!this.Contexts.Any(c => c.Id == context.Id)) - throw DomainException.NullReference(typeof(V1CorrelationContext), context.Id); - this.On(this.RegisterEvent(new V1CorrelationContextReleasedDomainEvent(this.Id, context.Id))); - } - - /// - /// Releases the specified - /// - /// The the to release belongs to - /// The to release - public virtual void ReleaseEvent(V1CorrelationContext context, V1Event e) - { - if (context == null) throw DomainException.ArgumentNull(nameof(context)); - if (e == null) throw DomainException.ArgumentNull(nameof(e)); - var matchedContext = this.Contexts.FirstOrDefault(c => c.Id == context.Id); - if (matchedContext == null) throw DomainException.NullReference(typeof(V1CorrelationContext), context.Id); - var matchedEvent = matchedContext.PendingEvents?.FirstOrDefault(evt => evt.Id.Equals(e.Id, StringComparison.InvariantCultureIgnoreCase)); - if (matchedEvent == null) throw DomainException.NullReference(typeof(V1Event), e.Id); - this.On(this.RegisterEvent(new V1CorrelatedEventReleasedDomainEvent(this.Id, context.Id, e.Id))); - if (matchedContext.PendingEvents?.Any() == true) return; - this.ReleaseContext(matchedContext); - } - - /// - /// Attempts to complete the in the specified - /// - /// The to attempt completing the in - /// A boolean indicating whether or not the could be completed in the specified - public virtual bool TryComplete(V1CorrelationContext context) - { - if (context == null) - throw DomainException.ArgumentNull(nameof(context)); - var isCompleted = false; - isCompleted = this.ConditionType switch - { - V1CorrelationConditionType.AnyOf => this.Conditions.Any(c => c.MatchesAny(context)), - V1CorrelationConditionType.AllOf => this.Conditions.All(c => c.MatchesAll(context)), - _ => throw new NotSupportedException($"The specified {nameof(V1CorrelationConditionType)} '{this.ConditionType}' is not supported"), - }; - return isCompleted; - } - - /// - /// Deletes the - /// - public virtual void Delete() - { - this.On(this.RegisterEvent(new V1CorrelationDeletedDomainEvent(this.Id))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1CorrelationCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.ActivationType = e.ActivationType; - this.Lifetime = e.Lifetime; - this.ConditionType = e.ConditionType; - this._Conditions = e.Conditions.ToList(); - this.Outcome = e.Outcome; - if (e.Context != null) - this._Contexts.Add(e.Context); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ContextAddedToCorrelationDomainEvent e) - { - this.LastModified = e.CreatedAt; - this._Contexts.Add(e.Context); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1EventCorrelatedDomainEvent e) - { - this.LastModified = e.CreatedAt; - var context = this.Contexts.FirstOrDefault(c => c.Id == e.ContextId); - if (context == null) - throw DomainException.NullReference(typeof(V1CorrelationContext), e.ContextId); - context.Correlate(e.Event, e.Mappings, true); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1CorrelationContextReleasedDomainEvent e) - { - var context = this.Contexts.FirstOrDefault(c => c.Id == e.ContextId); - if (context == null) - throw DomainException.NullReference(typeof(V1CorrelationContext), e.ContextId); - this._Contexts.Remove(context); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1CorrelatedEventReleasedDomainEvent e) - { - var context = this.Contexts.FirstOrDefault(c => c.Id == e.ContextId); - if (context == null) throw DomainException.NullReference(typeof(V1CorrelationContext), e.ContextId); - var evt = context.PendingEvents?.FirstOrDefault(x => x.Id.Equals(e.EventId, StringComparison.InvariantCultureIgnoreCase)); - if (evt == null) throw DomainException.NullReference(typeof(V1Event), e.EventId); - context.RemoveEvent(evt); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1CorrelationDeletedDomainEvent e) - { - - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1CorrelationCondition.cs b/src/core/Synapse.Domain/Models/v1/V1CorrelationCondition.cs deleted file mode 100644 index e5e3c9a8d..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1CorrelationCondition.cs +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using k8s.KubeConfigModels; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents a condition of an event correlation - /// - [DataTransferObjectType(typeof(Integration.Models.V1CorrelationCondition))] - public class V1CorrelationCondition - { - - /// - /// Initializes a new - /// - protected V1CorrelationCondition() - { - - } - - /// - /// Initializes a new - /// - public V1CorrelationCondition(params V1EventFilter[] filters) - { - if (filters == null) - throw DomainException.ArgumentNull(nameof(filters)); - if (!filters.Any()) - throw DomainException.ArgumentMustHaveMinimumLengthOf(nameof(filters), 1); - this._Filters = filters.ToList(); - } - - [Newtonsoft.Json.JsonProperty(nameof(Filters))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Filters))] - private List _Filters = new(); - /// - /// Gets an containing the used to configure the filtering of events that can fire the - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public IReadOnlyCollection Filters => this._Filters.AsReadOnly(); - - /// - /// Determines whether or not the matches the specified - /// - /// The to match - /// A boolean indicating whether or not the matches the specified - public virtual bool Matches(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.GetMatchingFilterFor(e) != null; - } - - /// - /// Determines whether or not the matches the specified - /// - /// The to match - /// A boolean indicating whether or not the matches the specified - public virtual bool MatchesAny(V1CorrelationContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - return context.PendingEvents.Any(e => this.Filters.Any(f => f.Filters(e))); - } - - /// - /// Determines whether or not the matches the specified - /// - /// The to match - /// A boolean indicating whether or not the matches the specified - public virtual bool MatchesAll(V1CorrelationContext context) - { - if (context == null) - throw new ArgumentNullException(nameof(context)); - return this.Filters.All(f => context.PendingEvents.Any(e => f.Filters(e))); - } - - /// - /// Gets the matching for the specified - /// - /// The to get the matching for - /// The matching for the specified , if any - public virtual V1EventFilter? GetMatchingFilterFor(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.Filters.FirstOrDefault(f => f.Filters(e)); - } - - /// - /// Creates a new used to match events defined by the specified s - /// - /// An array containing the s for which to build a new - /// A new - public static V1CorrelationCondition Match(params EventDefinition[] eventDefinitions) - { - if(eventDefinitions == null) - throw new ArgumentNullException(nameof(eventDefinitions)); - var filters = new List(eventDefinitions.Length); - foreach(var eventDefinition in eventDefinitions) - { - filters.Add(V1EventFilter.Match(eventDefinition)); - } - return new(filters.ToArray()); - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1CorrelationContext.cs b/src/core/Synapse.Domain/Models/v1/V1CorrelationContext.cs deleted file mode 100644 index ebfe9d393..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1CorrelationContext.cs +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Collections.ObjectModel; -using System.Text.RegularExpressions; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents the context of an event correlation - /// - [DataTransferObjectType(typeof(Integration.Models.V1CorrelationContext))] - public class V1CorrelationContext - : Entity - { - - /// - /// Initializes a new - /// - public V1CorrelationContext() - : base(Guid.NewGuid().ToString()) - { - - } - - [Newtonsoft.Json.JsonProperty(nameof(Mappings))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Mappings))] - private Dictionary _Mappings = new(); - /// - /// Gets an containing the correlations' value by key mappings - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyDictionary Mappings => new ReadOnlyDictionary(this._Mappings); - - [Newtonsoft.Json.JsonProperty(nameof(PendingEvents))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(PendingEvents))] - private List _PendingEvents = new(); - /// - /// Gets an containing all correlated s pending processing - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyCollection PendingEvents => this._PendingEvents.AsReadOnly(); - - /// - /// Determines whether or not the specified correlates to the - /// - /// The to correlate - /// A boolean indicating whether or not the specified correlates to the - public virtual bool CorrelatesTo(V1Event e) - { - if (e == null) - throw DomainException.ArgumentNull(nameof(e)); - if (this.PendingEvents.Any(ev => ev.Id == e.Id)) - return true; - if (this.PendingEvents.Any(be => be.Type == e.Type && be.Source == e.Source)) - return false; //the specified event type/source has already been correlated - foreach (var mapping in this.Mappings) - { - if (!e.TryGetAttribute(mapping.Key, out var attributeValue) - || !Regex.IsMatch(attributeValue, mapping.Value, RegexOptions.IgnoreCase)) - return false; - } - return true; - } - - /// - /// Correlates the specified - /// - /// The to correlate - /// An containing the context attributes used to correlate the specified - /// A boolean indicating whether or not to enqueue the specified for processing. Defaults to false. - public virtual void Correlate(V1Event e, IEnumerable mappings, bool enqueue = false) - { - if (e == null) - throw DomainException.ArgumentNull(nameof(e)); - if (this._PendingEvents.Any(pe => pe.Id == e.Id)) - return; - if (mappings == null) - mappings = Array.Empty(); - if (enqueue) - this._PendingEvents.Add(e); - foreach (var key in mappings) - { - if (this.Mappings.ContainsKey(key)) - continue; - if (!e.TryGetAttribute(key, out var attributeValue)) - throw new InvalidOperationException($"The event with id '{e.Id}' does not define the required mapping '{key}'"); - this._Mappings.Add(key, attributeValue); - } - } - - /// - /// Removes the specified from the pending queue - /// - /// The to remove from the pending queue - /// A boolean indicating whether or not the could be removed - public virtual bool RemoveEvent(V1Event e) - { - if (e == null) - throw DomainException.ArgumentNull(nameof(e)); - return this._PendingEvents.Remove(e); - } - - /// - /// Sets the specified correlation mapping - /// - /// The key of the correlation mapping to set - /// The value of the correlation mapping to set - internal protected virtual void SetMapping(string key, string value) - { - if (string.IsNullOrWhiteSpace(key)) - throw DomainException.ArgumentNull(nameof(key)); - if (string.IsNullOrWhiteSpace(value)) - throw DomainException.ArgumentNull(nameof(value)); - this._Mappings[key] = value; - } - - /// - /// Creates a new for the specified bootstrap - /// - /// The that has bootstrapped the - /// An containing the context attributes used to correlate s - /// A new - public static V1CorrelationContext CreateFor(V1Event e, IEnumerable mappings) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - if (mappings == null) - throw new ArgumentNullException(nameof(mappings)); - var correlationContext = new V1CorrelationContext(); - correlationContext._PendingEvents.Add(e); - foreach (string key in mappings) - { - if (!e.TryGetAttribute(key, out var attributeValue)) - throw new InvalidOperationException($"The cloud event with id '{e.Id}' does not define the required context attribute '{key}'"); - correlationContext._Mappings.Add(key, attributeValue); - } - return correlationContext; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1CorrelationOutcome.cs b/src/core/Synapse.Domain/Models/v1/V1CorrelationOutcome.cs deleted file mode 100644 index bf92f265f..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1CorrelationOutcome.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents the outcome of an event correlation - /// - [DataTransferObjectType(typeof(Integration.Models.V1CorrelationOutcome))] - public class V1CorrelationOutcome - { - - /// - /// Initializes a new - /// - protected V1CorrelationOutcome() - { - - } - - /// - /// Initializes a new - /// - /// The 's type - /// The identifier of the 's target (a or a ) - public V1CorrelationOutcome(V1CorrelationOutcomeType type, string target) - { - if (string.IsNullOrWhiteSpace(target)) - throw DomainException.ArgumentNullOrWhitespace(nameof(target)); - this.Type = type; - this.Target = target; - } - - /// - /// Gets the 's type - /// - public virtual V1CorrelationOutcomeType Type { get; protected set; } - - /// - /// Gets the identifier of the 's target (a or a ) - /// - public virtual string Target { get; protected set; } = null!; - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1Event.cs b/src/core/Synapse.Domain/Models/v1/V1Event.cs deleted file mode 100644 index 866188819..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1Event.cs +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Newtonsoft.Json.Linq; -using System.Dynamic; -using System.Text.RegularExpressions; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents an event - /// - [DataTransferObjectType(typeof(Integration.Models.V1Event))] - public class V1Event - { - - /// - /// Initializes a new - /// - protected V1Event() - { - this.Id = null!; - this.Source = null!; - this.Type = null!; - } - - /// - /// Initializes a new - /// - /// The event's id - /// The event's source - /// event's type - /// The event's spec version - public V1Event(string id, Uri source, string type, string? specVersion) - { - if (string.IsNullOrEmpty(id)) - throw new ArgumentNullException(nameof(id)); - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (string.IsNullOrEmpty(type)) - throw new ArgumentNullException(nameof(type)); - if (specVersion == null) - specVersion = CloudEventsSpecVersion.V1_0.VersionId; - this.Id = id; - this.Source = source; - this.SpecVersion = specVersion; - this.Type = type; - } - - /// - /// Gets the event's id - /// - public virtual string Id { get; protected set; } - - /// - /// Gets the event's source - /// - public virtual Uri Source { get; protected set; } - - /// - /// Gets the event's spec version - /// - public virtual string SpecVersion { get; protected set; } = CloudEventsSpecVersion.V1_0.VersionId; - - /// - /// Gets the event's type - /// - public virtual string Type { get; protected set; } - - /// - /// Gets the event's data content type - /// - public virtual string? DataContentType { get; protected set; } - - /// - /// Gets the event's data schema , if any - /// - public virtual Uri? DataSchema { get; protected set; } - - /// - /// Gets the event's subject - /// - public virtual string? Subject { get; protected set; } - - /// - /// Gets the event's type - /// - public virtual DateTimeOffset? Time { get; protected set; } - - /// - /// Gets the event's data - /// - public virtual Neuroglia.Serialization.Dynamic? Data { get; protected set; } - - /// - /// Gets an that contains the event's extension attributes key/value mappings - /// - [Newtonsoft.Json.JsonExtensionData] - [System.Text.Json.Serialization.JsonExtensionData] - public virtual IDictionary ExtensionAttributes { get; protected set; } = new Dictionary(); - - /// - /// Gets an containing all the attributes - /// - [ProjectNever] - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyDictionary Attributes - { - get - { - return this.AttributesEnumerator.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString()); - } - } - - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - IEnumerable> AttributesEnumerator - { - get - { - yield return new KeyValuePair(nameof(Id).ToLower(), this.Id); - yield return new KeyValuePair(nameof(Source).ToLower(), this.Source.ToString()); - yield return new KeyValuePair(nameof(SpecVersion).ToLower(), this.SpecVersion); - yield return new KeyValuePair(nameof(Type).ToLower(), this.Type); - if(!string.IsNullOrEmpty(this.DataContentType)) - yield return new KeyValuePair(nameof(DataContentType).ToLower(), this.DataContentType); - if (this.DataSchema != null) - yield return new KeyValuePair(nameof(DataSchema).ToLower(), this.DataSchema.ToString()); - if (!string.IsNullOrEmpty(this.Subject)) - yield return new KeyValuePair(nameof(Subject).ToLower(), this.Subject); - if (this.Time.HasValue) - yield return new KeyValuePair(nameof(Time).ToLower(), this.Time.ToString()!); - if(this.ExtensionAttributes != null) - { - foreach (var extension in this.ExtensionAttributes) - yield return new(extension.Key, extension.Value.ToString()!); - } - } - } - - /// - /// Attempts to get the attribute with the specified name - /// - /// The name of the attribute to get - /// The value of the attribute, if any - /// A boolean indicating whether or not the attribute with the specified name is defined - public virtual bool TryGetAttribute(string name, out string value) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentNullException(nameof(name)); - return this.Attributes.TryGetValue(name, out value!); - } - - /// - /// Determines whether or not the matches the specified - /// - /// The to match - /// A boolean indicating whether or not the matches the specified - public virtual bool Matches(EventDefinition eventDefinition) - { - if (eventDefinition == null) - throw new ArgumentNullException(nameof(eventDefinition)); - if (!string.IsNullOrWhiteSpace(eventDefinition.Source) - && !Regex.IsMatch(this.Source.ToString(), eventDefinition.Source, RegexOptions.IgnoreCase)) - return false; - if (!string.IsNullOrWhiteSpace(eventDefinition.Type) - && !Regex.IsMatch(this.Type, eventDefinition.Type, RegexOptions.IgnoreCase)) - return false; - if (eventDefinition.Correlations != null) - { - foreach (EventCorrelationDefinition correlationDefinition in eventDefinition.Correlations) - { - if (!this.TryGetAttribute(correlationDefinition.ContextAttributeName, out string value)) return false; - if (string.IsNullOrWhiteSpace(correlationDefinition.ContextAttributeValue)) continue; - if (!Regex.IsMatch(value, correlationDefinition.ContextAttributeValue, RegexOptions.IgnoreCase)) return false; - } - } - return true; - } - - /// - /// Creates a new for the specified - /// - /// The to create a new for - /// A new - public static V1Event CreateFrom(CloudEvent cloudEvent) - { - if (cloudEvent == null) - throw new ArgumentNullException(nameof(cloudEvent)); - var e = new V1Event(cloudEvent.Id!, cloudEvent.Source!, cloudEvent.Type!, cloudEvent.SpecVersion.VersionId) - { - DataContentType = cloudEvent.DataContentType, - DataSchema = cloudEvent.DataSchema, - Subject = cloudEvent.Subject, - Time = cloudEvent.Time - }; - var data = cloudEvent.Data; - if (data is JObject jobject) - data = jobject.ToObject(); - e.Data = Neuroglia.Serialization.Dynamic.FromObject(data); - foreach(var extensionsAttribute in cloudEvent.ExtensionAttributes) - { - e.ExtensionAttributes.Add(extensionsAttribute.Name, extensionsAttribute.Format(cloudEvent[extensionsAttribute]!)); - } - return e; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1EventDefinitionCollection.cs b/src/core/Synapse.Domain/Models/v1/V1EventDefinitionCollection.cs deleted file mode 100644 index f251fdf55..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1EventDefinitionCollection.cs +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Semver; -using Synapse.Domain.Events.EventDefinitionCollections; - -namespace Synapse.Domain.Models -{ - ///

- /// Represents a managed collection - /// - [DataTransferObjectType(typeof(Integration.Models.V1EventDefinitionCollection))] - public class V1EventDefinitionCollection - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1EventDefinitionCollection() - : base(null!) - { - - } - - /// - /// Initializes a new - /// - /// The 's name - /// The 's version - /// The 's description - /// An array containg the s the is made out of - public V1EventDefinitionCollection(string name, string version, string? description = null, params EventDefinition[] events) - : base(BuildId(name, version)) - { - if (string.IsNullOrEmpty(name)) throw DomainException.ArgumentNullOrWhitespace(nameof(name)); - if (string.IsNullOrEmpty(version)) throw DomainException.ArgumentNullOrWhitespace(nameof(version)); - if (events == null) throw DomainException.ArgumentNull(nameof(events)); - this.On(this.RegisterEvent(new V1EventDefinitionCollectionCreatedDomainEvent(this.Id, name, version, description, events))); - } - - /// - /// Gets the 's name - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the 's version - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the 's description - /// - public virtual string? Description { get; protected set; } - - [Newtonsoft.Json.JsonProperty(nameof(Events))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Events))] - private List _Events = new(); - /// - /// Gets an containing the s the is made out of - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyCollection Events => this._Events.AsReadOnly(); - - /// - /// Deletes the - /// - public virtual void Delete() - { - this.On(this.RegisterEvent(new V1EventDefinitionCollectionDeletedDomainEvent(this.Id))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1EventDefinitionCollectionCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.Name = e.Name; - this.Version = e.Version; - this.Description = e.Description; - this._Events = e.Events.ToList(); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1EventDefinitionCollectionDeletedDomainEvent e) - { - - } - - /// - /// Builds a new id for the specified name and version - /// - /// The name of the to create a new id for - /// The version of the to create a new id for - /// A new id for the specified name and version - /// - public static string BuildId(string name, string version) - { - if (string.IsNullOrEmpty(name)) throw DomainException.ArgumentNullOrWhitespace(nameof(name)); - if (string.IsNullOrEmpty(version)) throw DomainException.ArgumentNullOrWhitespace(nameof(version)); - if (!SemVersion.TryParse(version, SemVersionStyles.Any, out _)) - throw new DomainArgumentException($"The specified value '{version}' is not a valid semantic version", nameof(version)); - return $"{name.ToLowerInvariant().Slugify("-")}:{version}"; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1EventFilter.cs b/src/core/Synapse.Domain/Models/v1/V1EventFilter.cs deleted file mode 100644 index 2d1fa18c3..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1EventFilter.cs +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Text.RegularExpressions; - -namespace Synapse.Domain.Models; - -///

-/// Represents an object used to filter events -/// -[DataTransferObjectType(typeof(Integration.Models.V1EventFilter))] -public class V1EventFilter -{ - - /// - /// Initializes a new - /// - protected V1EventFilter() - { - - } - - /// - /// Initializes a new - /// - /// A containing the attributes to filter s by - /// A containing the attributes key/value to use when correlating an incoming event to the - public V1EventFilter(Dictionary attributes, Dictionary correlationMappings) - : this() - { - this._Attributes = attributes ?? throw new ArgumentNullException(nameof(attributes)); - this._CorrelationMappings = correlationMappings ?? throw new ArgumentNullException(nameof(correlationMappings)); - } - - [Newtonsoft.Json.JsonProperty(nameof(Attributes))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Attributes))] - private Dictionary _Attributes = new(); - /// - /// Gets an containing the attributes to filter s by - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public IReadOnlyDictionary Attributes => this._Attributes; - - [Newtonsoft.Json.JsonProperty(nameof(CorrelationMappings))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(CorrelationMappings))] - private Dictionary _CorrelationMappings = new(); - /// - /// Gets an containing the attributes key/value to use when correlating an incoming event to the - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public IReadOnlyDictionary CorrelationMappings => this._CorrelationMappings; - - /// - /// Determines whether or not the filters the specified - /// - /// The to filter - /// A boolean indicating whether or not the filters the specified - public virtual bool Filters(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - foreach (var attribute in this.Attributes) - { - if (!e.TryGetAttribute(attribute.Key, out var value)) - return false; - if (!Regex.IsMatch(value, attribute.Value, RegexOptions.IgnoreCase)) - return false; - } - foreach (var mapping in this.CorrelationMappings) - { - if (!e.TryGetAttribute(mapping.Key, out var value)) - return false; - if (!string.IsNullOrWhiteSpace(mapping.Value) - && !Regex.IsMatch(value, mapping.Value, RegexOptions.IgnoreCase)) - return false; - } - return true; - } - - /// - /// Creates a new used to match events defined by the specified - /// - /// The for which to build a new - /// A new - public static V1EventFilter Match(EventDefinition eventDefinition) - { - if (eventDefinition == null) - throw new ArgumentNullException(nameof(eventDefinition)); - var attributes = new Dictionary(); - if (!string.IsNullOrWhiteSpace(eventDefinition.Source)) - attributes.Add(nameof(CloudEvent.Source).ToLower(), eventDefinition.Source); - if (!string.IsNullOrWhiteSpace(eventDefinition.Type)) - attributes.Add(nameof(CloudEvent.Type).ToLower(), eventDefinition.Type); - var correlationMappings = new Dictionary(); - if (eventDefinition.Correlations != null) - { - foreach (var mapping in eventDefinition.Correlations) - { - var value = null as string; - if (!string.IsNullOrWhiteSpace(mapping.ContextAttributeValue) - && !mapping.ContextAttributeValue.IsRuntimeExpression()) - value = mapping.ContextAttributeValue; - correlationMappings.Add(mapping.ContextAttributeName, value!); - } - } - return new(attributes, correlationMappings); - } - -} \ No newline at end of file diff --git a/src/core/Synapse.Domain/Models/v1/V1FunctionDefinitionCollection.cs b/src/core/Synapse.Domain/Models/v1/V1FunctionDefinitionCollection.cs deleted file mode 100644 index e80f18c64..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1FunctionDefinitionCollection.cs +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Org.BouncyCastle.Asn1.Cms; -using Semver; -using Synapse.Domain.Events.FunctionDefinitionCollections; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents a managed collection - /// - [DataTransferObjectType(typeof(Integration.Models.V1FunctionDefinitionCollection))] - public class V1FunctionDefinitionCollection - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1FunctionDefinitionCollection() - : base(null!) - { - - } - - /// - /// Initializes a new - /// - /// The 's name - /// The 's version - /// The 's description - /// An array containg the s the is made out of - public V1FunctionDefinitionCollection(string name, string version, string? description = null, params FunctionDefinition[] functions) - : base(BuildId(name, version)) - { - if(string.IsNullOrEmpty(name)) throw DomainException.ArgumentNullOrWhitespace(nameof(name)); - if (string.IsNullOrEmpty(version))throw DomainException.ArgumentNullOrWhitespace(nameof(version)); - if(functions == null) throw DomainException.ArgumentNull(nameof(functions)); - this.On(this.RegisterEvent(new V1FunctionDefinitionCollectionCreatedDomainEvent(this.Id, name, version, description, functions))); - } - - /// - /// Gets the 's name - /// - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the 's version - /// - public virtual string Version { get; protected set; } = null!; - - /// - /// Gets the 's description - /// - public virtual string? Description { get; protected set; } - - [Newtonsoft.Json.JsonProperty(nameof(Functions))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Functions))] - private List _Functions = new(); - /// - /// Gets an containing the s the is made out of - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyCollection Functions => this._Functions.AsReadOnly(); - - /// - /// Deletes the - /// - public virtual void Delete() - { - this.On(this.RegisterEvent(new V1FunctionDefinitionCollectionDeletedDomainEvent(this.Id))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1FunctionDefinitionCollectionCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.Name = e.Name; - this.Version = e.Version; - this.Description = e.Description; - this._Functions = e.Functions.ToList(); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1FunctionDefinitionCollectionDeletedDomainEvent e) - { - - } - - /// - /// Builds a new id for the specified name and version - /// - /// The name of the to create a new id for - /// The version of the to create a new id for - /// A new id for the specified name and version - /// - public static string BuildId(string name, string version) - { - if (string.IsNullOrEmpty(name)) throw DomainException.ArgumentNullOrWhitespace(nameof(name)); - if (string.IsNullOrEmpty(version)) throw DomainException.ArgumentNullOrWhitespace(nameof(version)); - if (!SemVersion.TryParse(version, SemVersionStyles.Any, out _)) - throw new DomainArgumentException($"The specified value '{version}' is not a valid semantic version", nameof(version)); - return $"{name.ToLowerInvariant().Slugify("-")}:{version}"; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1Schedule.cs b/src/core/Synapse.Domain/Models/v1/V1Schedule.cs deleted file mode 100644 index 5083c1ea0..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1Schedule.cs +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.JsonPatch; -using Microsoft.AspNetCore.JsonPatch.Operations; -using ServerlessWorkflow.Sdk; -using Synapse.Domain.Events.Schedules; -using System.Text.RegularExpressions; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents a schedule - /// - [Patchable] - [DataTransferObjectType(typeof(Integration.Models.V1Schedule))] - public class V1Schedule - : AggregateRoot, IDeletable - { - - /// - /// Initializes a new - /// - protected V1Schedule() - : base(string.Empty) - { - - } - - /// - /// Initializes a new - /// - /// The 's activation type - /// The 's - /// The to schedule - public V1Schedule(V1ScheduleActivationType activationType, ScheduleDefinition definition, V1Workflow workflow) - : base(BuildId(workflow?.Id!)) - { - if (definition == null) throw DomainException.ArgumentNull(nameof(definition)); - if (workflow == null) throw DomainException.ArgumentNull(nameof(workflow)); - this.On(this.RegisterEvent(new V1ScheduleCreatedDomainEvent(this.Id, activationType, definition, workflow.Id, definition.GetNextOccurence()))); - } - - /// - /// Gets the 's activation type - /// - public virtual V1ScheduleActivationType ActivationType { get; protected set; } - - /// - /// Gets the 's status - /// - public virtual V1ScheduleStatus Status { get; protected set; } - - /// - /// Gets the 's definition - /// - public virtual ScheduleDefinition Definition { get; protected set; } = null!; - - /// - /// Gets the id of the scheduled - /// - public virtual string WorkflowId { get; protected set; } = null!; - - /// - /// Gets the date and time the has last been suspended at - /// - public virtual DateTimeOffset? SuspendedAt { get; protected set; } - - /// - /// Gets the date and time the has been retired at - /// - public virtual DateTimeOffset? RetiredAt { get; protected set; } - - /// - /// Gets the date and time the has been made obsolete at - /// - public virtual DateTimeOffset? ObsoletedAt { get; protected set; } - - /// - /// Gets the date and time at which the has last occured - /// - public virtual DateTimeOffset? LastOccuredAt { get; protected set; } - - /// - /// Gets the date and time at which the scheduled has last completed - /// - public virtual DateTimeOffset? LastCompletedAt { get; protected set; } - - /// - /// Gets the date and time at which the will next occur - /// - public virtual DateTimeOffset? NextOccurenceAt { get; protected set; } - - /// - /// Sets the 's definition - /// - /// The 's definition - [JsonPatchOperation(OperationType.Replace, nameof(Definition))] - public virtual void SetDefinition(ScheduleDefinition definition) - { - if (definition == null) throw DomainException.ArgumentNull(nameof(definition)); - this.On(this.RegisterEvent(new V1ScheduleDefinitionChangedDomainEvent(this.Id, definition, definition.GetNextOccurence(this.LastOccuredAt)))); - } - - /// - /// Occurs the - /// - /// The id of the the has created as a result of its occurence - public virtual void Occur(string workflowInstanceId) - { - if (string.IsNullOrWhiteSpace(workflowInstanceId)) throw DomainException.ArgumentNullOrWhitespace(nameof(workflowInstanceId)); - if (this.Status != V1ScheduleStatus.Active) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status); - this.On(this.RegisterEvent(new V1ScheduleOccuredDomainEvent(this.Id, workflowInstanceId, this.Definition.Type == ScheduleDefinitionType.Interval ? null : this.Definition.GetNextOccurence()))); - } - - /// - /// Completes a triggered occurence - /// - /// The id of the 's occurence that has been executed - public virtual void CompleteOccurence(string workflowInstanceId) - { - if(this.Status != V1ScheduleStatus.Active) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status); - this.On(this.RegisterEvent(new V1ScheduleOccurenceCompletedDomainEvent(this.Id, workflowInstanceId, this.Definition.Type == ScheduleDefinitionType.Interval ? this.Definition.GetNextOccurence() : null))); - } - - /// - /// Suspends the - /// - public virtual void Suspend() - { - if (this.Status != V1ScheduleStatus.Active) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status); - this.On(this.RegisterEvent(new V1ScheduleSuspendedDomainEvent(this.Id))); - } - - /// - /// Resumes the - /// - public virtual void Resume() - { - if (this.Status != V1ScheduleStatus.Suspended) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status); - this.On(this.RegisterEvent(new V1ScheduleResumedDomainEvent(this.Id, this.Definition.GetNextOccurence()))); - } - - /// - /// Retires the - /// - public virtual void Retire() - { - if (this.Status >= V1ScheduleStatus.Retired) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status); - this.On(this.RegisterEvent(new V1ScheduleRetiredDomainEvent(this.Id))); - } - - /// - /// Makes the obsolete - /// - public virtual void MakeObsolete() - { - if (this.Status >= V1ScheduleStatus.Retired) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status); - this.On(this.RegisterEvent(new V1ScheduleObsolitedDomainEvent(this.Id))); - } - - /// - /// Deletes the - /// - public virtual void Delete() - { - this.On(this.RegisterEvent(new V1ScheduleDeletedDomainEvent(this.Id))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleDefinitionChangedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Definition = e.Definition; - this.NextOccurenceAt = e.NextOccurenceAt; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.ActivationType = e.ActivationType; - this.Status = V1ScheduleStatus.Active; - this.Definition = e.Definition; - this.WorkflowId = e.WorkflowId; - this.NextOccurenceAt = e.NextOccurenceAt; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleOccuredDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.LastOccuredAt = e.CreatedAt; - this.NextOccurenceAt = e.NextOccurenceAt; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleOccurenceCompletedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.LastCompletedAt = e.CreatedAt; - this.NextOccurenceAt = e.NextOccurenceAt; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleSuspendedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.SuspendedAt = e.CreatedAt; - this.Status = V1ScheduleStatus.Suspended; - this.NextOccurenceAt = null; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleResumedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.SuspendedAt = null; - this.NextOccurenceAt = e.NextOccurenceAt; - this.Status = V1ScheduleStatus.Active; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleRetiredDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.RetiredAt = e.CreatedAt; - this.Status = V1ScheduleStatus.Retired; - this.NextOccurenceAt = null; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleObsolitedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.ObsoletedAt = e.CreatedAt; - this.Status = V1ScheduleStatus.Obsolete; - this.NextOccurenceAt = null; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1ScheduleDeletedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.NextOccurenceAt = null; - } - - /// - /// Builds a new id - /// - /// The id of the to build a new id for - /// A new id - /// Note that because it uses a generated , this method should never return twice the same output - public static string BuildId(string workflowId) - { - if (string.IsNullOrWhiteSpace(workflowId)) throw DomainException.ArgumentNullOrWhitespace(nameof(workflowId)); - return $"{workflowId}-{Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", string.Empty).ToLowerInvariant()}"; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1Workflow.cs b/src/core/Synapse.Domain/Models/v1/V1Workflow.cs deleted file mode 100644 index 36dfd69b6..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1Workflow.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.Workflows; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents a workflow - /// - [DataTransferObjectType(typeof(Integration.Models.V1Workflow))] - public class V1Workflow - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1Workflow() - : base(null!) - { - this.Definition = null!; - } - - /// - /// Initializes a new - /// - /// The 's definition - public V1Workflow(WorkflowDefinition definition) - : this() - { - if(definition == null) - throw DomainException.ArgumentNull(nameof(definition)); - this.On(this.RegisterEvent(new V1WorkflowCreatedDomainEvent(definition.GetUniqueIdentifier(), definition))); - } - - /// - /// Gets the 's definition - /// - public virtual WorkflowDefinition Definition { get; protected set; } - - /// - /// Gets the date and time at which the was last instanciated - /// - public virtual DateTimeOffset? LastInstanciated { get; protected set; } - - /// - /// Instanciates the - /// - public virtual void Instanciate() - { - this.On(this.RegisterEvent(new V1WorkflowInstanciatedDomainEvent(this.Id))); - } - - /// - /// Deletes the - /// - public virtual void Delete() - { - this.On(this.RegisterEvent(new V1WorkflowDeletedDomainEvent(this.Id))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.Definition = e.Definition; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanciatedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.LastInstanciated = e.CreatedAt; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowDeletedDomainEvent e) - { - - } - - /// - public override string ToString() - { - return this.Id; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1WorkflowActivity.cs b/src/core/Synapse.Domain/Models/v1/V1WorkflowActivity.cs deleted file mode 100644 index ceed109d0..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1WorkflowActivity.cs +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.WorkflowActivities; - -namespace Synapse.Domain.Models -{ - - ///

- /// Describes a workflow activity - /// - [DataTransferObjectType(typeof(Integration.Models.V1WorkflowActivity))] - public class V1WorkflowActivity - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1WorkflowActivity() - : base(null!) - { - this.WorkflowInstanceId = null!; - } - - /// - /// Initializes a new - /// - /// The the belongs to - /// The 's type - /// The 's data - /// The 's metadata - /// The 's parent, if any - public V1WorkflowActivity(V1WorkflowInstance workflowInstance, V1WorkflowActivityType type, object? input = null, IDictionary? metadata = null, V1WorkflowActivity? parent = null) - : this() - { - if (workflowInstance == null) - throw DomainException.ArgumentNull(nameof(workflowInstance)); - this.On(this.RegisterEvent(new V1WorkflowActivityCreatedDomainEvent(Guid.NewGuid().ToString(), workflowInstance.Id, type, input, metadata, parent?.Id))); - } - - /// - /// Gets the id of the the belongs to - /// - public virtual string WorkflowInstanceId { get; protected set; } - - /// - /// Gets the 's type - /// - public virtual V1WorkflowActivityType Type { get; protected set; } - - /// - /// Gets the 's status - /// - public virtual V1WorkflowActivityStatus Status { get; protected set; } - - /// - /// Gets the date and time at which the has started - /// - public virtual DateTimeOffset? StartedAt { get; protected set; } - - /// - /// Gets the date and time at which the has been executed - /// Value is set when the has been cancelled, faults or completes - /// - public virtual DateTimeOffset? ExecutedAt { get; protected set; } - - /// - /// Gets the that caused the to end prematurily - /// - public virtual Neuroglia.Error? Error { get; protected set; } - - /// - /// Gets the 's input - /// - public virtual object? Input { get; protected set; } - - /// - /// Gets the 's output - /// - public virtual object? Output { get; protected set; } - - /// - /// Gets the 's metadata - /// - public virtual IDictionary? Metadata { get; protected set; } - - /// - /// Gets the id of the 's parent, if any - /// - public virtual string? ParentId { get; protected set; } - - /// - /// Starts or resumes the - /// - public virtual void StartOrResume() - { - if (this.Status != V1WorkflowActivityStatus.Pending) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivityStartedDomainEvent(this.Id))); - } - - /// - /// Supsend the - /// - public virtual void Suspend() - { - if (this.Status != V1WorkflowActivityStatus.Running) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivitySuspendedDomainEvent(this.Id))); - } - - /// - /// Faults the - /// - /// The unhandled that caused the to end prematurily - public virtual void Fault(Exception ex) - { - if (ex == null) - throw DomainException.ArgumentNull(nameof(ex)); - if (this.Status >= V1WorkflowActivityStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.Fault(new Neuroglia.Error(ex.GetType().Name.Replace("exception", string.Empty, StringComparison.OrdinalIgnoreCase), ex.Message)); - } - - /// - /// Faults the - /// - /// The unhandled that caused the to end prematurily - public virtual void Fault(Neuroglia.Error error) - { - if (error == null) - throw DomainException.ArgumentNull(nameof(error)); - if (this.Status >= V1WorkflowActivityStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivityFaultedDomainEvent(this.Id, error))); - } - - /// - /// Compensates the - /// - public virtual void Compensate() - { - if (this.Status > V1WorkflowActivityStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivityCompensatingDomainEvent(this.Id))); - } - - /// - /// Marks the as compensated - /// - public virtual void MarkAsCompensated() - { - if (this.Status != V1WorkflowActivityStatus.Compensating) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivityCompensatedDomainEvent(this.Id))); - } - - /// - /// Cancels the - /// - public virtual void Cancel() - { - if (this.Status >= V1WorkflowActivityStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivityCancelledDomainEvent(this.Id))); - } - - /// - /// Skips the - /// - public virtual void Skip() - { - if (this.Status >= V1WorkflowActivityStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivitySkippedDomainEvent(this.Id))); - } - - /// - /// Sets the 's metadata - /// - /// An containing the metadata to set - public virtual void SetMetadata(IDictionary? metadata) - { - this.On(this.RegisterEvent(new V1WorkflowActivityMetadataChangedDomainEvent(this.Id, metadata))); - } - - /// - /// Completes and sets the 's output - /// - public virtual void SetOutput(object? output) - { - if (this.Status >= V1WorkflowActivityStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowActivity), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowActivityCompletedDomainEvent(this.Id, output))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.WorkflowInstanceId = e.WorkflowInstanceId; - this.Type = e.Type; - this.Input = e.Input; - this.Metadata = e.Metadata; - this.ParentId = e.ParentId; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityStartedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.StartedAt = e.CreatedAt; - this.Status = V1WorkflowActivityStatus.Running; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivitySuspendedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowActivityStatus.Suspended; //todo: keep track of runtime sessions - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityResumedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowActivityStatus.Running; //todo: keep track of runtime sessions - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityFaultedDomainEvent e) - { - this.Status = V1WorkflowActivityStatus.Faulted; - this.Error = e.Error; - this.On(this.RegisterEvent(new V1WorkflowActivityExecutedDomainEvent(this.Id, this.Status, this.Error))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityCompensatingDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowActivityStatus.Compensating; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityCompensatedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowActivityStatus.Compensated; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityCancelledDomainEvent e) - { - this.Status = V1WorkflowActivityStatus.Cancelled; - this.On(this.RegisterEvent(new V1WorkflowActivityExecutedDomainEvent(this.Id, this.Status))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivitySkippedDomainEvent e) - { - this.Status = V1WorkflowActivityStatus.Skipped; - this.On(this.RegisterEvent(new V1WorkflowActivityExecutedDomainEvent(this.Id, this.Status))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityMetadataChangedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Metadata = e.Metadata; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityCompletedDomainEvent e) - { - this.Status = V1WorkflowActivityStatus.Completed; - this.Output = e.Output; - this.On(this.RegisterEvent(new V1WorkflowActivityExecutedDomainEvent(this.Id, this.Status, this.Output))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowActivityExecutedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.ExecutedAt = e.CreatedAt; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1WorkflowInstance.cs b/src/core/Synapse.Domain/Models/v1/V1WorkflowInstance.cs deleted file mode 100644 index e61cd9550..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1WorkflowInstance.cs +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.WorkflowInstances; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represent an instance of a - /// - [DataTransferObjectType(typeof(Integration.Models.V1WorkflowInstance))] - public class V1WorkflowInstance - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1WorkflowInstance() - : base(null!) - { - this.WorkflowId = null!; - this.Key = null!; - this.CorrelationContext = null!; - } - - /// - /// Initializes a new - /// - /// The 's key - /// The 's - /// The 's activation type - /// The 's input data - /// An containing the s that have triggered the creation of the - /// The 's parent, if any - public V1WorkflowInstance(string key, V1Workflow workflow, V1WorkflowInstanceActivationType activationType, object? input = null, V1CorrelationContext? correlationContext = null, V1WorkflowInstance? parent = null) - : this() - { - if(workflow == null) - throw DomainException.ArgumentNull(nameof(workflow)); - if (input == null) - input = new(); - if (correlationContext == null) - correlationContext = new(); - this.On(this.RegisterEvent(new V1WorkflowInstanceCreatedDomainEvent(BuildUniqueIdentifier(key, workflow), workflow.Id, key.ToLowerInvariant(), activationType, input, correlationContext, parent?.Id))); - } - - /// - /// Gets the id of the instanciated . - /// The workflow id is used as the first out of the two components of the 's id - /// - public virtual string WorkflowId { get; protected set; } - - /// - /// Gets the 's key. - /// The key is used as the second out of the two components of the 's id - /// - public virtual string Key { get; protected set; } - - /// - /// Gets the 's activation type - /// - public virtual V1WorkflowInstanceActivationType ActivationType { get; protected set; } - - /// - /// Gets the id of the 's parent, if any - /// - public virtual string? ParentId { get; protected set; } - - /// - /// Gets the 's input - /// - public virtual object? Input { get; protected set; } - - private List _TriggerEvents = null!; - /// - /// Gets an containing descriptors of the s that have triggered the - /// - public virtual IReadOnlyCollection TriggerEvents - { - get - { - return this._TriggerEvents?.AsReadOnly()!; - } - } - - /// - /// Gets the 's status - /// - public virtual V1WorkflowInstanceStatus Status { get; protected set; } - - /// - /// Gets the date and time at which the has started - /// - public virtual DateTimeOffset? StartedAt { get; protected set; } - - /// - /// Gets the date and time at which the has been executed - /// The value is set when the has been cancelled, faults or completes. - /// - public virtual DateTimeOffset? ExecutedAt { get; protected set; } - - /// - /// Gets the 's - /// - public virtual V1CorrelationContext CorrelationContext { get; protected set; } - - [Newtonsoft.Json.JsonProperty(nameof(Sessions))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Sessions))] - private readonly List _Sessions = new(); - /// - /// Gets an containing the sessions the is made out of - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyCollection Sessions - { - get - { - return this._Sessions.AsReadOnly(); - } - } - - /// - /// Gets the currently active , if any - /// - public virtual V1WorkflowRuntimeSession? ActiveSession => this.Sessions.FirstOrDefault(s => s.IsActive); - - [Newtonsoft.Json.JsonProperty(nameof(Activities))] - [System.Text.Json.Serialization.JsonPropertyName(nameof(Activities))] - private readonly List _Activities = new(); - /// - /// Gets an containing the activities the is made out of - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyCollection Activities - { - get - { - return this._Activities.AsReadOnly(); - } - } - - /// - /// Gets the that caused the to end prematurily - /// - public virtual Neuroglia.Error? Error { get; protected set; } - - /// - /// Gets the 's output - /// - public virtual object? Output { get; protected set; } - - /// - /// Schedules the 's execution - /// - public virtual void Scheduling() - { - if (this.Status != V1WorkflowInstanceStatus.Pending - && this.Status != V1WorkflowInstanceStatus.Suspended) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceSchedulingDomainEvent(this.Id))); - } - - /// - /// Schedules the 's execution - /// - public virtual void MarkAsScheduled() - { - if (this.Status != V1WorkflowInstanceStatus.Pending - && this.Status != V1WorkflowInstanceStatus.Suspended) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceScheduledDomainEvent(this.Id))); - } - - /// - /// Starts the 's execution - /// - /// A string used to uniquely identify the 's process - public virtual void Start(string processId) - { - if(string.IsNullOrEmpty(processId)) - throw DomainException.ArgumentNull(nameof(processId)); - if (this.Status > V1WorkflowInstanceStatus.Scheduled) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceStartingDomainEvent(this.Id, processId))); - } - - /// - /// Marks the as running - /// - public virtual void MarkAsRunning() - { - if (this.Status != V1WorkflowInstanceStatus.Starting - && this.Status != V1WorkflowInstanceStatus.Resuming) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceStartedDomainEvent(this.Id))); - } - - /// - /// Suspsends the 's execution - /// - public virtual void Suspend() - { - if (this.Status != V1WorkflowInstanceStatus.Running) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceSuspendingDomainEvent(this.Id))); - } - - /// - /// Marks the as suspended - /// - public virtual void MarkAsSuspended() - { - if (this.Status != V1WorkflowInstanceStatus.Running - && this.Status != V1WorkflowInstanceStatus.Suspending) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceSuspendedDomainEvent(this.Id))); - } - - /// - /// Resumes the 's execution - /// - /// A string used to uniquely identify the 's process - public virtual void Resume(string processId) - { - if (string.IsNullOrEmpty(processId)) - throw DomainException.ArgumentNull(nameof(processId)); - if (this.Status != V1WorkflowInstanceStatus.Suspended) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceResumingDomainEvent(this.Id, processId))); - } - - /// - /// Marks the as resumed - /// - public virtual void MarkAsResumed() - { - if (this.Status != V1WorkflowInstanceStatus.Suspended) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceResumedDomainEvent(this.Id))); - } - - /// - /// Sets the 's - /// - /// The to set - public virtual void SetCorrelationContext(V1CorrelationContext correlationContext) - { - if (correlationContext == null) - throw DomainException.ArgumentNull(nameof(correlationContext)); - if (this.Status >= V1WorkflowInstanceStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowCorrelationContextChangedDomainEvent(this.Id, correlationContext))); - } - - /// - /// Sets the specified correlation mapping - /// - /// The key of the mapping to set - /// The value of the mapping to set - public virtual void SetCorrelationMapping(string key, string value) - { - if (string.IsNullOrWhiteSpace(key)) - throw DomainException.ArgumentNull(nameof(key)); - if (string.IsNullOrWhiteSpace(value)) - throw DomainException.ArgumentNull(nameof(value)); - if (this.Status >= V1WorkflowInstanceStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowCorrelationMappingSetDomainEvent(this.Id, key, value))); - } - - /// - /// Faults the - /// - /// The that has caused the to fault - public virtual void Fault(Neuroglia.Error error) - { - if (error == null) - throw new ArgumentNullException(nameof(error)); - if (this.Status >= V1WorkflowInstanceStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceFaultedDomainEvent(this.Id, error))); - } - - /// - /// Cancels the 's execution - /// - public virtual void Cancel() - { - if (this.Status >= V1WorkflowInstanceStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceCancellingDomainEvent(this.Id))); - } - - /// - /// Marks the as cancelled - /// - public virtual void MarkAsCancelled() - { - if (this.Status != V1WorkflowInstanceStatus.Cancelling) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceCancelledDomainEvent(this.Id))); - } - - /// - /// Completes and sets the 's output - /// - /// The 's output - public virtual void MarkAsCompleted(object? output) - { - if (this.Status >= V1WorkflowInstanceStatus.Faulted) - throw DomainException.UnexpectedState(typeof(V1WorkflowInstance), this.Id, this.Status); - this.On(this.RegisterEvent(new V1WorkflowInstanceCompletedDomainEvent(this.Id, output))); - } - - /// - /// Deletes the - /// - public virtual void Delete() - { - this.On(this.RegisterEvent(new V1WorkflowInstanceDeletedDomainEvent(this.Id))); - } - - /// - public override string ToString() - { - return this.Id; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceCreatedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - this.WorkflowId = e.WorkflowId; - this.Key = e.Key; - this.ActivationType = e.ActivationType; - this.Input = e.Input; - this._TriggerEvents = e.CorrelationContext.PendingEvents.ToList(); - this.CorrelationContext = e.CorrelationContext; - this.ParentId = e.ParentId; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceSchedulingDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Scheduling; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceScheduledDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Scheduled; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceStartingDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Starting; - this._Sessions.Add(new(e.CreatedAt, e.ProcessId)); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceStartedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Running; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceSuspendingDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Suspending; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceSuspendedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Suspended; - var session = this.Sessions.FirstOrDefault(s => s.IsActive); - if (session != null) - session.MarkAsEnded(e.CreatedAt); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceResumingDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Resuming; - this._Sessions.Add(new(e.CreatedAt, e.ProcessId)); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceResumedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Running; //todo: keep track of runtime sessions - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowCorrelationContextChangedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.CorrelationContext = e.CorrelationContext; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowCorrelationMappingSetDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.CorrelationContext.SetMapping(e.Key, e.Value); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceFaultedDomainEvent e) - { - this.Status = V1WorkflowInstanceStatus.Faulted; - this.Error = e.Error; - this.On(this.RegisterEvent(new V1WorkflowInstanceExecutedDomainEvent(this.Id, this.Status, this.Error))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceCancellingDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Status = V1WorkflowInstanceStatus.Cancelling; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceCancelledDomainEvent e) - { - this.Status = V1WorkflowInstanceStatus.Cancelled; - this.On(this.RegisterEvent(new V1WorkflowInstanceExecutedDomainEvent(this.Id, this.Status))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceCompletedDomainEvent e) - { - this.Status = V1WorkflowInstanceStatus.Completed; - this.Output = e.Output; - this.On(this.RegisterEvent(new V1WorkflowInstanceExecutedDomainEvent(this.Id, this.Status, this.Output))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceExecutedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.ExecutedAt = e.CreatedAt; - var session = this.Sessions.FirstOrDefault(s => s.IsActive); - if (session != null) - session.MarkAsEnded(e.CreatedAt); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowInstanceDeletedDomainEvent e) - { - - } - - /// - /// Builds a unique identifier based on the specified key and - /// - /// The key of the to build the unique identifier for - /// The of the to build the unique identifier for - /// A unique identifier - public static string BuildUniqueIdentifier(string key, V1Workflow workflow) - { - return $"{workflow.Definition.Id}-{key}"; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1WorkflowProcess.cs b/src/core/Synapse.Domain/Models/v1/V1WorkflowProcess.cs deleted file mode 100644 index 61631248a..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1WorkflowProcess.cs +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Domain.Events.V1WorkflowProcesses; - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents a process - /// - [DataTransferObjectType(typeof(Integration.Models.V1WorkflowProcess))] - public class V1WorkflowProcess - : AggregateRoot - { - - /// - /// Initializes a new - /// - protected V1WorkflowProcess() - : base(default!) - { - - } - - /// - /// Initializes a new - /// - /// The 's id - public V1WorkflowProcess(string id) - : base(id) - { - this.On(this.RegisterEvent(new V1WorkflowProcessStartedDomainEvent(this.Id))); - } - - /// - /// Gets the date and time at which the has exited - /// - public virtual DateTimeOffset? ExitedAt { get; protected set; } - - /// - /// Gets the logs associated to the - /// - public virtual string? Logs { get; protected set; } - - /// - /// Gets the 's exit code - /// - public virtual long? ExitCode { get; protected set; } - - /// - /// Gets a boolean indicating whether or not the is running - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual bool IsRunning => !this.ExitedAt.HasValue; - - /// - /// Gets the 's duration - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual TimeSpan? Duration => this.ExitedAt.HasValue ? this.ExitedAt.Value.Subtract(this.CreatedAt) : null; - - /// - /// Appends the specified value to the 's logs - /// - /// The value to append to the 's logs - public virtual void AppendLog(string log) - { - this.On(this.RegisterEvent(new V1WorkflowProcessLogOutputDomainEvent(this.Id, log))); - } - - /// - /// Marks the as exited - /// - /// The 's exit code - public virtual void Exit(long exitCode) - { - this.On(this.RegisterEvent(new V1WorkflowProcessExitedDomainEvent(this.Id, exitCode))); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowProcessStartedDomainEvent e) - { - this.Id = e.AggregateId; - this.CreatedAt = e.CreatedAt; - this.LastModified = e.CreatedAt; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowProcessLogOutputDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.Logs += e.Log + Environment.NewLine; - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void On(V1WorkflowProcessExitedDomainEvent e) - { - this.LastModified = e.CreatedAt; - this.ExitedAt = e.CreatedAt; - this.ExitCode = e.ExitCode; - } - - } - -} diff --git a/src/core/Synapse.Domain/Models/v1/V1WorkflowRuntimeSession.cs b/src/core/Synapse.Domain/Models/v1/V1WorkflowRuntimeSession.cs deleted file mode 100644 index f40c8f158..000000000 --- a/src/core/Synapse.Domain/Models/v1/V1WorkflowRuntimeSession.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Domain.Models -{ - - ///

- /// Represents a 's runtime sessions - /// - [DataTransferObjectType(typeof(Integration.Models.V1WorkflowRuntimeSession))] - public class V1WorkflowRuntimeSession - { - - /// - /// Initializes a new - /// - protected V1WorkflowRuntimeSession() - { - - } - - /// - /// Initializes a new - /// - /// The date and time at which the has started - /// The id used to uniquely identify the process the session is bound to - public V1WorkflowRuntimeSession(DateTimeOffset startedAt, string processId) - { - if (string.IsNullOrWhiteSpace(processId)) - throw new ArgumentNullException(nameof(processId)); - this.StartedAt = startedAt; - this.ProcessId = processId; - } - - /// - /// Gets the string used to uniquely identify the process the session is bound to - /// - public virtual string ProcessId { get; protected set; } = null!; - - /// - /// Gets the date and time at which the has started - /// - public virtual DateTimeOffset StartedAt { get; protected set; } - - /// - /// Gets the date and time at which the has ended - /// - public virtual DateTimeOffset? EndedAt { get; protected set; } - - /// - /// Gets the logs associated to the - /// - public virtual string? Logs { get; protected set; } - - /// - /// Gets a boolean indicating whether or not the is active - /// - [ProjectNever] - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual bool IsActive => !this.EndedAt.HasValue; - - /// - /// Gets the 's duration - /// - [ProjectNever] - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual TimeSpan? Duration => this.EndedAt.HasValue ? this.EndedAt.Value.Subtract(this.StartedAt) : null; - - /// - /// Sets the 's logs - /// - /// The 's logs - public virtual void SetLogs(string logs) - { - this.Logs = logs; - } - - /// - /// Sets the date and time at which the ended - /// - /// The date and time at which the ended - public virtual void MarkAsEnded(DateTimeOffset endedAt) - { - this.EndedAt = endedAt; - } - - } - -} diff --git a/src/core/Synapse.Domain/Properties/GlobalUsings.cs b/src/core/Synapse.Domain/Properties/GlobalUsings.cs deleted file mode 100644 index 702e792b4..000000000 --- a/src/core/Synapse.Domain/Properties/GlobalUsings.cs +++ /dev/null @@ -1,6 +0,0 @@ -global using CloudNative.CloudEvents; -global using Neuroglia; -global using Neuroglia.Data; -global using ServerlessWorkflow.Sdk.Models; -global using Synapse.Domain.Models; -global using Synapse.Integration.Models; \ No newline at end of file diff --git a/src/core/Synapse.Domain/Synapse.Domain.csproj b/src/core/Synapse.Domain/Synapse.Domain.csproj deleted file mode 100644 index 7eb3c159e..000000000 --- a/src/core/Synapse.Domain/Synapse.Domain.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse domain - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - embedded - - - - - - - - - - - \ No newline at end of file diff --git a/src/core/Synapse.Infrastructure/ApplicationModelType.cs b/src/core/Synapse.Infrastructure/ApplicationModelType.cs deleted file mode 100644 index f8ad9a98b..000000000 --- a/src/core/Synapse.Infrastructure/ApplicationModelType.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Synapse.Infrastructure -{ - - /// - /// Enumerates all application model types - /// - public enum ApplicationModelType - { - /// - /// Indicates the application's write model - /// - WriteModel, - /// - /// Indicates the application's read model - /// - ReadModel - } - -} diff --git a/src/core/Synapse.Infrastructure/Plugins/IPlugin.cs b/src/core/Synapse.Infrastructure/Plugins/IPlugin.cs deleted file mode 100644 index 840098f6d..000000000 --- a/src/core/Synapse.Infrastructure/Plugins/IPlugin.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Plugins -{ - - ///

- /// Defines the base contract of all plugins - /// - public interface IPlugin - : IDisposable, IAsyncDisposable - { - - /// - /// Initialzes the - /// - /// A used to manage the 's lifetime - /// A new awaitable - ValueTask InitializeAsync(CancellationToken stoppingToken); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Plugins/IRepositoryPlugin.cs b/src/core/Synapse.Infrastructure/Plugins/IRepositoryPlugin.cs deleted file mode 100644 index 2fe3cde46..000000000 --- a/src/core/Synapse.Infrastructure/Plugins/IRepositoryPlugin.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Plugins -{ - - ///

- /// Defines the fundamentals of an used to create instances - /// - public interface IRepositoryPlugin - : IPlugin - { - - /// - /// Creates a new - /// - /// The type of entity to create a new for - /// The type of key used to manage the entities to create a new for - /// A new - IRepository CreateRepository(Type entityType, Type keyType); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Plugins/Plugin.cs b/src/core/Synapse.Infrastructure/Plugins/Plugin.cs deleted file mode 100644 index 037515757..000000000 --- a/src/core/Synapse.Infrastructure/Plugins/Plugin.cs +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Plugins -{ - - ///

- /// Represents a base class for implementations - /// - public abstract class Plugin - : IPlugin - { - - private bool _Disposed; - - /// - /// Gets the 's - /// - protected CancellationTokenSource CancellationTokenSource { get; private set; } = null!; - - /// - async ValueTask IPlugin.InitializeAsync(CancellationToken stoppingToken) - { - this.CancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); - await this.InitializeAsync(this.CancellationTokenSource.Token); - } - - /// - /// Initialzes the - /// - /// A used to manage the 's lifetime - /// A new awaitable - protected virtual ValueTask InitializeAsync(CancellationToken stoppingToken) - { - return ValueTask.CompletedTask; - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - /// A new awaitable - protected virtual ValueTask DisposeAsync(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - - } - this._Disposed = true; - } - return ValueTask.CompletedTask; - } - - /// - public async ValueTask DisposeAsync() - { - await this.DisposeAsync(true); - GC.SuppressFinalize(this); - } - - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.CancellationTokenSource?.Dispose(); - } - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/core/Synapse.Infrastructure/Properties/GlobalUsings.cs b/src/core/Synapse.Infrastructure/Properties/GlobalUsings.cs deleted file mode 100644 index c4d8687f2..000000000 --- a/src/core/Synapse.Infrastructure/Properties/GlobalUsings.cs +++ /dev/null @@ -1,5 +0,0 @@ -global using CloudNative.CloudEvents; -global using Microsoft.Extensions.Logging; -global using Neuroglia; -global using Neuroglia.Data; -global using Synapse.Domain.Models; \ No newline at end of file diff --git a/src/core/Synapse.Infrastructure/Services/IBackgroundJobManager.cs b/src/core/Synapse.Infrastructure/Services/IBackgroundJobManager.cs deleted file mode 100644 index e8ca34c02..000000000 --- a/src/core/Synapse.Infrastructure/Services/IBackgroundJobManager.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Services; - -///

-/// Defines the fundamentals of a service used to manage background jobs -/// -public interface IBackgroundJobManager -{ - - /// - /// Schedules a new background job - /// - /// The unique id of the job to schedule - /// A representing the job to schedule - /// The date and time at which to schedule the job - /// A - /// A new awaitable - Task ScheduleJobAsync(string jobId, Func job, DateTimeOffset scheduleAt, CancellationToken cancellationToken = default); - - /// - /// Cancels the specified background job - /// - /// The id of the background job to cancel - /// A - /// A new awaitable - Task CancelJobAsync(string jobId, CancellationToken cancellationToken = default); - -} - diff --git a/src/core/Synapse.Infrastructure/Services/IIntegrationEventBus.cs b/src/core/Synapse.Infrastructure/Services/IIntegrationEventBus.cs deleted file mode 100644 index f31b71ca9..000000000 --- a/src/core/Synapse.Infrastructure/Services/IIntegrationEventBus.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using CloudNative.CloudEvents; -using Neuroglia; - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a service used to publish s - /// - public interface IIntegrationEventBus - { - - /// - /// Publishes the specified - /// - /// The to publish - /// A - /// A new awaitable - Task PublishAsync(CloudEvent e, CancellationToken cancellationToken = default); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IIntegrationEventBusFactory.cs b/src/core/Synapse.Infrastructure/Services/IIntegrationEventBusFactory.cs deleted file mode 100644 index 7f8935f01..000000000 --- a/src/core/Synapse.Infrastructure/Services/IIntegrationEventBusFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Synapse.Infrastructure.Services -{ - - /// - /// Defines the fundamentals of a service used to create es - /// - public interface IIntegrationEventBusFactory - { - - /// - /// Creates a new - /// - /// A new - IIntegrationEventBus Create(); - - } - - -} diff --git a/src/core/Synapse.Infrastructure/Services/IPluginHandle.cs b/src/core/Synapse.Infrastructure/Services/IPluginHandle.cs deleted file mode 100644 index dee0b4d85..000000000 --- a/src/core/Synapse.Infrastructure/Services/IPluginHandle.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure.Plugins; -using Synapse.Integration.Models; - -namespace Synapse.Infrastructure.Services; - -///

-/// Defines the fundamentals of an object used to handle a plugin -/// -public interface IPluginHandle - : IDisposable, IAsyncDisposable -{ - - /// - /// Gets the event fired whenever the has been disposed of - /// - event EventHandler Disposed; - - /// - /// Gets the path to the handled 's metadata file - /// - string MetadataFilePath { get; } - - /// - /// Gets a boolean indicating whether or not the is loaded - /// - bool IsLoaded{ get; } - - /// - /// Gets an object used to describe the handled - /// - V1PluginMetadata Metadata { get; } - - /// - /// Loads and initializes the - /// - /// A used to manage the handled 's lifetime - /// A new awaitable - ValueTask LoadAsync(CancellationToken stoppingToken); - - /// - /// Gets the - /// - /// Will throw a new if the is not loaded - /// The loaded - IPlugin GetPlugin(); - - /// - /// Unloads the - /// - void Unload(); - -} \ No newline at end of file diff --git a/src/core/Synapse.Infrastructure/Services/IPluginManager.cs b/src/core/Synapse.Infrastructure/Services/IPluginManager.cs deleted file mode 100644 index 2a458d025..000000000 --- a/src/core/Synapse.Infrastructure/Services/IPluginManager.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Infrastructure.Plugins; - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a service used to manage plugins - /// - public interface IPluginManager - : IService - { - - /// - /// Gets an containing all detected s - /// - IEnumerable Plugins { get; } - - /// - /// Loads the specified - /// - /// The service used to handle the to load - /// The loaded - Task LoadPluginAsync(IPluginHandle pluginHandle); - - /// - /// Unloads the specified - /// - /// The service used to handle the to unload - /// A new awaitable - ValueTask UnloadPluginAsync(IPluginHandle pluginHandle); - - /// - /// Gets the of the specified type, with the specified name - /// - /// The name of the to get - /// The of the specified type - IPlugin? GetPlugin(string name); - - /// - /// Gets the of the specified type - /// - /// The type of the to get - /// The of the specified type - TPlugin? GetPlugin() - where TPlugin : IPlugin; - - /// - /// Gets all s of the specified type - /// - /// The type of the s to get - /// A new containing the s of the specified type - IEnumerable GetPlugins() - where TPlugin : IPlugin; - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IRepositoryFactory.cs b/src/core/Synapse.Infrastructure/Services/IRepositoryFactory.cs deleted file mode 100644 index ae767f469..000000000 --- a/src/core/Synapse.Infrastructure/Services/IRepositoryFactory.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Synapse.Infrastructure.Services -{ - - /// - /// Defines the fundamentals of a service used to create instances - /// - public interface IRepositoryFactory - { - - /// - /// Creates a new - /// - /// The type of entity to create the for - /// The type of key used to uniquely identify the entities managed by the to create - /// The type of the application model to create a new for - /// A new - IRepository CreateRepository(Type entityType, Type keyType, ApplicationModelType modelType); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IService.cs b/src/core/Synapse.Infrastructure/Services/IService.cs deleted file mode 100644 index 11f71234b..000000000 --- a/src/core/Synapse.Infrastructure/Services/IService.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Services -{ - ///

- /// Defines the fundamentals of a service - /// - public interface IService - { - - /// - /// Waits for the to start - /// - /// A used to control the 's lifetime - /// A new awaitable - ValueTask WaitForStartupAsync(CancellationToken stoppingToken); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IWorkflowProcess.cs b/src/core/Synapse.Infrastructure/Services/IWorkflowProcess.cs deleted file mode 100644 index 11029bbb0..000000000 --- a/src/core/Synapse.Infrastructure/Services/IWorkflowProcess.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a workflow process - /// - public interface IWorkflowProcess - : IDisposable, IAsyncDisposable - { - - /// - /// Gets the event fired whenever the has been disposed of - /// - event EventHandler? Disposed; - - /// - /// Gets the event fired whenever the has exited - /// - event EventHandler? Exited; - - /// - /// Gets the 's id - /// - string Id { get; } - - /// - /// Gets the 's status - /// - ProcessStatus Status { get; } - - /// - /// Gets an used to observe the 's logs - /// - IObservable Logs { get; } - - /// - /// Gets the 's exit code - /// - long? ExitCode { get; } - - /// - /// Starts the - /// - /// A - /// A new awaitable - ValueTask StartAsync(CancellationToken cancellationToken = default); - - /// - /// Terminates the - /// - /// A - /// A new awaitable - ValueTask TerminateAsync(CancellationToken cancellationToken = default); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IWorkflowProcessManager.cs b/src/core/Synapse.Infrastructure/Services/IWorkflowProcessManager.cs deleted file mode 100644 index a5da88168..000000000 --- a/src/core/Synapse.Infrastructure/Services/IWorkflowProcessManager.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.Extensions.Hosting; - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a service used to manage es - /// - public interface IWorkflowProcessManager - : IHostedService, IDisposable - { - - /// - /// Starts a new for the specified - /// - /// The instanciated to start a new for - /// The to start a new for - /// A - /// A new - Task StartProcessAsync(V1Workflow workflow, V1WorkflowInstance workflowInstance, CancellationToken cancellationToken = default); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// The with the specified id - IWorkflowProcess GetProcessById(string id); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntime.cs b/src/core/Synapse.Infrastructure/Services/IWorkflowRuntime.cs deleted file mode 100644 index 381015e98..000000000 --- a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntime.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a service used to host workflow runtimes - /// - public interface IWorkflowRuntime - : IDisposable, IAsyncDisposable - { - - /// - /// Creates a new for the specified - /// - /// The instanciated to start a new for - /// The to create a new for - /// A - /// A new - Task CreateProcessAsync(V1Workflow workflow, V1WorkflowInstance workflowInstance, CancellationToken cancellationToken = default); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxy.cs b/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxy.cs deleted file mode 100644 index 416bc3577..000000000 --- a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxy.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a service used to proxy a workflow runtime - /// - public interface IWorkflowRuntimeProxy - : IDisposable - { - - /// - /// The event fired whenever the has been disposed of - /// - event EventHandler? Disposed; - - /// - /// Gets the 's id, which is the same than the id of the executed workflow instance - /// - string Id { get; } - - /// - /// Peforms runtime correlation in the specified - /// - /// The in which to perform the correlation - /// A - /// A new awaitable - Task CorrelateAsync(V1CorrelationContext context, CancellationToken cancellationToken = default); - - /// - /// Suspends the 's execution - /// - /// A - /// A new awaitable - Task SuspendAsync(CancellationToken cancellationToken = default); - - /// - /// Cancels the 's execution - /// - /// A - /// A new awaitable - Task CancelAsync(CancellationToken cancellationToken = default); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxyFactory.cs b/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxyFactory.cs deleted file mode 100644 index 57307fb01..000000000 --- a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxyFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Apis.Runtime; - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a service used to create - /// - public interface IWorkflowRuntimeProxyFactory - { - - /// - /// Creates a new - /// - /// The id of the workflow instance to create a new for - /// The service used to write s to the stream - /// A new - IWorkflowRuntimeProxy CreateProxy(string id, IAsyncStreamWriter signalStream); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxyManager.cs b/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxyManager.cs deleted file mode 100644 index 461494350..000000000 --- a/src/core/Synapse.Infrastructure/Services/IWorkflowRuntimeProxyManager.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Infrastructure.Services -{ - - ///

- /// Defines the fundamentals of a service used to manage instances - /// - public interface IWorkflowRuntimeProxyManager - { - - /// - /// Registers the specified - /// - /// The to register - /// The registered - IWorkflowRuntimeProxy Register(IWorkflowRuntimeProxy runtimeProxy); - - /// - /// Gets the with the specified id - /// - /// The id of the to get - /// The with the specified id - IWorkflowRuntimeProxy GetProxy(string id); - - /// - /// Unregisters the specified - /// - /// The to unregister - void Unregister(IWorkflowRuntimeProxy runtimeProxy); - - } - -} diff --git a/src/core/Synapse.Infrastructure/Synapse.Infrastructure.csproj b/src/core/Synapse.Infrastructure/Synapse.Infrastructure.csproj deleted file mode 100644 index 454d10146..000000000 --- a/src/core/Synapse.Infrastructure/Synapse.Infrastructure.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - net6.0 - enable - enable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse infrastructure - en - Apache-2.0 - True - $(VersionPrefix).0 - $(VersionPrefix).0 - embedded - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/core/Synapse.Integration/AggregateHelper.cs b/src/core/Synapse.Integration/AggregateHelper.cs deleted file mode 100644 index 943d4f632..000000000 --- a/src/core/Synapse.Integration/AggregateHelper.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Defines helpers for handling aggregates - /// - public static class AggregateHelper - { - - /// - /// Gets the name of the specified aggregate type - /// - /// The type of the aggregate to get the name of - /// The name of the specified aggregate type - public static string NameOf(Type type) - { - return type.Name[VersionOf(type).Length..].ToLowerInvariant(); - } - - /// - /// Gets the name of the specified aggregate type - /// - /// The type of the aggregate to get the name of - /// The name of the specified aggregate type - public static string NameOf() - where TAggregate : class - { - return NameOf(typeof(TAggregate)); - } - - /// - /// Gets the version of the specified aggregate type - /// - /// The type of the aggregate to get the version of - /// The version of the specified aggregate type - public static string VersionOf(Type type) - { - if (type == null) - throw new ArgumentNullException(nameof(type)); - var components = type.Name.SplitCamelCase(false, false).Split(' ', StringSplitOptions.RemoveEmptyEntries); - return components.First().ToLowerInvariant(); - } - - /// - /// Gets the version of the specified aggregate type - /// - /// The type of the aggregate to get the version of - /// The version of the specified aggregate type - public static string VersionOf() - { - return VersionOf(typeof(TAggregate)); - } - - } - -} diff --git a/src/core/Synapse.Integration/Attributes/PatchableAttribute.cs b/src/core/Synapse.Integration/Attributes/PatchableAttribute.cs deleted file mode 100644 index 68750f58c..000000000 --- a/src/core/Synapse.Integration/Attributes/PatchableAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Synapse -{ - /// - /// Represents an used to mark a class as patchable - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class PatchableAttribute - : Attribute - { - - - - } - -} diff --git a/src/core/Synapse.Integration/Attributes/ProjectNeverAttribute.cs b/src/core/Synapse.Integration/Attributes/ProjectNeverAttribute.cs deleted file mode 100644 index 20cbf7a3a..000000000 --- a/src/core/Synapse.Integration/Attributes/ProjectNeverAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Synapse -{ - /// - /// Represents an used to ignore the projection of a marked property - /// - [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] - public class ProjectNeverAttribute - : QueryableAttribute - { - - - - } - -} diff --git a/src/core/Synapse.Integration/Attributes/QueryableAttribute.cs b/src/core/Synapse.Integration/Attributes/QueryableAttribute.cs deleted file mode 100644 index a708bfa62..000000000 --- a/src/core/Synapse.Integration/Attributes/QueryableAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Synapse -{ - - /// - /// Represents an used to mark an entity as queryable - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class QueryableAttribute - : Attribute - { - - - - } - -} diff --git a/src/core/Synapse.Integration/Attributes/ReadModelAttribute.cs b/src/core/Synapse.Integration/Attributes/ReadModelAttribute.cs deleted file mode 100644 index fba98a947..000000000 --- a/src/core/Synapse.Integration/Attributes/ReadModelAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Synapse -{ - /// - /// Represents an used to mark an entity as a read model - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ReadModelAttribute - : QueryableAttribute - { - - - - } - -} diff --git a/src/core/Synapse.Integration/CloudEvents.cs b/src/core/Synapse.Integration/CloudEvents.cs deleted file mode 100644 index 62c4dc4b5..000000000 --- a/src/core/Synapse.Integration/CloudEvents.cs +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using CloudNative.CloudEvents; - -namespace Synapse -{ - - ///

- /// Exposes constants about s produced by Synapse - /// - public static class CloudEvents - { - - /// - /// Gets the source uri of Synapse s - /// - public static Uri Source = new("https://synapse.io/runtime/events"); - - /// - /// Gets the path for the specified event CLR type - /// - /// The event type to get the path of - /// The type of aggregate that has produced the event - /// The path - public static string LogicalPathOf(Type eventType, Type aggregateType) - { - if (eventType == null) - throw new ArgumentNullException(nameof(eventType)); - if (aggregateType == null) - throw new ArgumentNullException(nameof(aggregateType)); - var aggregateName = AggregateHelper.NameOf(aggregateType); - var version = AggregateHelper.VersionOf(aggregateType); - var actionName = eventType.Name - .Replace(aggregateName, string.Empty, StringComparison.OrdinalIgnoreCase) - .Replace("v1", string.Empty, StringComparison.OrdinalIgnoreCase) - .Replace("DomainEvent", string.Empty, StringComparison.OrdinalIgnoreCase) - .Replace("IntegrationEvent", string.Empty, StringComparison.OrdinalIgnoreCase) - .ToLower(); - return $"/{aggregateName}/{version}/{actionName}"; - } - - /// - /// Gets the type for the specified event CLR type - /// - /// The event type to get the type of - /// The type of aggregate that has produced the event - /// The type - public static string TypeOf(Type eventType, Type aggregateType) - { - if (eventType == null) - throw new ArgumentNullException(nameof(eventType)); - if (aggregateType == null) - throw new ArgumentNullException(nameof(aggregateType)); - return $"io.synapse{LogicalPathOf(eventType, aggregateType)}"; - } - - /// - /// Gets the type for the specified event CLR type - /// - /// The event type to get the type of - /// The type of aggregate that has produced the event - /// The type - public static string TypeOf() - { - return TypeOf(typeof(TEvent), typeof(TAggregate)); - } - - /// - /// Gets the schema for the specified event CLR type - /// - /// The event type to get the schema for - /// The type of aggregate that has produced the event - /// The schema - public static Uri SchemaOf(Type eventType, Type aggregateType) - { - if (eventType == null) - throw new ArgumentNullException(nameof(eventType)); - if (aggregateType == null) - throw new ArgumentNullException(nameof(aggregateType)); - var aggregateName = AggregateHelper.NameOf(aggregateType); - var version = AggregateHelper.VersionOf(aggregateType); - var actionName = eventType.Name - .Replace(aggregateName, string.Empty, StringComparison.OrdinalIgnoreCase) - .Replace("v1", string.Empty, StringComparison.OrdinalIgnoreCase) - .Replace("DomainEvent", string.Empty, StringComparison.OrdinalIgnoreCase) - .Replace("IntegrationEvent", string.Empty, StringComparison.OrdinalIgnoreCase) - .ToLower(); - return new($"https://synapse.io/events{LogicalPathOf(eventType, aggregateType)}"); - } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/AuthenticationDefinitionCollections/v1/Generated/V1CreateAuthenticationDefinitionCollectionCommand.cs b/src/core/Synapse.Integration/Commands/AuthenticationDefinitionCollections/v1/Generated/V1CreateAuthenticationDefinitionCollectionCommand.cs deleted file mode 100644 index 65db1edd3..000000000 --- a/src/core/Synapse.Integration/Commands/AuthenticationDefinitionCollections/v1/Generated/V1CreateAuthenticationDefinitionCollectionCommand.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.AuthenticationDefinitionCollections -{ - - ///

- /// Represents the ICommand used to create a new V1AuthenticationDefinitionCollection - /// - [DataContract] - public partial class V1CreateAuthenticationDefinitionCollectionCommand - : Command - { - - /// - /// The name of the V1AuthenticationDefinitionCollection to create - /// - [DataMember(Name = "Name", Order = 1)] - [Description("The name of the V1AuthenticationDefinitionCollection to create")] - public virtual string Name { get; set; } - - /// - /// The version of the V1AuthenticationDefinitionCollection to create - /// - [DataMember(Name = "Version", Order = 2)] - [Description("The version of the V1AuthenticationDefinitionCollection to create")] - public virtual string Version { get; set; } - - /// - /// The description of the V1AuthenticationDefinitionCollection to create - /// - [DataMember(Name = "Description", Order = 3)] - [Description("The description of the V1AuthenticationDefinitionCollection to create")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the AuthenticationDefinitions the V1AuthenticationDefinitionCollection to create is made out of - /// - [DataMember(Name = "Authentications", Order = 4)] - [Description("An IReadOnlyCollection`1 containing the AuthenticationDefinitions the V1AuthenticationDefinitionCollection to create is made out of")] - public virtual ICollection Authentications { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Command.cs b/src/core/Synapse.Integration/Commands/Command.cs deleted file mode 100644 index 931d46154..000000000 --- a/src/core/Synapse.Integration/Commands/Command.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Synapse.Integration.Commands -{ - - /// - /// Represents the base class for all commands - /// - public abstract class Command - : DataTransferObject - { - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1CorrelateEventCommand.cs b/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1CorrelateEventCommand.cs deleted file mode 100644 index 8993c8902..000000000 --- a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1CorrelateEventCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Correlations -{ - - ///

- /// Represents the ICommand used to correlate a V1Event - /// - [DataContract] - public partial class V1CorrelateEventCommand - : Command - { - - /// - /// The V1Event to correlate - /// - [DataMember(Name = "Event", Order = 1)] - [Description("The V1Event to correlate")] - public virtual V1Event Event { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1CreateCorrelationCommand.cs b/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1CreateCorrelationCommand.cs deleted file mode 100644 index 997ad7db8..000000000 --- a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1CreateCorrelationCommand.cs +++ /dev/null @@ -1,82 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Correlations -{ - - ///

- /// Represents the ICommand used to create a new V1Correlation - /// - [DataContract] - public partial class V1CreateCorrelationCommand - : Command - { - - /// - /// The activation type of the V1Correlation to create - /// - [DataMember(Name = "ActivationType", Order = 1)] - [Description("The activation type of the V1Correlation to create")] - public virtual V1CorrelationActivationType ActivationType { get; set; } - - /// - /// The lifetime of the V1Correlation to create - /// - [DataMember(Name = "Lifetime", Order = 2)] - [Description("The lifetime of the V1Correlation to create")] - [Required] - public virtual V1CorrelationLifetime Lifetime { get; set; } - - /// - /// The type of V1CorrelationCondition evaluation the V1Correlation should use - /// - [DataMember(Name = "ConditionType", Order = 3)] - [Description("The type of V1CorrelationCondition evaluation the V1Correlation should use")] - [Required] - public virtual V1CorrelationConditionType ConditionType { get; set; } - - /// - /// An IEnumerable`1 containing all V1CorrelationConditions the V1Correlation to create is made out of - /// - [DataMember(Name = "Conditions", Order = 4)] - [Description("An IEnumerable`1 containing all V1CorrelationConditions the V1Correlation to create is made out of")] - [MinLength(1)] - public virtual ICollection Conditions { get; set; } - - /// - /// The V1CorrelationOutcome of the V1Correlation to create - /// - [DataMember(Name = "Outcome", Order = 5)] - [Description("The V1CorrelationOutcome of the V1Correlation to create")] - [Required] - public virtual V1CorrelationOutcome Outcome { get; set; } - - /// - /// The initial V1CorrelationContext of the V1Correlation to create - /// - [DataMember(Name = "Context", Order = 6)] - [Description("The initial V1CorrelationContext of the V1Correlation to create")] - public virtual V1CorrelationContext Context { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelatedEventCommand.cs b/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelatedEventCommand.cs deleted file mode 100644 index 022a21d1e..000000000 --- a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelatedEventCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Correlations -{ - - ///

- /// Represents the ICommand used to delete a correlated V1Event - /// - [DataContract] - public partial class V1DeleteCorrelatedEventCommand - : Command - { - - /// - /// The id of the V1Correlation the V1Event to delete belongs to - /// - [DataMember(Name = "CorrelationId", Order = 1)] - [Description("The id of the V1Correlation the V1Event to delete belongs to")] - public virtual string CorrelationId { get; set; } - - /// - /// The id of the V1CorrelationContext the V1Event to delete belongs to - /// - [DataMember(Name = "ContextId", Order = 2)] - [Description("The id of the V1CorrelationContext the V1Event to delete belongs to")] - public virtual string ContextId { get; set; } - - /// - /// The id of the V1Event to delete - /// - [DataMember(Name = "EventId", Order = 3)] - [Description("The id of the V1Event to delete")] - public virtual string EventId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelationCommand.cs b/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelationCommand.cs deleted file mode 100644 index 1a3fdd542..000000000 --- a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelationCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Correlations -{ - - ///

- /// Represents the ICommand used to delete an existing V1Correlation - /// - [DataContract] - public partial class V1DeleteCorrelationCommand - : Command - { - - /// - /// The id of the V1Correlation to delete - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1Correlation to delete")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelationContextCommand.cs b/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelationContextCommand.cs deleted file mode 100644 index 7efa0e447..000000000 --- a/src/core/Synapse.Integration/Commands/Correlations/v1/Generated/V1DeleteCorrelationContextCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Correlations -{ - - ///

- /// Represents the ICommand used to delete a V1Correlation's V1CorrelationContext - /// - [DataContract] - public partial class V1DeleteCorrelationContextCommand - : Command - { - - /// - /// The id of the V1Correlation the V1CorrelationContext to delete belongs to - /// - [DataMember(Name = "CorrelationId", Order = 1)] - [Description("The id of the V1Correlation the V1CorrelationContext to delete belongs to")] - public virtual string CorrelationId { get; set; } - - /// - /// The id of the V1CorrelationContext to delete - /// - [DataMember(Name = "ContextId", Order = 2)] - [Description("The id of the V1CorrelationContext to delete")] - public virtual string ContextId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/EventDefinitionCollections/v1/Generated/V1CreateEventDefinitionCollectionCommand.cs b/src/core/Synapse.Integration/Commands/EventDefinitionCollections/v1/Generated/V1CreateEventDefinitionCollectionCommand.cs deleted file mode 100644 index 838983e08..000000000 --- a/src/core/Synapse.Integration/Commands/EventDefinitionCollections/v1/Generated/V1CreateEventDefinitionCollectionCommand.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.EventDefinitionCollections -{ - - ///

- /// Represents the ICommand used to create a new V1EventDefinitionCollection - /// - [DataContract] - public partial class V1CreateEventDefinitionCollectionCommand - : Command - { - - /// - /// The name of the V1EventDefinitionCollection to create - /// - [DataMember(Name = "Name", Order = 1)] - [Description("The name of the V1EventDefinitionCollection to create")] - public virtual string Name { get; set; } - - /// - /// The version of the V1EventDefinitionCollection to create - /// - [DataMember(Name = "Version", Order = 2)] - [Description("The version of the V1EventDefinitionCollection to create")] - public virtual string Version { get; set; } - - /// - /// The description of the V1EventDefinitionCollection to create - /// - [DataMember(Name = "Description", Order = 3)] - [Description("The description of the V1EventDefinitionCollection to create")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection to create is made out of - /// - [DataMember(Name = "Events", Order = 4)] - [Description("An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection to create is made out of")] - public virtual ICollection Events { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Events/v1/Generated/V1PublishEventCommand.cs b/src/core/Synapse.Integration/Commands/Events/v1/Generated/V1PublishEventCommand.cs deleted file mode 100644 index 6bd0982b1..000000000 --- a/src/core/Synapse.Integration/Commands/Events/v1/Generated/V1PublishEventCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Events -{ - - ///

- /// Represents the ICommand used to publish a new V1Event - /// - [DataContract] - public partial class V1PublishEventCommand - : Command - { - - /// - /// The V1Event to publish - /// - [DataMember(Name = "Event", Order = 1)] - [Description("The V1Event to publish")] - public virtual V1Event Event { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/FunctionDefinitionCollections/v1/Generated/V1CreateFunctionDefinitionCollectionCommand.cs b/src/core/Synapse.Integration/Commands/FunctionDefinitionCollections/v1/Generated/V1CreateFunctionDefinitionCollectionCommand.cs deleted file mode 100644 index 7041480da..000000000 --- a/src/core/Synapse.Integration/Commands/FunctionDefinitionCollections/v1/Generated/V1CreateFunctionDefinitionCollectionCommand.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.FunctionDefinitionCollections -{ - - ///

- /// Represents the ICommand used to create a new V1FunctionDefinitionCollection - /// - [DataContract] - public partial class V1CreateFunctionDefinitionCollectionCommand - : Command - { - - /// - /// The name of the V1FunctionDefinitionCollection to create - /// - [DataMember(Name = "Name", Order = 1)] - [Description("The name of the V1FunctionDefinitionCollection to create")] - public virtual string Name { get; set; } - - /// - /// The version of the V1FunctionDefinitionCollection to create - /// - [DataMember(Name = "Version", Order = 2)] - [Description("The version of the V1FunctionDefinitionCollection to create")] - public virtual string Version { get; set; } - - /// - /// The description of the V1FunctionDefinitionCollection to create - /// - [DataMember(Name = "Description", Order = 3)] - [Description("The description of the V1FunctionDefinitionCollection to create")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the FunctionDefinitions the V1FunctionDefinitionCollection to create is made out of - /// - [DataMember(Name = "Functions", Order = 4)] - [Description("An IReadOnlyCollection`1 containing the FunctionDefinitions the V1FunctionDefinitionCollection to create is made out of")] - public virtual ICollection Functions { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Generic/v1/Generated/V1DeleteCommand.cs b/src/core/Synapse.Integration/Commands/Generic/v1/Generated/V1DeleteCommand.cs deleted file mode 100644 index 60bde5d65..000000000 --- a/src/core/Synapse.Integration/Commands/Generic/v1/Generated/V1DeleteCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Generic -{ - - ///

- /// Represents the ICommand used to delete an existing IAggregateRoot by id - /// - [DataContract] - public partial class V1DeleteCommand - : Command - { - - /// - /// The id of the entity to delete - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the entity to delete")] - public virtual Dynamic Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Generic/v1/Generated/V1PatchCommand.cs b/src/core/Synapse.Integration/Commands/Generic/v1/Generated/V1PatchCommand.cs deleted file mode 100644 index a5188f694..000000000 --- a/src/core/Synapse.Integration/Commands/Generic/v1/Generated/V1PatchCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Generic -{ - - ///

- /// Represents the ICommand used to patch an existing IAggregateRoot - /// - [DataContract] - public partial class V1PatchCommand - : Command - { - - /// - /// The id of the entity to patch - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the entity to patch")] - public virtual Dynamic Id { get; set; } - - /// - /// The JsonPatchDocument`1 to apply - /// - [DataMember(Name = "Patch", Order = 2)] - [Description("The JsonPatchDocument`1 to apply")] - public virtual JsonPatchDocument Patch { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Generic/v1/V1PatchCommand.cs b/src/core/Synapse.Integration/Commands/Generic/v1/V1PatchCommand.cs deleted file mode 100644 index 757ceb74d..000000000 --- a/src/core/Synapse.Integration/Commands/Generic/v1/V1PatchCommand.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Synapse.Integration.Commands.Generic -{ - - /// - /// Represents the ICommand used to patch an existing IAggregateRoot - /// - /// The type of the aggregate to patch - /// The type of key used to uniquely identify the object to patch - [DataContract] - public partial class V1PatchCommand - : Command - where TAggregate : class - { - - /// - /// The id of the entity to patch - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the entity to patch")] - public virtual TKey Id { get; set; } - - /// - /// The JsonPatchDocument`1 to apply - /// - [DataMember(Name = "Patch", Order = 2)] - [Description("The JsonPatchDocument`1 to apply")] - public virtual JsonPatchDocument Patch { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1CompleteScheduleOccurenceCommand.cs b/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1CompleteScheduleOccurenceCommand.cs deleted file mode 100644 index fe3d3809c..000000000 --- a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1CompleteScheduleOccurenceCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Schedules -{ - - ///

- /// Represents the ICommand used to complete a V1Schedule's occurence - /// - [DataContract] - public partial class V1CompleteScheduleOccurenceCommand - : Command - { - - /// - /// The id of the V1Schedule to complete an occurence of - /// - [DataMember(Name = "ScheduleId", Order = 1)] - [Description("The id of the V1Schedule to complete an occurence of")] - public virtual string ScheduleId { get; set; } - - /// - /// The id of the V1WorkflowInstance that has been executed - /// - [DataMember(Name = "WorkflowInstanceId", Order = 2)] - [Description("The id of the V1WorkflowInstance that has been executed")] - public virtual string WorkflowInstanceId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1CreateScheduleCommand.cs b/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1CreateScheduleCommand.cs deleted file mode 100644 index 03fdf70c5..000000000 --- a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1CreateScheduleCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Schedules -{ - - ///

- /// Represents the ICommand used to create a new V1Schedule - /// - [DataContract] - public partial class V1CreateScheduleCommand - : Command - { - - /// - /// The type of the V1Schedule to create - /// - [DataMember(Name = "ActivationType", Order = 1)] - [Description("The type of the V1Schedule to create")] - public virtual V1ScheduleActivationType ActivationType { get; set; } - - /// - /// The definition of the V1Schedule to create - /// - [DataMember(Name = "Definition", Order = 2)] - [Description("The definition of the V1Schedule to create")] - public virtual ScheduleDefinition Definition { get; set; } - - /// - /// The id of the V1Workflow to schedule - /// - [DataMember(Name = "WorkflowId", Order = 3)] - [Description("The id of the V1Workflow to schedule")] - public virtual string WorkflowId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1MakeScheduleObsoleteCommand.cs b/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1MakeScheduleObsoleteCommand.cs deleted file mode 100644 index 025faca1b..000000000 --- a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1MakeScheduleObsoleteCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Schedules -{ - - ///

- /// Represents the ICommand used to make a V1Schedule obsolete - /// - [DataContract] - public partial class V1MakeScheduleObsoleteCommand - : Command - { - - /// - /// The id of the V1Schedule to make obsolete - /// - [DataMember(Name = "ScheduleId", Order = 1)] - [Description("The id of the V1Schedule to make obsolete")] - public virtual string ScheduleId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1ResumeScheduleCommand.cs b/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1ResumeScheduleCommand.cs deleted file mode 100644 index 2ce766200..000000000 --- a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1ResumeScheduleCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Schedules -{ - - ///

- /// Represents the ICommand used to resume a V1Schedule - /// - [DataContract] - public partial class V1ResumeScheduleCommand - : Command - { - - /// - /// The id of the V1Schedule to resume - /// - [DataMember(Name = "ScheduleId", Order = 1)] - [Description("The id of the V1Schedule to resume")] - public virtual string ScheduleId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1RetireScheduleCommand.cs b/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1RetireScheduleCommand.cs deleted file mode 100644 index d4fb9893f..000000000 --- a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1RetireScheduleCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Schedules -{ - - ///

- /// Represents the ICommand used to retire a V1Schedule - /// - [DataContract] - public partial class V1RetireScheduleCommand - : Command - { - - /// - /// The id of the V1Schedule to retire - /// - [DataMember(Name = "ScheduleId", Order = 1)] - [Description("The id of the V1Schedule to retire")] - public virtual string ScheduleId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1SuspendScheduleCommand.cs b/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1SuspendScheduleCommand.cs deleted file mode 100644 index 0f74dbc7b..000000000 --- a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1SuspendScheduleCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Schedules -{ - - ///

- /// Represents the ICommand used to suspend a V1Schedule - /// - [DataContract] - public partial class V1SuspendScheduleCommand - : Command - { - - /// - /// The id of the V1Schedule to suspend - /// - [DataMember(Name = "ScheduleId", Order = 1)] - [Description("The id of the V1Schedule to suspend")] - public virtual string ScheduleId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1TriggerScheduleCommand.cs b/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1TriggerScheduleCommand.cs deleted file mode 100644 index 41d70b3d0..000000000 --- a/src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1TriggerScheduleCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Schedules -{ - - ///

- /// Represents the ICommand used to trigger a V1Schedule occurence - /// - [DataContract] - public partial class V1TriggerScheduleCommand - : Command - { - - /// - /// The id of the V1Schedule to trigger - /// - [DataMember(Name = "ScheduleId", Order = 1)] - [Description("The id of the V1Schedule to trigger")] - public virtual string ScheduleId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CancelWorkflowActivityCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CancelWorkflowActivityCommand.cs deleted file mode 100644 index 6e31d780f..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CancelWorkflowActivityCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to cancel a V1WorkflowActivity - /// - [DataContract] - public partial class V1CancelWorkflowActivityCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to cancel - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to cancel")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CompensateActivityCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CompensateActivityCommand.cs deleted file mode 100644 index 43b47eed4..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CompensateActivityCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to compensate a V1WorkflowActivity - /// - [DataContract] - public partial class V1CompensateActivityCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to compensate - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to compensate")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CreateWorkflowActivityCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CreateWorkflowActivityCommand.cs deleted file mode 100644 index 767f441f2..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1CreateWorkflowActivityCommand.cs +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to create a new V1WorkflowActivity - /// - [DataContract] - public partial class V1CreateWorkflowActivityCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance the V1WorkflowActivity to create belongs to - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance the V1WorkflowActivity to create belongs to")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The type of V1WorkflowActivity to create - /// - [DataMember(Name = "Type", Order = 2)] - [Description("The type of V1WorkflowActivity to create")] - public virtual V1WorkflowActivityType Type { get; set; } - - /// - /// The input data of the V1WorkflowActivity to create - /// - [DataMember(Name = "Input", Order = 3)] - [Description("The input data of the V1WorkflowActivity to create")] - public virtual Dynamic Input { get; set; } - - /// - /// The metadata of the V1WorkflowActivity to create - /// - [DataMember(Name = "Metadata", Order = 4)] - [Description("The metadata of the V1WorkflowActivity to create")] - public virtual NameValueCollection Metadata { get; set; } - - /// - /// The id the parent of the V1WorkflowActivity to create - /// - [DataMember(Name = "ParentId", Order = 5)] - [Description("The id the parent of the V1WorkflowActivity to create")] - public virtual string ParentId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1FaultWorkflowActivityCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1FaultWorkflowActivityCommand.cs deleted file mode 100644 index 3a60f392d..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1FaultWorkflowActivityCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to fault a V1WorkflowActivity - /// - [DataContract] - public partial class V1FaultWorkflowActivityCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to cancel - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to cancel")] - public virtual string Id { get; set; } - - /// - /// The Error that cause the V1WorkflowActivity to fault - /// - [DataMember(Name = "Error", Order = 2)] - [Description("The Error that cause the V1WorkflowActivity to fault")] - public virtual Error Error { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1MarkActivityAsCompensatedCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1MarkActivityAsCompensatedCommand.cs deleted file mode 100644 index cbc25dba3..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1MarkActivityAsCompensatedCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to mark a V1WorkflowActivity as compensated - /// - [DataContract] - public partial class V1MarkActivityAsCompensatedCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to mark as compensated - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to mark as compensated")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SetWorkflowActivityMetadataCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SetWorkflowActivityMetadataCommand.cs deleted file mode 100644 index 7c42d5497..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SetWorkflowActivityMetadataCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to set the metadata of a V1WorkflowActivity - /// - [DataContract] - public partial class V1SetWorkflowActivityMetadataCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to update the metadata of - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to update the metadata of")] - public virtual string Id { get; set; } - - /// - /// The IDictionary`2 containing the V1WorkflowActivity's updated metadata - /// - [DataMember(Name = "Metadata", Order = 2)] - [Description("The IDictionary`2 containing the V1WorkflowActivity's updated metadata")] - public virtual NameValueCollection Metadata { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SetWorkflowActivityOutputCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SetWorkflowActivityOutputCommand.cs deleted file mode 100644 index 027a807b6..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SetWorkflowActivityOutputCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to set the output of a V1WorkflowActivity - /// - [DataContract] - public partial class V1SetWorkflowActivityOutputCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to suspend - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to suspend")] - public virtual string Id { get; set; } - - /// - /// The V1WorkflowActivity's output - /// - [DataMember(Name = "Output", Order = 2)] - [Description("The V1WorkflowActivity's output")] - public virtual Dynamic Output { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SkipWorkflowActivityCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SkipWorkflowActivityCommand.cs deleted file mode 100644 index 161b55add..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SkipWorkflowActivityCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to skip the execution of a V1WorkflowActivity - /// - [DataContract] - public partial class V1SkipWorkflowActivityCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to cancel - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to cancel")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1StartWorkflowActivityCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1StartWorkflowActivityCommand.cs deleted file mode 100644 index 99d2e999f..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1StartWorkflowActivityCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to start a V1WorkflowActivity - /// - [DataContract] - public partial class V1StartWorkflowActivityCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to start - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to start")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SuspendWorkflowActivityCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SuspendWorkflowActivityCommand.cs deleted file mode 100644 index a671cebd8..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowActivities/v1/Generated/V1SuspendWorkflowActivityCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowActivities -{ - - ///

- /// Represents the ICommand used to suspend a V1WorkflowActivity - /// - [DataContract] - public partial class V1SuspendWorkflowActivityCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to suspend - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to suspend")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ArchiveWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ArchiveWorkflowInstanceCommand.cs deleted file mode 100644 index 742c3117b..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ArchiveWorkflowInstanceCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to archive an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1ArchiveWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to archive - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to archive")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CancelWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CancelWorkflowInstanceCommand.cs deleted file mode 100644 index 667b77058..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CancelWorkflowInstanceCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to cancel the execution of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1CancelWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to cancel - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance to cancel")] - public virtual string WorkflowInstanceId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CollectWorkflowInstanceLogsCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CollectWorkflowInstanceLogsCommand.cs deleted file mode 100644 index 0092782aa..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CollectWorkflowInstanceLogsCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to retrieve the logs of a V1WorkflowInstance's execution - /// - [DataContract] - public partial class V1CollectWorkflowInstanceLogsCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to get the logs of - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to get the logs of")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ConsumeOrBeginCorrelateEventCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ConsumeOrBeginCorrelateEventCommand.cs deleted file mode 100644 index 191ad603a..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ConsumeOrBeginCorrelateEventCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to consume a pending event of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1ConsumeOrBeginCorrelateEventCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to consume a pending event of - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance to consume a pending event of")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The EventDefinition that describes the event to consume - /// - [DataMember(Name = "EventDefinition", Order = 2)] - [Description("The EventDefinition that describes the event to consume")] - public virtual EventDefinition EventDefinition { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ConsumeWorkflowInstancePendingEventCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ConsumeWorkflowInstancePendingEventCommand.cs deleted file mode 100644 index 7d9f60da1..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ConsumeWorkflowInstancePendingEventCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to consume a pending event of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1ConsumeWorkflowInstancePendingEventCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to consume a pending event of - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance to consume a pending event of")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The EventDefinition that describes the event to consume - /// - [DataMember(Name = "EventDefinition", Order = 2)] - [Description("The EventDefinition that describes the event to consume")] - public virtual EventDefinition EventDefinition { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CorrelateWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CorrelateWorkflowInstanceCommand.cs deleted file mode 100644 index 1b96de863..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CorrelateWorkflowInstanceCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents an ICommand used to correlate an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1CorrelateWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to correlate - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to correlate")] - public virtual string Id { get; set; } - - /// - /// The V1CorrelationContext to correlate the V1WorkflowInstance with - /// - [DataMember(Name = "CorrelationContext", Order = 2)] - [Description("The V1CorrelationContext to correlate the V1WorkflowInstance with")] - public virtual V1CorrelationContext CorrelationContext { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CreateWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CreateWorkflowInstanceCommand.cs deleted file mode 100644 index 7a6c49491..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1CreateWorkflowInstanceCommand.cs +++ /dev/null @@ -1,78 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to create a new V1Workflows - /// - [DataContract] - public partial class V1CreateWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1Workflow to instanciate - /// - [DataMember(Name = "WorkflowId", Order = 1)] - [Description("The id of the V1Workflow to instanciate")] - public virtual string WorkflowId { get; set; } - - /// - /// The V1Workflow's activation type - /// - [DataMember(Name = "ActivationType", Order = 2)] - [Description("The V1Workflow's activation type")] - public virtual V1WorkflowInstanceActivationType ActivationType { get; set; } - - /// - /// The input data of the V1WorkflowInstance to create - /// - [DataMember(Name = "InputData", Order = 3)] - [Description("The input data of the V1WorkflowInstance to create")] - public virtual Dynamic InputData { get; set; } - - /// - /// V1CorrelationContext of the V1WorkflowInstance to create - /// - [DataMember(Name = "CorrelationContext", Order = 4)] - [Description("V1CorrelationContext of the V1WorkflowInstance to create")] - public virtual V1CorrelationContext CorrelationContext { get; set; } - - /// - /// A boolean indicating whether or not to automatically start the V1WorkflowInstance once it has been created - /// - [DataMember(Name = "AutoStart", Order = 5)] - [Description("A boolean indicating whether or not to automatically start the V1WorkflowInstance once it has been created")] - public virtual bool AutoStart { get; set; } - - /// - /// The id of the parent V1WorkflowInstance of the V1WorkflowInstance to create - /// - [DataMember(Name = "ParentId", Order = 6)] - [Description("The id of the parent V1WorkflowInstance of the V1WorkflowInstance to create")] - public virtual string ParentId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1DeleteWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1DeleteWorkflowInstanceCommand.cs deleted file mode 100644 index 61d80a151..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1DeleteWorkflowInstanceCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to delete an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1DeleteWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to delete - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance to delete")] - public virtual string WorkflowInstanceId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1FaultWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1FaultWorkflowInstanceCommand.cs deleted file mode 100644 index b419c2a4d..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1FaultWorkflowInstanceCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to fault the execution of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1FaultWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to fault - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to fault")] - public virtual string Id { get; set; } - - /// - /// The Error that caused the V1WorkflowInstance to fault - /// - [DataMember(Name = "Error", Order = 2)] - [Description("The Error that caused the V1WorkflowInstance to fault")] - public virtual Error Error { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsCancelledCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsCancelledCommand.cs deleted file mode 100644 index f3e6518f6..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsCancelledCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to mark the execution of a V1WorkflowInstance as cancelled - /// - [DataContract] - public partial class V1MarkWorkflowInstanceAsCancelledCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to mark as cancelled - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to mark as cancelled")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsStartedCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsStartedCommand.cs deleted file mode 100644 index c83946a8b..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsStartedCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to mark the execution of a V1WorkflowInstance as started - /// - [DataContract] - public partial class V1MarkWorkflowInstanceAsStartedCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to mark as started - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to mark as started")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsSuspendedCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsSuspendedCommand.cs deleted file mode 100644 index 9ca499b7d..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1MarkWorkflowInstanceAsSuspendedCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to mark the execution of a V1WorkflowInstance as suspended - /// - [DataContract] - public partial class V1MarkWorkflowInstanceAsSuspendedCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to mark as suspended - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to mark as suspended")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ResumeWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ResumeWorkflowInstanceCommand.cs deleted file mode 100644 index db554c717..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1ResumeWorkflowInstanceCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to resume the execution of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1ResumeWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to resume the execution of - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance to resume the execution of")] - public virtual string WorkflowInstanceId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceCorrelationMappingCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceCorrelationMappingCommand.cs deleted file mode 100644 index b83a84072..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceCorrelationMappingCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to set a correlation mapping for a V1WorkflowActivity - /// - [DataContract] - public partial class V1SetWorkflowInstanceCorrelationMappingCommand - : Command - { - - /// - /// The id of the V1WorkflowActivity to cancel - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowActivity to cancel")] - public virtual string Id { get; set; } - - /// - /// The key of the correlation mapping to set - /// - [DataMember(Name = "Key", Order = 2)] - [Description("The key of the correlation mapping to set")] - public virtual string Key { get; set; } - - /// - /// The value of the correlation mapping to set - /// - [DataMember(Name = "Value", Order = 3)] - [Description("The value of the correlation mapping to set")] - public virtual string Value { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceOutputCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceOutputCommand.cs deleted file mode 100644 index bd71370de..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceOutputCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to complete and set the output of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1SetWorkflowInstanceOutputCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to start - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to start")] - public virtual string Id { get; set; } - - /// - /// The V1WorkflowInstance's output - /// - [DataMember(Name = "Output", Order = 2)] - [Description("The V1WorkflowInstance's output")] - public virtual Dynamic Output { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceStartedCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceStartedCommand.cs deleted file mode 100644 index 126e32fd2..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SetWorkflowInstanceStartedCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to mark the execution of a V1WorkflowInstance as started - /// - [DataContract] - public partial class V1SetWorkflowInstanceStartedCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to mark as started - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to mark as started")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1StartWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1StartWorkflowInstanceCommand.cs deleted file mode 100644 index 4336cf054..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1StartWorkflowInstanceCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to start the execution of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1StartWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to start - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1WorkflowInstance to start")] - public virtual string Id { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SuspendWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SuspendWorkflowInstanceCommand.cs deleted file mode 100644 index 5a015e530..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1SuspendWorkflowInstanceCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to suspend the execution of an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1SuspendWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to suspend the execution of - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance to suspend the execution of")] - public virtual string WorkflowInstanceId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1TryCorrelateWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1TryCorrelateWorkflowInstanceCommand.cs deleted file mode 100644 index bb4dba266..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/Generated/V1TryCorrelateWorkflowInstanceCommand.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - ///

- /// Represents the ICommand used to attempt correlating a V1Event to an existing V1WorkflowInstance - /// - [DataContract] - public partial class V1TryCorrelateWorkflowInstanceCommand - : Command - { - - /// - /// The id of the V1WorkflowInstance to correlate - /// - [DataMember(Name = "WorkflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance to correlate")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The V1Event to correlate - /// - [DataMember(Name = "Event", Order = 2)] - [Description("The V1Event to correlate")] - public virtual V1Event Event { get; set; } - - /// - /// An IEnumerable`1 containing the mapping keys to use to correlate the V1WorkflowInstance - /// - [DataMember(Name = "MappingKeys", Order = 3)] - [Description("An IEnumerable`1 containing the mapping keys to use to correlate the V1WorkflowInstance")] - public virtual IEnumerable MappingKeys { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/V1CreateWorkflowInstanceCommand.cs b/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/V1CreateWorkflowInstanceCommand.cs deleted file mode 100644 index 33fe85262..000000000 --- a/src/core/Synapse.Integration/Commands/WorkflowInstances/v1/V1CreateWorkflowInstanceCommand.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Commands.WorkflowInstances -{ - - public partial class V1CreateWorkflowInstanceCommand - { - - ///

- /// Initializes a new - /// - public V1CreateWorkflowInstanceCommand() - { - this.AutoStart = true; - } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1ArchiveWorkflowCommand.cs b/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1ArchiveWorkflowCommand.cs deleted file mode 100644 index 080330290..000000000 --- a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1ArchiveWorkflowCommand.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Workflows -{ - - ///

- /// Represents the ICommand used to archive an existing V1Workflow - /// - [DataContract] - public partial class V1ArchiveWorkflowCommand - : Command - { - - /// - /// The id of the V1Workflow to archive - /// - [DataMember(Name = "Id", Order = 1)] - [Description("The id of the V1Workflow to archive")] - public virtual string Id { get; set; } - - /// - /// The version of the V1Workflow to archive - /// - [DataMember(Name = "Version", Order = 2)] - [Description("The version of the V1Workflow to archive")] - public virtual string Version { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1CreateWorkflowCommand.cs b/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1CreateWorkflowCommand.cs deleted file mode 100644 index 009d01ef7..000000000 --- a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1CreateWorkflowCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Workflows -{ - - ///

- /// Represents the ICommand used to create a new V1Workflow - /// - [DataContract] - public partial class V1CreateWorkflowCommand - : Command - { - - /// - /// The definition of the V1Workflow to create - /// - [DataMember(Name = "Definition", Order = 1)] - [Description("The definition of the V1Workflow to create")] - [Required] - public virtual WorkflowDefinition Definition { get; set; } - - /// - /// A boolean indicating whether the V1Workflow should be created only if it does not already exist. Defaults to false, in which case the Definition is automatically versionned - /// - [DataMember(Name = "IfNotExists", Order = 2)] - [Description("A boolean indicating whether the V1Workflow should be created only if it does not already exist. Defaults to false, in which case the Definition is automatically versionned")] - public virtual bool IfNotExists { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1DeleteWorkflowCommand.cs b/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1DeleteWorkflowCommand.cs deleted file mode 100644 index 56eac849c..000000000 --- a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1DeleteWorkflowCommand.cs +++ /dev/null @@ -1,44 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Workflows -{ - - ///

- /// Represents the ICommand used to delete an existing V1Workflow - /// - [DataContract] - public partial class V1DeleteWorkflowCommand - : Command - { - - /// - /// The id of the V1Workflow to delete. Note that failing to specify the version will remove all versions of the specified V1Workflow - /// - [DataMember(Name = "WorkflowId", Order = 1)] - [Description("The id of the V1Workflow to delete. Note that failing to specify the version will remove all versions of the specified V1Workflow")] - [Required, MinLength(1)] - public virtual string WorkflowId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1UploadWorkflowCommand.cs b/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1UploadWorkflowCommand.cs deleted file mode 100644 index 3a0282e44..000000000 --- a/src/core/Synapse.Integration/Commands/Workflows/v1/Generated/V1UploadWorkflowCommand.cs +++ /dev/null @@ -1,51 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Commands.Workflows -{ - - ///

- /// Represents the ICommand used to upload a new V1UploadWorkflowCommand - /// - [DataContract] - public partial class V1UploadWorkflowCommand - : Command - { - - /// - /// The IFormFile that contains the WorkflowDefinition to read - /// - [DataMember(Name = "DefinitionFile", Order = 1)] - [Description("The IFormFile that contains the WorkflowDefinition to read")] - [Required] - public virtual IFormFile DefinitionFile { get; set; } - - /// - /// An IEnumerable`1 of the IFormFiles that contain the resources of the WorkflowDefinition to read - /// - [DataMember(Name = "ResourceFiles", Order = 2)] - [Description("An IEnumerable`1 of the IFormFiles that contain the resources of the WorkflowDefinition to read")] - public virtual IEnumerable ResourceFiles { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/ProcessStatus.cs b/src/core/Synapse.Integration/Enumerations/ProcessStatus.cs deleted file mode 100644 index 72f5717a5..000000000 --- a/src/core/Synapse.Integration/Enumerations/ProcessStatus.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Enumerates all the statuses of a process - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum ProcessStatus - { - /// - /// Indicates that the process is pending startup - /// - [EnumMember(Value = "pending")] - Pending, - /// - /// Indicates that the process is running - /// - [EnumMember(Value = "running")] - Running, - /// - /// Indicates that the process has exited - /// - [EnumMember(Value = "exited")] - Exited - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/RuntimeCorrelationMode.cs b/src/core/Synapse.Integration/Enumerations/RuntimeCorrelationMode.cs deleted file mode 100644 index 2abd4a49e..000000000 --- a/src/core/Synapse.Integration/Enumerations/RuntimeCorrelationMode.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all supported runtime correlation modes - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum RuntimeCorrelationMode - { - /// - /// Indicates that the runtime actibvely for inbound events for a specified amount of time before shutting down and waiting for a Synapse Correlator to restart it upon consumption of correlated events - /// - [EnumMember(Value = "dual")] - Dual, - /// - /// Indicates that the runtime actively listens for inbound events - /// - [EnumMember(Value = "active")] - Active, - /// - /// Indicates that when requested to consume events, the runtime shuts down and waits for a Synapse Correlator to restart it upon consumption of correlated events - /// - [EnumMember(Value = "passive")] - Passive - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1CorrelationActivationType.cs b/src/core/Synapse.Integration/Enumerations/V1CorrelationActivationType.cs deleted file mode 100644 index 5d51a0238..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1CorrelationActivationType.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all supported correlation activation types - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1CorrelationActivationType - { - /// - /// Indicates that the correlation has been explicitly created - /// - [EnumMember(Value = "explicit")] - Explicit = 1, - /// - /// Indicates that the correlation has been implicitly created following the creation of a workflow defining an event-based start - /// - [EnumMember(Value = "implicit")] - Implicit = 2 - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1CorrelationConditionType.cs b/src/core/Synapse.Integration/Enumerations/V1CorrelationConditionType.cs deleted file mode 100644 index 95f7bb5bf..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1CorrelationConditionType.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all supported correlation condition types - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1CorrelationConditionType - { - /// - /// Indicates that the trigger should fire if any of its conditions are met - /// - [EnumMember(Value = "any")] - AnyOf, - /// - /// Indicates that the trigger should fire only if all of its conditions are met - /// - [EnumMember(Value = "all")] - AllOf - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1CorrelationLifetime.cs b/src/core/Synapse.Integration/Enumerations/V1CorrelationLifetime.cs deleted file mode 100644 index 1fb2a1e43..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1CorrelationLifetime.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all supported correlation modes - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1CorrelationLifetime - { - /// - /// Indicates that the correlation is a singleton, and will be disposed of upon release of the single context it is bound to - /// - [EnumMember(Value = "singleton")] - Singleton, - /// - /// Indicates that the correlation is transient, and a new context instance is expected to be created for each event matching any of its conditions - /// - [EnumMember(Value = "transient")] - Transient - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1CorrelationOutcomeType.cs b/src/core/Synapse.Integration/Enumerations/V1CorrelationOutcomeType.cs deleted file mode 100644 index 731d4e1dc..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1CorrelationOutcomeType.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all supported correlation outcome types - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1CorrelationOutcomeType - { - /// - /// Indicates that the correlation should start a new workflow instance - /// - [EnumMember(Value = "start")] - Start, - /// - /// Indicates that the correlation should be performed against an existing workflow instance, possibly waking it up - /// - [EnumMember(Value = "correlate")] - Correlate - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1PluginType.cs b/src/core/Synapse.Integration/Enumerations/V1PluginType.cs deleted file mode 100644 index d85d6e05d..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1PluginType.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all supported plugin types - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1PluginType - { - /// - /// Indicates a generic plugin - /// - [EnumMember(Value = "generic")] - Generic = 0, - /// - /// Indicates a generic plugin - /// - [EnumMember(Value = "repository")] - Repository = 1, - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1ScheduleActivationType.cs b/src/core/Synapse.Integration/Enumerations/V1ScheduleActivationType.cs deleted file mode 100644 index 2461706ab..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1ScheduleActivationType.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all supported schedule activation types - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1ScheduleActivationType - { - /// - /// Indicates that the schedule has been explicitly created - /// - [EnumMember(Value = "explicit")] - Explicit = 1, - /// - /// Indicates that the schedule has been implicitly created following the creation of a workflow defining a scheduled start - /// - [EnumMember(Value = "implicit")] - Implicit = 2 - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1ScheduleStatus.cs b/src/core/Synapse.Integration/Enumerations/V1ScheduleStatus.cs deleted file mode 100644 index b4ac8afe1..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1ScheduleStatus.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Enumerates all possibles workflow schedule statuses - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1ScheduleStatus - { - /// - /// Indicates that the schedule is active - /// - [EnumMember(Value = "active")] - Active = 1, - /// - /// Indicates that the schedule has been suspended - /// - [EnumMember(Value = "suspended")] - Suspended = 2, - /// - /// Indicates that the schedule has been retired, either manually or automatically because it reached its deadline - /// - [EnumMember(Value = "retired")] - Retired = 4, - /// - /// Indicates that the schedule has been made obsolete by a newer version - /// - [EnumMember(Value = "obsolete")] - Obsolete = 8 - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1WorkflowActivityStatus.cs b/src/core/Synapse.Integration/Enumerations/V1WorkflowActivityStatus.cs deleted file mode 100644 index a580028aa..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1WorkflowActivityStatus.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Enumerates all supported values for a workflow activity status - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1WorkflowActivityStatus - { - /// - /// Indicates that the activity is pending execution - /// - [EnumMember(Value = "pending")] - Pending, - /// - /// Indicates that the activity is running - /// - [EnumMember(Value = "running")] - Running, - /// - /// Indicates that the activity has been suspended - /// - [EnumMember(Value = "suspended")] - Suspended, - /// - /// Indicates that the activity has faulted due to an unhandled exception - /// - [EnumMember(Value = "faulted")] - Faulted, - /// - /// Indicates that the activity is being compensated - /// - [EnumMember(Value = "compensating")] - Compensating, - /// - /// Indicates that the activity has been compensated - /// - [EnumMember(Value = "compensated")] - Compensated, - /// - /// Indicates that the activity has been cancelled - /// - [EnumMember(Value = "cancelled")] - Cancelled, - /// - /// Indicates that the activity has been skipped - /// - [EnumMember(Value = "skipped")] - Skipped, - /// - /// Indicates that the activity ran to completion - /// - [EnumMember(Value = "completed")] - Completed - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1WorkflowActivityType.cs b/src/core/Synapse.Integration/Enumerations/V1WorkflowActivityType.cs deleted file mode 100644 index 8da53417b..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1WorkflowActivityType.cs +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using CloudNative.CloudEvents; -using System.Runtime.Serialization; - -namespace Synapse -{ - ///

- /// Enumerates all possible workflow activity types - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1WorkflowActivityType - { - /// - /// Indicates an activity that processes the start of a workflow instance - /// - [EnumMember(Value = "start")] - Start, - /// - /// Indicates an activity that processes a state definition's execution - /// - [EnumMember(Value = "state")] - State, - /// - /// Indicates an activity that processes an action's execution - /// - [EnumMember(Value = "action")] - Action, - /// - /// Indicates an activity that consumes an inbound - /// - [EnumMember(Value = "consume-event")] - ConsumeEvent, - /// - /// Indicates an activity that produces an output - /// - [EnumMember(Value = "produce-event")] - ProduceEvent, - /// - /// Indicates an activity that processes an event trigger - /// - [EnumMember(Value = "event-trigger")] - EventTrigger, - /// - /// Indicates an activity that processes a function call - /// - [EnumMember(Value = "function")] - Function, - /// - /// Indicates an activity that processes a workflow branch - /// - [EnumMember(Value = "branch")] - Branch, - /// - /// Indicates an activity that processes a subflow - /// - [EnumMember(Value = "subflow")] - SubFlow, - /// - /// Indicates an activity that processes a transition from a state to another - /// - [EnumMember(Value = "transition")] - Transition, - /// - /// Indicates an activity that processes an iteration - /// - [EnumMember(Value = "iteration")] - Iteration, - /// - /// Indicates an activity that processes the end of a workflow instance - /// - [EnumMember(Value = "end")] - End, - /// - /// Indicates an activity that handles specific domain errors - /// - [EnumMember(Value = "error")] - Error - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1WorkflowInstanceActivationType.cs b/src/core/Synapse.Integration/Enumerations/V1WorkflowInstanceActivationType.cs deleted file mode 100644 index 91b6c16bb..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1WorkflowInstanceActivationType.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Enumerates all types of workflow instanciation - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1WorkflowInstanceActivationType - { - /// - /// Indicates that the workflow instance has been created manually - /// - [EnumMember(Value = "manual")] - Manual = 0, - /// - /// Indicates that the workflow instance has been dynamically created by an event trigger - /// - [EnumMember(Value = "trigger")] - Trigger = 1, - /// - /// Indicates that the workflow instance has been dynamically created by a schedule - /// - [EnumMember(Value = "schedule")] - Schedule = 2, - /// - /// Indicates that the workflow instance has been created by another workflow instance - /// - [EnumMember(Value = "subflow")] - Subflow = 3 - } - -} diff --git a/src/core/Synapse.Integration/Enumerations/V1WorkflowInstanceStatus.cs b/src/core/Synapse.Integration/Enumerations/V1WorkflowInstanceStatus.cs deleted file mode 100644 index 8e733b18c..000000000 --- a/src/core/Synapse.Integration/Enumerations/V1WorkflowInstanceStatus.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Enumerates all possible workflow instance statuses - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum V1WorkflowInstanceStatus - { - /// - /// Indicates that the workflow instance is pending execution - /// - [EnumMember(Value = "pending")] - Pending, - /// - /// Indicates that the workflow instance is being scheduled - /// - [EnumMember(Value = "scheduling")] - Scheduling, - /// - /// Indicates that the workflow instance has been scheduled - /// - [EnumMember(Value = "scheduled")] - Scheduled, - /// - /// Indicates that the workflow instance is starting - /// - [EnumMember(Value = "starting")] - Starting, - /// - /// Indicates that the workflow instance is running - /// - [EnumMember(Value = "running")] - Running, - /// - /// Indicates that the execution of the workflow instance is being suspended - /// - [EnumMember(Value = "suspending")] - Suspending, - /// - /// Indicates that the workflow instance has been suspended - /// - [EnumMember(Value = "suspended")] - Suspended, - /// - /// Indicates that the execution of the workflow instance is resuming - /// - [EnumMember(Value = "resuming")] - Resuming, - /// - /// Indicates that the workflow instance has faulted due to an unhandled exception - /// - [EnumMember(Value = "faulted")] - Faulted, - /// - /// Indicates that the execution of the workflow instance is being cancelled - /// - [EnumMember(Value = "cancelling")] - Cancelling, - /// - /// Indicates that the workflow instance has been cancelled - /// - [EnumMember(Value = "cancelled")] - Cancelled, - /// - /// Indicates that the workflow instance ran to completion - /// - [EnumMember(Value = "completed")] - Completed - } - -} diff --git a/src/core/Synapse.Integration/EnvironmentVariables.cs b/src/core/Synapse.Integration/EnvironmentVariables.cs deleted file mode 100644 index 4e4b1e96e..000000000 --- a/src/core/Synapse.Integration/EnvironmentVariables.cs +++ /dev/null @@ -1,462 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using CloudNative.CloudEvents; - -namespace Synapse -{ - - ///

- /// Exposes the environment variables used by the application - /// - public static class EnvironmentVariables - { - - /// - /// Gets the prefix for all Synapse environment variables - /// - public const string Prefix = "SYNAPSE_"; - - /// - /// Exposes constants about the environment variable used to skip certificate validation - /// - public static class SkipCertificateValidation - { - - /// - /// Gets the name of the environment variable used to skip certificate validation - /// - public const string Name = Prefix + "SKIP_CERTIFICATE_VALIDATION"; - - /// - /// Gets the value of the environment variable used to skip certificate validation - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - /// - /// Exposes constants about api-related environment variables - /// - public static class Api - { - - /// - /// Gets the prefix for all api related environment variables - /// - public const string Prefix = EnvironmentVariables.Prefix + "API_"; - - /// - /// Exposes constants about the api host name environment variable - /// - public static class HostName - { - - /// - /// Gets the name of the api host environment variable - /// - public const string Name = Prefix + "HOSTNAME"; - - /// - /// Gets the value of the api host environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - /// - /// Exposes constants about HTTP api-related environment variables - /// - public static class Http - { - - /// - /// Gets the prefix for all HTTP api related environment variables - /// - public const string Prefix = EnvironmentVariables.Prefix + "HTTP_"; - - /// - /// Exposes constants about the HTTP api scheme environment variable - /// - public static class Scheme - { - - /// - /// Gets the name of the HTTP api scheme environment variable - /// - public const string Name = Prefix + "SCHEME"; - - /// - /// Gets the value of the HTTP api scheme environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - /// - /// Exposes constants about the HTTP api port environment variable - /// - public static class Port - { - - /// - /// Gets the name of the HTTP api port environment variable - /// - public const string Name = Prefix + "PORT"; - - /// - /// Gets the value of the HTTP api port environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - /// - /// Exposes constants about GRPC api-related environment variables - /// - public static class Grpc - { - - /// - /// Gets the prefix for all GRPC api related environment variables - /// - public const string Prefix = EnvironmentVariables.Prefix + "GRPC_"; - - /// - /// Exposes constants about the GRPC api scheme environment variable - /// - public static class Scheme - { - - /// - /// Gets the name of the GRPC api scheme environment variable - /// - public const string Name = Prefix + "SCHEME"; - - /// - /// Gets the value of the GRPC api scheme environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - /// - /// Exposes constants about the GRPC api port environment variable - /// - public static class Port - { - - /// - /// Gets the name of the GRPC api port environment variable - /// - public const string Name = Prefix + "PORT"; - - /// - /// Gets the value of the GRPC api port environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - } - - /// - /// Exposes constants about -related environment variables - /// - public static class CloudEvents - { - - /// - /// Gets the prefix for all -related environment variables - /// - public const string Prefix = EnvironmentVariables.Prefix + "CLOUDEVENTS_"; - - /// - /// Exposes constants about sink related environment variables - /// - public static class Sink - { - - /// - /// Gets the prefix for all sink related environment variables - /// - public const string Prefix = CloudEvents.Prefix + "SINK_"; - - /// - /// Exposes constants about the sink uri environment variable - /// - public static class Uri - { - - /// - /// Gets the name of the sink uri environment variable - /// - public const string Name = Prefix + "URI"; - - /// - /// Gets the value of the sink uri environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - } - - /// - /// Exposes constants about Kuberneres environment variables - /// - public static class Kubernetes - { - - /// - /// Gets the prefix for all Kubernetes environment variables - /// - public const string Prefix = "KUBERNETES_"; - - /// - /// Exposes constants about the Kubernetes namespace environment variable - /// - public static class Namespace - { - - /// - /// Gets the name of the Kubernetes namespace environment variable - /// - public const string Name = Prefix + "NAMESPACE"; - - /// - /// Gets the value of the Kubernetes namespace environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - /// - /// Exposes constants about the Kubernetes pod name environment variable - /// - public static class PodName - { - - /// - /// Gets the name of the Kubernetes pod name environment variable - /// - public const string Name = Prefix + "POD_NAME"; - - /// - /// Gets the value of the Kubernetes pod name environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - /// - /// Exposes constants about persistence-related environment variables - /// - public static class Persistence - { - - /// - /// Gets the prefix for all persistence related environment variables - /// - public const string Prefix = EnvironmentVariables.Prefix + "PERSISTENCE_"; - - /// - /// Exposes constants about write model-related environment variables - /// - public static class WriteModel - { - - /// - /// Gets the prefix for all write model related environment variables - /// - public const string Prefix = Persistence.Prefix + "WRITEMODEL_"; - - /// - /// Exposes constants about the default write model repository environment variable - /// - public static class DefaultRepository - { - - /// - /// Gets the name of the default write model repository environment variable - /// - public const string Name = Prefix + "DEFAULT_REPOSITORY"; - - /// - /// Gets the value of the default write model repository environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - /// - /// Exposes constants about read model-related environment variables - /// - public static class ReadModel - { - - /// - /// Gets the prefix for all read model related environment variables - /// - public const string Prefix = Persistence.Prefix + "READMODEL_"; - - /// - /// Exposes constants about the default read model repository environment variable - /// - public static class DefaultRepository - { - - /// - /// Gets the name of the default read model repository environment variable - /// - public const string Name = Prefix + "DEFAULT_REPOSITORY"; - - /// - /// Gets the value of the default read model repository environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - } - - /// - /// Exposes constants about plugins-related environment variables - /// - public static class Plugins - { - - /// - /// Gets the prefix for all plugins related environment variables - /// - public const string Prefix = EnvironmentVariables.Prefix + "PLUGINS_"; - - /// - /// Exposes constants about the plugins directory environment variable - /// - public static class Directory - { - - /// - /// Gets the name of the plugins directory environment variable - /// - public const string Name = Prefix + "DIRECTORY"; - - /// - /// Gets the value of the plugins directory environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - /// - /// Exposes constants about runtime-related environment variables - /// - public static class Runtime - { - - /// - /// Gets the prefix for all runtime related environment variables - /// - public const string Prefix = EnvironmentVariables.Prefix + "RUNTIME_"; - - /// - /// Exposes constants about the workflow instance id environment variable - /// - public static class WorkflowInstanceId - { - - /// - /// Gets the name of the workflow instance id environment variable - /// - public const string Name = Prefix + "WORKFLOW_INSTANCE_ID"; - - /// - /// Gets the value of the workflow instance id environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - /// - /// Exposes constants about correlation related environment variables - /// - public static class Correlation - { - - /// - /// Gets the prefix for all correlation related environment variables - /// - public const string Prefix = Runtime.Prefix + "CORRELATION_"; - - /// - /// Exposes constants about the Synapse runtime correlation mode environment variable - /// - public static class Mode - { - - /// - /// Gets the name of the Synapse runtime correlation mode environment variable - /// - public const string Name = Prefix + "MODE"; - - /// - /// Gets the value of the Synapse runtime correlation mode environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - /// - /// Exposes constants about the environment variable that regulates the maximum amount of time an active correlation can be running before going to sleep - /// - public static class MaxActiveDuration - { - - /// - /// Gets the name of the workflow instance id environment variable - /// - public const string Name = Prefix + "MAX_ACTIVE_DURATION"; - - /// - /// Gets the value of the workflow instance id environment variable - /// - public static string Value = Environment.GetEnvironmentVariable(Name); - - } - - } - - } - - } - -} diff --git a/src/core/Synapse.Integration/Events/AuthenticationDefinitionCollections/v1/Generated/V1AuthenticationDefinitionCollectionCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/AuthenticationDefinitionCollections/v1/Generated/V1AuthenticationDefinitionCollectionCreatedIntegrationEvent.cs deleted file mode 100644 index ea64f05e2..000000000 --- a/src/core/Synapse.Integration/Events/AuthenticationDefinitionCollections/v1/Generated/V1AuthenticationDefinitionCollectionCreatedIntegrationEvent.cs +++ /dev/null @@ -1,78 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.AuthenticationDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1EventDefinitionCollection has been created - /// - [DataContract] - public partial class V1AuthenticationDefinitionCollectionCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1EventDefinitionCollection's name - /// - [DataMember(Name = "Name", Order = 3)] - [Description("The V1EventDefinitionCollection's name")] - public virtual string Name { get; set; } - - /// - /// The V1EventDefinitionCollection's version - /// - [DataMember(Name = "Version", Order = 4)] - [Description("The V1EventDefinitionCollection's version")] - public virtual string Version { get; set; } - - /// - /// The V1EventDefinitionCollection's description - /// - [DataMember(Name = "Description", Order = 5)] - [Description("The V1EventDefinitionCollection's description")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection is made out of - /// - [DataMember(Name = "Events", Order = 6)] - [Description("An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection is made out of")] - public virtual ICollection Events { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/AuthenticationDefinitionCollections/v1/Generated/V1AuthenticationDefinitionCollectionDeletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/AuthenticationDefinitionCollections/v1/Generated/V1AuthenticationDefinitionCollectionDeletedIntegrationEvent.cs deleted file mode 100644 index 5e09daf9f..000000000 --- a/src/core/Synapse.Integration/Events/AuthenticationDefinitionCollections/v1/Generated/V1AuthenticationDefinitionCollectionDeletedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.AuthenticationDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1EventDefinitionCollection has been deleted - /// - [DataContract] - public partial class V1AuthenticationDefinitionCollectionDeletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1ContextAddedToCorrelationIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1ContextAddedToCorrelationIntegrationEvent.cs deleted file mode 100644 index d38c033f4..000000000 --- a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1ContextAddedToCorrelationIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Correlations -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1CorrelationContext has been added to an existing V1Correlation - /// - [DataContract] - public partial class V1ContextAddedToCorrelationIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1CorrelationContext that has been added to the V1Correlation - /// - [DataMember(Name = "Context", Order = 3)] - [Description("The V1CorrelationContext that has been added to the V1Correlation")] - public virtual V1CorrelationContext Context { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelatedEventReleasedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelatedEventReleasedIntegrationEvent.cs deleted file mode 100644 index 3c985789f..000000000 --- a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelatedEventReleasedIntegrationEvent.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Correlations -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Event has been released by a V1Correlation - /// - [DataContract] - public partial class V1CorrelatedEventReleasedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the V1CorrelationContext the V1Event has been released from - /// - [DataMember(Name = "ContextId", Order = 3)] - [Description("The id of the V1CorrelationContext the V1Event has been released from")] - public virtual string ContextId { get; set; } - - /// - /// The id of the V1Event that has been released - /// - [DataMember(Name = "EventId", Order = 4)] - [Description("The id of the V1Event that has been released")] - public virtual string EventId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationContextReleasedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationContextReleasedIntegrationEvent.cs deleted file mode 100644 index 9bb33cd63..000000000 --- a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationContextReleasedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Correlations -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1CorrelationContext has been released by a V1Correlation - /// - [DataContract] - public partial class V1CorrelationContextReleasedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the context that has been released - /// - [DataMember(Name = "ContextId", Order = 3)] - [Description("The id of the context that has been released")] - public virtual string ContextId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationCreatedIntegrationEvent.cs deleted file mode 100644 index c2c231df1..000000000 --- a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationCreatedIntegrationEvent.cs +++ /dev/null @@ -1,92 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Correlations -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1Correlation has been created - /// - [DataContract] - public partial class V1CorrelationCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The newly created V1Correlation's activation type - /// - [DataMember(Name = "ActivationType", Order = 3)] - [Description("The newly created V1Correlation's activation type")] - public virtual V1CorrelationActivationType ActivationType { get; set; } - - /// - /// The lifetime of the newly created V1Correlation - /// - [DataMember(Name = "Lifetime", Order = 4)] - [Description("The lifetime of the newly created V1Correlation")] - public virtual V1CorrelationLifetime Lifetime { get; set; } - - /// - /// The type of V1CorrelationCondition evaluation the newly created V1Correlation should use - /// - [DataMember(Name = "ConditionType", Order = 5)] - [Description("The type of V1CorrelationCondition evaluation the newly created V1Correlation should use")] - public virtual V1CorrelationConditionType ConditionType { get; set; } - - /// - /// An IEnumerable`1 containing all V1CorrelationConditions the newly created V1Correlation is made out of - /// - [DataMember(Name = "Conditions", Order = 6)] - [Description("An IEnumerable`1 containing all V1CorrelationConditions the newly created V1Correlation is made out of")] - public virtual IEnumerable Conditions { get; set; } - - /// - /// The V1CorrelationOutcome of the newly created V1Correlation - /// - [DataMember(Name = "Outcome", Order = 7)] - [Description("The V1CorrelationOutcome of the newly created V1Correlation")] - public virtual V1CorrelationOutcome Outcome { get; set; } - - /// - /// The initial V1CorrelationContext of the newly created V1Correlation - /// - [DataMember(Name = "Context", Order = 8)] - [Description("The initial V1CorrelationContext of the newly created V1Correlation")] - public virtual V1CorrelationContext Context { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationDeletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationDeletedIntegrationEvent.cs deleted file mode 100644 index de1ffa3d2..000000000 --- a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1CorrelationDeletedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Correlations -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1Correlation has been deleted - /// - [DataContract] - public partial class V1CorrelationDeletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1EventCorrelatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1EventCorrelatedIntegrationEvent.cs deleted file mode 100644 index 74451dffe..000000000 --- a/src/core/Synapse.Integration/Events/Correlations/v1/Generated/V1EventCorrelatedIntegrationEvent.cs +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Correlations -{ - - ///

- /// Represents the IDomainEvent fired whenever a new CloudEvent has been correlated - /// - [DataContract] - public partial class V1EventCorrelatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the context in which the correlation has been performed - /// - [DataMember(Name = "ContextId", Order = 3)] - [Description("The id of the context in which the correlation has been performed")] - public virtual string ContextId { get; set; } - - /// - /// The V1Event that has been correlated - /// - [DataMember(Name = "Event", Order = 4)] - [Description("The V1Event that has been correlated")] - public virtual V1Event Event { get; set; } - - /// - /// An ICollection`1 containing the keys of the mappings used to correlate the V1Event - /// - [DataMember(Name = "Mappings", Order = 5)] - [Description("An ICollection`1 containing the keys of the mappings used to correlate the V1Event")] - public virtual IEnumerable Mappings { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/EventDefinitionCollections/v1/Generated/V1EventDefinitionCollectionCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/EventDefinitionCollections/v1/Generated/V1EventDefinitionCollectionCreatedIntegrationEvent.cs deleted file mode 100644 index 168b9eeca..000000000 --- a/src/core/Synapse.Integration/Events/EventDefinitionCollections/v1/Generated/V1EventDefinitionCollectionCreatedIntegrationEvent.cs +++ /dev/null @@ -1,78 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.EventDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1EventDefinitionCollection has been created - /// - [DataContract] - public partial class V1EventDefinitionCollectionCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1EventDefinitionCollection's name - /// - [DataMember(Name = "Name", Order = 3)] - [Description("The V1EventDefinitionCollection's name")] - public virtual string Name { get; set; } - - /// - /// The V1EventDefinitionCollection's version - /// - [DataMember(Name = "Version", Order = 4)] - [Description("The V1EventDefinitionCollection's version")] - public virtual string Version { get; set; } - - /// - /// The V1EventDefinitionCollection's description - /// - [DataMember(Name = "Description", Order = 5)] - [Description("The V1EventDefinitionCollection's description")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection is made out of - /// - [DataMember(Name = "Events", Order = 6)] - [Description("An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection is made out of")] - public virtual ICollection Events { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/EventDefinitionCollections/v1/Generated/V1EventDefinitionCollectionDeletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/EventDefinitionCollections/v1/Generated/V1EventDefinitionCollectionDeletedIntegrationEvent.cs deleted file mode 100644 index 482619353..000000000 --- a/src/core/Synapse.Integration/Events/EventDefinitionCollections/v1/Generated/V1EventDefinitionCollectionDeletedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.EventDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1EventDefinitionCollection has been deleted - /// - [DataContract] - public partial class V1EventDefinitionCollectionDeletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionAddedToCollectionIntegrationEvent.cs b/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionAddedToCollectionIntegrationEvent.cs deleted file mode 100644 index 34c259da7..000000000 --- a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionAddedToCollectionIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.FunctionDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever the description of a V1FunctionDefinitionCollection has changed - /// - [DataContract] - public partial class V1FunctionDefinitionAddedToCollectionIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The newly added FunctionDefinition - /// - [DataMember(Name = "Function", Order = 3)] - [Description("The newly added FunctionDefinition")] - public virtual FunctionDefinition Function { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionCreatedIntegrationEvent.cs deleted file mode 100644 index 523711e01..000000000 --- a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionCreatedIntegrationEvent.cs +++ /dev/null @@ -1,78 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.FunctionDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1FunctionDefinitionCollection has been created - /// - [DataContract] - public partial class V1FunctionDefinitionCollectionCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1FunctionDefinitionCollection's name - /// - [DataMember(Name = "Name", Order = 3)] - [Description("The V1FunctionDefinitionCollection's name")] - public virtual string Name { get; set; } - - /// - /// The V1FunctionDefinitionCollection's version - /// - [DataMember(Name = "Version", Order = 4)] - [Description("The V1FunctionDefinitionCollection's version")] - public virtual string Version { get; set; } - - /// - /// The V1FunctionDefinitionCollection's description - /// - [DataMember(Name = "Description", Order = 5)] - [Description("The V1FunctionDefinitionCollection's description")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the FunctionDefinitions the V1FunctionDefinitionCollection is made out of - /// - [DataMember(Name = "Functions", Order = 6)] - [Description("An IReadOnlyCollection`1 containing the FunctionDefinitions the V1FunctionDefinitionCollection is made out of")] - public virtual ICollection Functions { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionDeletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionDeletedIntegrationEvent.cs deleted file mode 100644 index 4a3792f86..000000000 --- a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionDeletedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.FunctionDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1FunctionDefinitionCollection has been deleted - /// - [DataContract] - public partial class V1FunctionDefinitionCollectionDeletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionDescriptionChangedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionDescriptionChangedIntegrationEvent.cs deleted file mode 100644 index d2e02465e..000000000 --- a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionCollectionDescriptionChangedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.FunctionDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever the description of a V1FunctionDefinitionCollection has changed - /// - [DataContract] - public partial class V1FunctionDefinitionCollectionDescriptionChangedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1FunctionDefinitionCollection's updated description - /// - [DataMember(Name = "Description", Order = 3)] - [Description("The V1FunctionDefinitionCollection's updated description")] - public virtual string Description { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionRemovedFromCollectionIntegrationEvent.cs b/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionRemovedFromCollectionIntegrationEvent.cs deleted file mode 100644 index 719099044..000000000 --- a/src/core/Synapse.Integration/Events/FunctionDefinitionCollections/v1/Generated/V1FunctionDefinitionRemovedFromCollectionIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.FunctionDefinitionCollections -{ - - ///

- /// Represents the IDomainEvent fired whenever the description of a V1FunctionDefinitionCollection has changed - /// - [DataContract] - public partial class V1FunctionDefinitionRemovedFromCollectionIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The name of the removed FunctionDefinition - /// - [DataMember(Name = "FunctionName", Order = 3)] - [Description("The name of the removed FunctionDefinition")] - public virtual string FunctionName { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/IV1WorkflowActivityIntegrationEvent.cs b/src/core/Synapse.Integration/Events/IV1WorkflowActivityIntegrationEvent.cs deleted file mode 100644 index 19e8b5a01..000000000 --- a/src/core/Synapse.Integration/Events/IV1WorkflowActivityIntegrationEvent.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events -{ - - ///

- /// Defines the fundamentals of workflow activity related s - /// - public interface IV1WorkflowActivityIntegrationEvent - : IIntegrationEvent - { - - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleCreatedIntegrationEvent.cs deleted file mode 100644 index 605538c0f..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleCreatedIntegrationEvent.cs +++ /dev/null @@ -1,78 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1Schedule has been created - /// - [DataContract] - public partial class V1ScheduleCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The activation type of the newly created V1Schedule - /// - [DataMember(Name = "ActivationType", Order = 3)] - [Description("The activation type of the newly created V1Schedule")] - public virtual V1ScheduleActivationType ActivationType { get; set; } - - /// - /// The definition of the newly created V1Schedule - /// - [DataMember(Name = "Definition", Order = 4)] - [Description("The definition of the newly created V1Schedule")] - public virtual ScheduleDefinition Definition { get; set; } - - /// - /// The id of the V1Workflow scheduled by the newly created V1Schedule - /// - [DataMember(Name = "WorkflowId", Order = 5)] - [Description("The id of the V1Workflow scheduled by the newly created V1Schedule")] - public virtual string WorkflowId { get; set; } - - /// - /// The V1Schedule's next occurence - /// - [DataMember(Name = "NextOccurenceAt", Order = 6)] - [Description("The V1Schedule's next occurence")] - public virtual DateTime? NextOccurenceAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleDefinitionChangedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleDefinitionChangedIntegrationEvent.cs deleted file mode 100644 index 4c0886cfb..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleDefinitionChangedIntegrationEvent.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever the ScheduleDefinition of a V1Schedule has changed - /// - [DataContract] - public partial class V1ScheduleDefinitionChangedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1Schedule's updated ScheduleDefinition - /// - [DataMember(Name = "Definition", Order = 3)] - [Description("The V1Schedule's updated ScheduleDefinition")] - public virtual ScheduleDefinition Definition { get; set; } - - /// - /// The V1Schedule's next occurence - /// - [DataMember(Name = "NextOccurenceAt", Order = 4)] - [Description("The V1Schedule's next occurence")] - public virtual DateTime? NextOccurenceAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleDeletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleDeletedIntegrationEvent.cs deleted file mode 100644 index 780e2ae4f..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleDeletedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Schedule has been deleted - /// - [DataContract] - public partial class V1ScheduleDeletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleObsolitedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleObsolitedIntegrationEvent.cs deleted file mode 100644 index cb799e1ff..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleObsolitedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Schedule has been made obsolete - /// - [DataContract] - public partial class V1ScheduleObsolitedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleOccuredIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleOccuredIntegrationEvent.cs deleted file mode 100644 index 12039d045..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleOccuredIntegrationEvent.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Schedule has occured - /// - [DataContract] - public partial class V1ScheduleOccuredIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the V1WorkflowInstance that has been created as the result of the V1Schedule's occurence - /// - [DataMember(Name = "WorkflowInstanceId", Order = 3)] - [Description("The id of the V1WorkflowInstance that has been created as the result of the V1Schedule's occurence")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The V1Schedule's next occurence - /// - [DataMember(Name = "NextOccurenceAt", Order = 4)] - [Description("The V1Schedule's next occurence")] - public virtual DateTime? NextOccurenceAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleOccurenceCompletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleOccurenceCompletedIntegrationEvent.cs deleted file mode 100644 index 2891d82f9..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleOccurenceCompletedIntegrationEvent.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a scheduled V1Workflow has been executed - /// - [DataContract] - public partial class V1ScheduleOccurenceCompletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the V1Schedule's occurence V1WorkflowInstance that has been executed - /// - [DataMember(Name = "WorkflowInstanceId", Order = 3)] - [Description("The id of the V1Schedule's occurence V1WorkflowInstance that has been executed")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The V1Schedule's next occurence - /// - [DataMember(Name = "NextOccurenceAt", Order = 4)] - [Description("The V1Schedule's next occurence")] - public virtual DateTime? NextOccurenceAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleResumedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleResumedIntegrationEvent.cs deleted file mode 100644 index def4a75cc..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleResumedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Schedule has been resumed - /// - [DataContract] - public partial class V1ScheduleResumedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1Schedule's next occurence - /// - [DataMember(Name = "NextOccurenceAt", Order = 3)] - [Description("The V1Schedule's next occurence")] - public virtual DateTime? NextOccurenceAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleRetiredIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleRetiredIntegrationEvent.cs deleted file mode 100644 index 210830b6d..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleRetiredIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Schedule has been retired - /// - [DataContract] - public partial class V1ScheduleRetiredIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleSuspendedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleSuspendedIntegrationEvent.cs deleted file mode 100644 index 2a8ff1fb6..000000000 --- a/src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleSuspendedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Schedules -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Schedule has been suspended - /// - [DataContract] - public partial class V1ScheduleSuspendedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/V1IntegrationEvent.cs b/src/core/Synapse.Integration/Events/V1IntegrationEvent.cs deleted file mode 100644 index 20fb30334..000000000 --- a/src/core/Synapse.Integration/Events/V1IntegrationEvent.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events -{ - - ///

- /// Describes an integration event, which is an event used to exchange information across domain boundaries - /// - public abstract class V1IntegrationEvent - : IIntegrationEvent, IDataTransferObject - { - - - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCancelledIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCancelledIntegrationEvent.cs deleted file mode 100644 index f6f989b3b..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCancelledIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowActivity has been cancelled - /// - [DataContract] - public partial class V1WorkflowActivityCancelledIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompensatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompensatedIntegrationEvent.cs deleted file mode 100644 index 421fe471a..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompensatedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowActivity has been compensated - /// - [DataContract] - public partial class V1WorkflowActivityCompensatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompensatingIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompensatingIntegrationEvent.cs deleted file mode 100644 index 0cd17b75a..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompensatingIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowActivity is being compensated - /// - [DataContract] - public partial class V1WorkflowActivityCompensatingIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompletedIntegrationEvent.cs deleted file mode 100644 index 19c181db7..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCompletedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowActivity has been completed - /// - [DataContract] - public partial class V1WorkflowActivityCompletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1WorkflowActivity's output, if any - /// - [DataMember(Name = "Output", Order = 3)] - [Description("The V1WorkflowActivity's output, if any")] - public virtual Dynamic Output { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCreatedIntegrationEvent.cs deleted file mode 100644 index b1cdc42f4..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityCreatedIntegrationEvent.cs +++ /dev/null @@ -1,85 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1WorkflowActivity has been created - /// - [DataContract] - public partial class V1WorkflowActivityCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the V1WorkflowInstance the newly created V1WorkflowActivity belongs to - /// - [DataMember(Name = "WorkflowInstanceId", Order = 3)] - [Description("The id of the V1WorkflowInstance the newly created V1WorkflowActivity belongs to")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The newly created V1WorkflowActivity's type - /// - [DataMember(Name = "Type", Order = 4)] - [Description("The newly created V1WorkflowActivity's type")] - public virtual V1WorkflowActivityType Type { get; set; } - - /// - /// The newly created V1WorkflowActivity's data - /// - [DataMember(Name = "Input", Order = 5)] - [Description("The newly created V1WorkflowActivity's data")] - public virtual Dynamic Input { get; set; } - - /// - /// The newly created V1WorkflowActivity's metadata - /// - [DataMember(Name = "Metadata", Order = 6)] - [Description("The newly created V1WorkflowActivity's metadata")] - public virtual NameValueCollection Metadata { get; set; } - - /// - /// The id of the newly created V1WorkflowActivity's parent, if any - /// - [DataMember(Name = "ParentId", Order = 7)] - [Description("The id of the newly created V1WorkflowActivity's parent, if any")] - public virtual string ParentId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityExecutedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityExecutedIntegrationEvent.cs deleted file mode 100644 index 2ce0c21b2..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityExecutedIntegrationEvent.cs +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowActivity has been executed - /// - [DataContract] - public partial class V1WorkflowActivityExecutedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1WorkflowActivityStatus of the V1WorkflowActivity when it finished executing - /// - [DataMember(Name = "Status", Order = 3)] - [Description("The V1WorkflowActivityStatus of the V1WorkflowActivity when it finished executing")] - public virtual V1WorkflowActivityStatus Status { get; set; } - - /// - /// The V1WorkflowActivity's error, if any - /// - [DataMember(Name = "Error", Order = 4)] - [Description("The V1WorkflowActivity's error, if any")] - public virtual Error Error { get; set; } - - /// - /// The V1WorkflowActivity's output, if any - /// - [DataMember(Name = "Output", Order = 5)] - [Description("The V1WorkflowActivity's output, if any")] - public virtual Dynamic Output { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityFaultedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityFaultedIntegrationEvent.cs deleted file mode 100644 index 1febd34c7..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityFaultedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowActivity has faulted - /// - [DataContract] - public partial class V1WorkflowActivityFaultedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The Error due to which the V1WorkflowActivity has faulted - /// - [DataMember(Name = "Error", Order = 3)] - [Description("The Error due to which the V1WorkflowActivity has faulted")] - public virtual Error Error { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityMetadataChangedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityMetadataChangedIntegrationEvent.cs deleted file mode 100644 index c81bf44d0..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityMetadataChangedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever the metadata of a V1WorkflowActivity has changed - /// - [DataContract] - public partial class V1WorkflowActivityMetadataChangedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1WorkflowActivity's metadata - /// - [DataMember(Name = "Metadata", Order = 3)] - [Description("The V1WorkflowActivity's metadata")] - public virtual NameValueCollection Metadata { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityResumedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityResumedIntegrationEvent.cs deleted file mode 100644 index b746ddd84..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityResumedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowActivity has been suspended - /// - [DataContract] - public partial class V1WorkflowActivityResumedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivitySkippedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivitySkippedIntegrationEvent.cs deleted file mode 100644 index b383a95b6..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivitySkippedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1WorkflowActivity has been skipped - /// - [DataContract] - public partial class V1WorkflowActivitySkippedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityStartedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityStartedIntegrationEvent.cs deleted file mode 100644 index d3f203eb0..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivityStartedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowActivity has started - /// - [DataContract] - public partial class V1WorkflowActivityStartedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivitySuspendedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivitySuspendedIntegrationEvent.cs deleted file mode 100644 index 7cf01a25c..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/Generated/V1WorkflowActivitySuspendedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowActivity has been suspended - /// - [DataContract] - public partial class V1WorkflowActivitySuspendedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCancelledIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCancelledIntegrationEvent.cs deleted file mode 100644 index b1cb6767a..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCancelledIntegrationEvent.cs +++ /dev/null @@ -1,29 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - public partial class V1WorkflowActivityCancelledIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCompletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCompletedIntegrationEvent.cs deleted file mode 100644 index aac36a7b0..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCompletedIntegrationEvent.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - public partial class V1WorkflowActivityCompletedIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - ///

- /// Initializes a new - /// - public V1WorkflowActivityCompletedIntegrationEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the completed workflow activity - /// The output of the completed workflow activity - public V1WorkflowActivityCompletedIntegrationEvent(string id, object output) - { - this.AggregateId = id; - var outputValue = output as Dynamic; - if (outputValue == null - && output != null) - outputValue = Dynamic.FromObject(output); - this.Output = outputValue; - } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCreatedIntegrationEvent.cs deleted file mode 100644 index 36cbd3d0f..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityCreatedIntegrationEvent.cs +++ /dev/null @@ -1,28 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - public partial class V1WorkflowActivityCreatedIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityFaultedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityFaultedIntegrationEvent.cs deleted file mode 100644 index 5c99c7a85..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityFaultedIntegrationEvent.cs +++ /dev/null @@ -1,29 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - public partial class V1WorkflowActivityFaultedIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityResumedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityResumedIntegrationEvent.cs deleted file mode 100644 index 90d369031..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityResumedIntegrationEvent.cs +++ /dev/null @@ -1,27 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - public partial class V1WorkflowActivityResumedIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivitySkippedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivitySkippedIntegrationEvent.cs deleted file mode 100644 index f64b3b352..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivitySkippedIntegrationEvent.cs +++ /dev/null @@ -1,43 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - public partial class V1WorkflowActivitySkippedIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - ///

- /// Initializes a new - /// - public V1WorkflowActivitySkippedIntegrationEvent() - { - - } - - /// - /// Initializes a new - /// - /// The id of the skipped activity - public V1WorkflowActivitySkippedIntegrationEvent(string id) - { - this.AggregateId = id; - } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityStartedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityStartedIntegrationEvent.cs deleted file mode 100644 index 9951e87c5..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivityStartedIntegrationEvent.cs +++ /dev/null @@ -1,27 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - - public partial class V1WorkflowActivityStartedIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivitySuspendedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivitySuspendedIntegrationEvent.cs deleted file mode 100644 index 971a50250..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowActivities/v1/V1WorkflowActivitySuspendedIntegrationEvent.cs +++ /dev/null @@ -1,26 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace Synapse.Integration.Events.WorkflowActivities -{ - public partial class V1WorkflowActivitySuspendedIntegrationEvent - : IV1WorkflowActivityIntegrationEvent - { - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowCorrelationContextChangedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowCorrelationContextChangedIntegrationEvent.cs deleted file mode 100644 index 04cb588db..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowCorrelationContextChangedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the V1CorrelationContext of an existing V1WorkflowInstance has changed - /// - [DataContract] - public partial class V1WorkflowCorrelationContextChangedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1WorkflowInstance's new V1CorrelationContext - /// - [DataMember(Name = "CorrelationContext", Order = 3)] - [Description("The V1WorkflowInstance's new V1CorrelationContext")] - public virtual V1CorrelationContext CorrelationContext { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowCorrelationMappingSetIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowCorrelationMappingSetIntegrationEvent.cs deleted file mode 100644 index ae5c8855f..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowCorrelationMappingSetIntegrationEvent.cs +++ /dev/null @@ -1,64 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowInstance's correlation mapping has been set - /// - [DataContract] - public partial class V1WorkflowCorrelationMappingSetIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The key of the V1WorkflowInstance's correlation mapping that has been set - /// - [DataMember(Name = "Key", Order = 3)] - [Description("The key of the V1WorkflowInstance's correlation mapping that has been set")] - public virtual string Key { get; set; } - - /// - /// The value of the V1WorkflowInstance's correlation mapping that has been set - /// - [DataMember(Name = "Value", Order = 4)] - [Description("The value of the V1WorkflowInstance's correlation mapping that has been set")] - public virtual string Value { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCancelledIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCancelledIntegrationEvent.cs deleted file mode 100644 index e421abfb0..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCancelledIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance has been cancelled - /// - [DataContract] - public partial class V1WorkflowInstanceCancelledIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCancellingIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCancellingIntegrationEvent.cs deleted file mode 100644 index d5db1d6cd..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCancellingIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance is being cancelled - /// - [DataContract] - public partial class V1WorkflowInstanceCancellingIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCompletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCompletedIntegrationEvent.cs deleted file mode 100644 index 321bdd1c6..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCompletedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance completes - /// - [DataContract] - public partial class V1WorkflowInstanceCompletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1WorkflowInstance's output - /// - [DataMember(Name = "Output", Order = 3)] - [Description("The V1WorkflowInstance's output")] - public virtual Dynamic Output { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCreatedIntegrationEvent.cs deleted file mode 100644 index 53e0ae208..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceCreatedIntegrationEvent.cs +++ /dev/null @@ -1,92 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1WorkflowInstance has been created - /// - [DataContract] - public partial class V1WorkflowInstanceCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the instanciated V1Workflow - /// - [DataMember(Name = "WorkflowId", Order = 3)] - [Description("The id of the instanciated V1Workflow")] - public virtual string WorkflowId { get; set; } - - /// - /// The key of the newly created V1WorkflowInstance - /// - [DataMember(Name = "Key", Order = 4)] - [Description("The key of the newly created V1WorkflowInstance")] - public virtual string Key { get; set; } - - /// - /// The type of the V1WorkflowInstance's activation - /// - [DataMember(Name = "ActivationType", Order = 5)] - [Description("The type of the V1WorkflowInstance's activation")] - public virtual V1WorkflowInstanceActivationType ActivationType { get; set; } - - /// - /// The newly created V1WorkflowInstance's input data - /// - [DataMember(Name = "Input", Order = 6)] - [Description("The newly created V1WorkflowInstance's input data")] - public virtual Dynamic Input { get; set; } - - /// - /// The newly created V1WorkflowInstance's V1CorrelationContext - /// - [DataMember(Name = "CorrelationContext", Order = 7)] - [Description("The newly created V1WorkflowInstance's V1CorrelationContext")] - public virtual V1CorrelationContext CorrelationContext { get; set; } - - /// - /// The id of the newly created V1WorkflowInstance's parent, if any - /// - [DataMember(Name = "ParentId", Order = 8)] - [Description("The id of the newly created V1WorkflowInstance's parent, if any")] - public virtual string ParentId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceDeletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceDeletedIntegrationEvent.cs deleted file mode 100644 index 9ca25c624..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceDeletedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowInstance has been deleted - /// - [DataContract] - public partial class V1WorkflowInstanceDeletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceExecutedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceExecutedIntegrationEvent.cs deleted file mode 100644 index 662768a8b..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceExecutedIntegrationEvent.cs +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowInstance has been executed - /// - [DataContract] - public partial class V1WorkflowInstanceExecutedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The V1WorkflowInstanceStatus of the V1WorkflowInstance when it finished executing - /// - [DataMember(Name = "Status", Order = 3)] - [Description("The V1WorkflowInstanceStatus of the V1WorkflowInstance when it finished executing")] - public virtual V1WorkflowInstanceStatus Status { get; set; } - - /// - /// The V1WorkflowInstance's error, if any - /// - [DataMember(Name = "Error", Order = 4)] - [Description("The V1WorkflowInstance's error, if any")] - public virtual Error Error { get; set; } - - /// - /// The V1WorkflowInstance's output, if any - /// - [DataMember(Name = "Output", Order = 5)] - [Description("The V1WorkflowInstance's output, if any")] - public virtual Dynamic Output { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceFaultedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceFaultedIntegrationEvent.cs deleted file mode 100644 index 6f741b0a5..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceFaultedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance has faulted - /// - [DataContract] - public partial class V1WorkflowInstanceFaultedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The Error that caused the V1WorkflowInstance to fault - /// - [DataMember(Name = "Error", Order = 3)] - [Description("The Error that caused the V1WorkflowInstance to fault")] - public virtual Error Error { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceResumedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceResumedIntegrationEvent.cs deleted file mode 100644 index e0536036e..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceResumedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance has been resumed - /// - [DataContract] - public partial class V1WorkflowInstanceResumedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceResumingIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceResumingIntegrationEvent.cs deleted file mode 100644 index 48d823af8..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceResumingIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance is resuming - /// - [DataContract] - public partial class V1WorkflowInstanceResumingIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// A string used to uniquely identify the resuming V1WorkflowInstance's process - /// - [DataMember(Name = "ProcessId", Order = 3)] - [Description("A string used to uniquely identify the resuming V1WorkflowInstance's process")] - public virtual string ProcessId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceScheduledIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceScheduledIntegrationEvent.cs deleted file mode 100644 index c0039456e..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceScheduledIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance has been scheduled - /// - [DataContract] - public partial class V1WorkflowInstanceScheduledIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSchedulingIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSchedulingIntegrationEvent.cs deleted file mode 100644 index f2941bfdf..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSchedulingIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance is being scheduled - /// - [DataContract] - public partial class V1WorkflowInstanceSchedulingIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceStartedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceStartedIntegrationEvent.cs deleted file mode 100644 index 46b550eb7..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceStartedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance has started - /// - [DataContract] - public partial class V1WorkflowInstanceStartedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceStartingIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceStartingIntegrationEvent.cs deleted file mode 100644 index 8ec8d1bb6..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceStartingIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance is starting - /// - [DataContract] - public partial class V1WorkflowInstanceStartingIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The string used to uniquely identify the V1WorkflowInstance's process - /// - [DataMember(Name = "ProcessId", Order = 3)] - [Description("The string used to uniquely identify the V1WorkflowInstance's process")] - public virtual string ProcessId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSuspendedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSuspendedIntegrationEvent.cs deleted file mode 100644 index 949647bee..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSuspendedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance has been suspended - /// - [DataContract] - public partial class V1WorkflowInstanceSuspendedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSuspendingIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSuspendingIntegrationEvent.cs deleted file mode 100644 index 5108f0c16..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowInstances/v1/Generated/V1WorkflowInstanceSuspendingIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowInstances -{ - - ///

- /// Represents the IDomainEvent fired whenever the execution of a V1WorkflowInstance is being suspended - /// - [DataContract] - public partial class V1WorkflowInstanceSuspendingIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessExitedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessExitedIntegrationEvent.cs deleted file mode 100644 index ea45da06a..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessExitedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowProcesses -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowProcess has exited - /// - [DataContract] - public partial class V1WorkflowProcessExitedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The id of the V1WorkflowProcess's exit code - /// - [DataMember(Name = "ExitCode", Order = 3)] - [Description("The id of the V1WorkflowProcess's exit code")] - public virtual long ExitCode { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessLogOutputIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessLogOutputIntegrationEvent.cs deleted file mode 100644 index b207d5e5a..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessLogOutputIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowProcesses -{ - - ///

- /// Represents the IDomainEvent fired whenever a new log has been outputed to a V1WorkflowProcess - /// - [DataContract] - public partial class V1WorkflowProcessLogOutputIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The log outputed by the V1WorkflowRuntimeSession - /// - [DataMember(Name = "Log", Order = 3)] - [Description("The log outputed by the V1WorkflowRuntimeSession")] - public virtual string Log { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessStartedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessStartedIntegrationEvent.cs deleted file mode 100644 index 851a18707..000000000 --- a/src/core/Synapse.Integration/Events/WorkflowProcesses/v1/Generated/V1WorkflowProcessStartedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.WorkflowProcesses -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1WorkflowProcess has started - /// - [DataContract] - public partial class V1WorkflowProcessStartedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowCreatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowCreatedIntegrationEvent.cs deleted file mode 100644 index 0b64449f3..000000000 --- a/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowCreatedIntegrationEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Workflows -{ - - ///

- /// Represents the IDomainEvent fired whenever a new V1Workflow has been created - /// - [DataContract] - public partial class V1WorkflowCreatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - /// - /// The newly created V1Workflow's WorkflowDefinition - /// - [DataMember(Name = "Definition", Order = 3)] - [Description("The newly created V1Workflow's WorkflowDefinition")] - public virtual WorkflowDefinition Definition { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowDeletedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowDeletedIntegrationEvent.cs deleted file mode 100644 index 9db092b2a..000000000 --- a/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowDeletedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Workflows -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Workflow has been deleted - /// - [DataContract] - public partial class V1WorkflowDeletedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowInstanciatedIntegrationEvent.cs b/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowInstanciatedIntegrationEvent.cs deleted file mode 100644 index 92ab20738..000000000 --- a/src/core/Synapse.Integration/Events/Workflows/v1/Generated/V1WorkflowInstanciatedIntegrationEvent.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Events.Workflows -{ - - ///

- /// Represents the IDomainEvent fired whenever a V1Workflow has been instanciated - /// - [DataContract] - public partial class V1WorkflowInstanciatedIntegrationEvent - : V1IntegrationEvent - { - - /// - /// Gets the id of the aggregate that has produced the event - /// - [DataMember(Name = "AggregateId", Order = 1)] - [Description("Gets the id of the aggregate that has produced the event")] - public virtual string AggregateId { get; set; } - - /// - /// Gets the date and time at which the event has been produced - /// - [DataMember(Name = "CreatedAt", Order = 2)] - [Description("Gets the date and time at which the event has been produced")] - public virtual DateTime CreatedAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Extensions/CloudEventExtensions.cs b/src/core/Synapse.Integration/Extensions/CloudEventExtensions.cs deleted file mode 100644 index 9a1df7342..000000000 --- a/src/core/Synapse.Integration/Extensions/CloudEventExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using CloudNative.CloudEvents; - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class CloudEventExtensions - { - - /// - /// Attempts to get the specified attribute - /// - /// The to check - /// The name of the attribute to get - /// The value of the attribute, if any - /// A boolean indicating whether or not the specified attribute is defined in the specified - public static bool TryGetAttribute(this CloudEvent e, string name, out string value) - { - value = null!; - if(string.IsNullOrWhiteSpace(name)) - throw new ArgumentNullException(nameof(name)); - var attribute = e.GetAttribute(name); - if (attribute == null) - return false; - value = attribute.Format(e[attribute]); - return true; - } - - } - -} diff --git a/src/core/Synapse.Integration/Extensions/DateTimeExtensions.cs b/src/core/Synapse.Integration/Extensions/DateTimeExtensions.cs deleted file mode 100644 index 524b1643a..000000000 --- a/src/core/Synapse.Integration/Extensions/DateTimeExtensions.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class DateTimeExtensions - { - - /// - /// Gets the first day of the week the specified date belongs to - /// - /// The date that belongs to the week to get the first day of - /// The first day of the week. Defaults to - /// The first day of the week the specified date belongs to - public static DateTime GetFirstDayOfWeek(this DateTime date, DayOfWeek startOfWeek = DayOfWeek.Monday) - { - int diff = (7 + (date.DayOfWeek - startOfWeek)) % 7; - return date.AddDays(-1 * diff).Date; - } - - /// - /// Gets the last day of the week the specified date belongs to - /// - /// The date that belongs to the week to get the last day of - /// The last day of the week. Defaults to - /// The last day of the week the specified date belongs to - public static DateTime GetLastDayOfWeek(this DateTime date, DayOfWeek startOfWeek = DayOfWeek.Monday) - { - int diff = (7 + (date.DayOfWeek - startOfWeek)) % 7; - return date.AddDays(6 - diff).Date; - } - - } - -} diff --git a/src/core/Synapse.Integration/Extensions/GuidExtensions.cs b/src/core/Synapse.Integration/Extensions/GuidExtensions.cs deleted file mode 100644 index 76c71803d..000000000 --- a/src/core/Synapse.Integration/Extensions/GuidExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Text.RegularExpressions; - -namespace Synapse -{ - ///

- /// Defines extensions for s - /// - public static class GuidExtensions - { - - /// - /// Converts the to its base 64 representation - /// - /// The to get the base 64 representation of - /// The 's base 64 representation - public static string ToBase64(this Guid guid) - { - return Regex.Replace(Convert.ToBase64String(guid.ToByteArray()), "[/+=]", ""); ; - } - - } - -} diff --git a/src/core/Synapse.Integration/Extensions/ScheduleDefinitionExtensions.cs b/src/core/Synapse.Integration/Extensions/ScheduleDefinitionExtensions.cs deleted file mode 100644 index 2218d02e3..000000000 --- a/src/core/Synapse.Integration/Extensions/ScheduleDefinitionExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Cronos; - -namespace Synapse -{ - - ///

- /// Defines extensuons for s - /// - public static class ScheduleDefinitionExtensions - { - - /// - /// Gets the next occured of the specified - /// - /// The to get the next occurence for - /// The 's next occurence - /// The date and time at which the will next occur, if any - public static DateTimeOffset? GetNextOccurence(this ScheduleDefinition scheduleDefinition, DateTimeOffset? lastOccurence = null) - { - TimeZoneInfo timeZone = null; - if (scheduleDefinition.Timezone != null && TimeZoneInfo.TryConvertIanaIdToWindowsId(scheduleDefinition.Timezone, out var timeZoneId)) timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); - if (timeZone == null) timeZone = TimeZoneInfo.Local; - var validUntilDateTime = scheduleDefinition.Cron?.ValidUntil; - var validUntilDateTimeOffset = (DateTimeOffset?)null; - if (validUntilDateTime.HasValue) validUntilDateTimeOffset = new(validUntilDateTime.Value, timeZone.GetUtcOffset(validUntilDateTime.Value)); - if (lastOccurence == null) lastOccurence = DateTimeOffset.Now; - if (scheduleDefinition.Cron != null) - { - if(validUntilDateTimeOffset.HasValue && DateTimeOffset.Now >= validUntilDateTimeOffset) return null; - else return CronExpression.Parse(scheduleDefinition.Cron.Expression).GetNextOccurrence(lastOccurence.Value, timeZone); - } - else if (scheduleDefinition.Interval != null) return lastOccurence.Value.Add(scheduleDefinition.Interval.Value); - else return null; - } - - } - -} diff --git a/src/core/Synapse.Integration/Extensions/StringExtensions.cs b/src/core/Synapse.Integration/Extensions/StringExtensions.cs deleted file mode 100644 index 4ca44bc36..000000000 --- a/src/core/Synapse.Integration/Extensions/StringExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Defines extensions for strings - /// - public static class StringExtensions - { - - /// - /// Determines whether or not the string is a workflow expression - /// - /// The string to test - /// A boolean indicating whether or not the string is a workflow expression - public static bool IsRuntimeExpression(this string text) - { - if (string.IsNullOrWhiteSpace(text)) - return false; - return text.StartsWith("${") && text.EndsWith("}"); - } - - } - -} diff --git a/src/core/Synapse.Integration/Extensions/WorkflowDefinitionExtensions.cs b/src/core/Synapse.Integration/Extensions/WorkflowDefinitionExtensions.cs deleted file mode 100644 index e78ead9ec..000000000 --- a/src/core/Synapse.Integration/Extensions/WorkflowDefinitionExtensions.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Defines extensions for s - /// - public static class WorkflowDefinitionExtensions - { - - /// - /// Gets the 's unique identifier, which is a concatenation of the and properties - /// - /// The to get the unique identifier of - /// The 's unique identifier - public static string GetUniqueIdentifier(this WorkflowDefinition workflowDefinition) - { - if (string.IsNullOrWhiteSpace(workflowDefinition.Id)) - throw new InvalidDataException($"The specified workflow definition must define the 'id' property"); - if (string.IsNullOrWhiteSpace(workflowDefinition.Version)) - throw new InvalidDataException($"The specified workflow definition must define the 'version' property"); - return $"{workflowDefinition.Id}:{workflowDefinition.Version}"; - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/Entity.cs b/src/core/Synapse.Integration/Models/Entity.cs deleted file mode 100644 index e02d110fb..000000000 --- a/src/core/Synapse.Integration/Models/Entity.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ProtoBuf; - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents the base class for all the application's entity Data Transfer Objects - /// - [DataContract] - [ProtoContract] - [ProtoInclude(10, typeof(V1Workflow))] - [ProtoInclude(20, typeof(V1WorkflowInstance))] - [ProtoInclude(30, typeof(V1WorkflowActivity))] - public abstract class Entity - : DataTransferObject, IIdentifiable - { - - /// - [DataMember(Order = 1), ProtoMember(1)] - public virtual string Id { get; set; } - - /// - [DataMember(Order = 2), ProtoMember(2)] - public virtual DateTime CreatedAt { get; set; } - - /// - [DataMember(Order = 3), ProtoMember(3)] - public virtual DateTime LastModified { get; set; } - - object IIdentifiable.Id => this.Id; - - } - -} diff --git a/src/core/Synapse.Integration/Models/NameValueCollection.cs b/src/core/Synapse.Integration/Models/NameValueCollection.cs deleted file mode 100644 index 110a8c6ea..000000000 --- a/src/core/Synapse.Integration/Models/NameValueCollection.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System.Collections; -using System.Collections.ObjectModel; - -namespace Synapse.Integration.Models -{ - - /// - /// Represents an OData-compatible wrapper - /// - /// The type of values held by the - /// Taken from Casimodo72's answer on Github - [DataContract] - public class NameValueCollection - : IDictionary, ICollection - { - - /// - /// Initializes a new - /// - public NameValueCollection() - { - - } - - /// - /// Initializes a new - /// - /// An array containing the items the is made out of - /// This constructor is a quick, dirty turn around a 'Simple.OData.Client' issue - public NameValueCollection(IEnumerable items) - { - //todo: remove once Simple.OData.Client issue has been fixed - this.Items = items.Select(str => str[1..^1].Split(',', StringSplitOptions.RemoveEmptyEntries)) - .ToDictionary(cpn => cpn.First(), cpn => (object)string.Join(',', cpn.Skip(1))); - } - - /// - /// Initializes a new - /// - /// An containing the items the is made out of - public NameValueCollection(IEnumerable> items) - { - this.Items = items.ToDictionary(kvp => kvp.Key, kvp => kvp.Value as object); - } - - /// - /// Initializes a new - /// - /// An containing the items the is made out of - public NameValueCollection(IDictionary items) - { - this.Items = items.ToDictionary(kvp => kvp.Key, kvp => kvp.Value as object); - } - - IDictionary _Items; - /// - /// Gets the inner - /// - /// Must be Public to be picked up by OData - [DataMember] - public IDictionary Items - { - get - { - return _Items ?? (_Items = new Dictionary()); - } - set - { - this._Items = value; - } - } - - /// - public virtual TValue this[string key] - { - get { return (TValue)Items[key]; } - set { Items[key] = value; } - } - - /// - public virtual int Count - { - get { return Items.Count; } - } - - /// - public virtual bool IsReadOnly - { - get { return Items.IsReadOnly; } - } - - /// - public virtual ICollection Keys - { - get { return Items.Keys; } - } - - /// - /// NOTE: This method will create a new ReadOnlyCollection based on the - /// values of the underlying dictionary. - /// - /// - public virtual ICollection Values => new ReadOnlyCollection(Items.Values.Cast().ToList()); - - bool ICollection.IsSynchronized => ((ICollection)this.Items).IsSynchronized; - - object ICollection.SyncRoot => ((ICollection)this.Items).SyncRoot; - - /// - public virtual void Add(KeyValuePair item) - { - Items.Add(Convert(item)); - } - - /// - public virtual void Add(string key, TValue value) - { - Items.Add(key, value); - } - - /// - public virtual void Clear() - { - Items.Clear(); - } - - /// - public virtual bool Contains(KeyValuePair item) - { - return Items.Contains(Convert(item)); - } - - /// - public virtual bool ContainsKey(string key) - { - return Items.ContainsKey(key); - } - - /// - public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) - { - if (array == null) throw new ArgumentNullException(nameof(array)); - if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - if (Items.Count > array.Length - arrayIndex) - throw new ArgumentException("The number of elements in the source dictionary " + - "is greater than the available space from arrayIndex to the end of the destination array.", - nameof(arrayIndex)); - var i = 0; - foreach (var item in Items) - { - array[i] = Convert(item); - i++; - } - } - - /// - public virtual bool Remove(KeyValuePair item) - { - return Items.Remove(Convert(item)); - } - - /// - public virtual bool Remove(string key) - { - return Items.Remove(key); - } - - /// - public virtual bool TryGetValue(string key, out TValue value) - { - object obj; - if (Items.TryGetValue(key, out obj)) - { - value = (TValue)obj; - return true; - } - - value = default(TValue); - return false; - } - - /// - public virtual IEnumerator> GetEnumerator() - { - foreach (var item in Items) - yield return Convert(item); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - static KeyValuePair Convert(KeyValuePair item) - { - return new KeyValuePair(item.Key, item.Value); - } - - static KeyValuePair Convert(KeyValuePair item) - { - return new KeyValuePair(item.Key, (TValue)item.Value); - } - - /// - public void CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - /// - /// Converts the specified int a new - /// - /// The to convert - public static implicit operator NameValueCollection(Dictionary dictionary) - { - return new NameValueCollection(dictionary); - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Error.cs b/src/core/Synapse.Integration/Models/v1/Error.cs deleted file mode 100644 index 5a476d119..000000000 --- a/src/core/Synapse.Integration/Models/v1/Error.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Describes an error - /// - [DataContract] - public class Error - { - - /// - /// Gets the error code - /// - [DataMember(Order = 1)] - public virtual string Code { get; set; } - - /// - /// Gets the error message - /// - [DataMember(Order = 2)] - public virtual string Message { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1AuthenticationDefinitionCollection.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1AuthenticationDefinitionCollection.cs deleted file mode 100644 index c00508c7f..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1AuthenticationDefinitionCollection.cs +++ /dev/null @@ -1,65 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a managed AuthenticationDefinition collection - /// - [DataContract] - [Queryable] - public partial class V1AuthenticationDefinitionCollection - : Entity - { - - /// - /// The V1AuthenticationDefinitionCollection's name - /// - [DataMember(Name = "name", Order = 1)] - [Description("The V1AuthenticationDefinitionCollection's name")] - public virtual string Name { get; set; } - - /// - /// The V1AuthenticationDefinitionCollection's version - /// - [DataMember(Name = "version", Order = 2)] - [Description("The V1AuthenticationDefinitionCollection's version")] - public virtual string Version { get; set; } - - /// - /// The V1AuthenticationDefinitionCollection's description - /// - [DataMember(Name = "description", Order = 3)] - [Description("The V1AuthenticationDefinitionCollection's description")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the AuthenticationDefinitions the V1AuthenticationDefinitionCollection is made out of - /// - [DataMember(Name = "authentications", Order = 4)] - [Description("An IReadOnlyCollection`1 containing the AuthenticationDefinitions the V1AuthenticationDefinitionCollection is made out of")] - public virtual ICollection Authentications { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1Correlation.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1Correlation.cs deleted file mode 100644 index 18fe9f4d8..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1Correlation.cs +++ /dev/null @@ -1,79 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents an event correlation - /// - [DataContract] - [Queryable] - public partial class V1Correlation - : Entity - { - - /// - /// The V1Correlation's activation type - /// - [DataMember(Name = "activationType", Order = 1)] - [Description("The V1Correlation's activation type")] - public virtual V1CorrelationActivationType ActivationType { get; set; } - - /// - /// The V1Correlation's lifetime - /// - [DataMember(Name = "lifetime", Order = 2)] - [Description("The V1Correlation's lifetime")] - public virtual V1CorrelationLifetime Lifetime { get; set; } - - /// - /// A value determining the type of the V1Correlation's V1CorrelationCondition evaluation - /// - [DataMember(Name = "conditionType", Order = 3)] - [Description("A value determining the type of the V1Correlation's V1CorrelationCondition evaluation")] - public virtual V1CorrelationConditionType ConditionType { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the V1Correlation's conditions - /// - [DataMember(Name = "conditions", Order = 4)] - [Description("An IReadOnlyCollection`1 containing the V1Correlation's conditions")] - public virtual ICollection Conditions { get; set; } - - /// - /// The outcome of the V1Correlation - /// - [DataMember(Name = "outcome", Order = 5)] - [Description("The outcome of the V1Correlation")] - public virtual V1CorrelationOutcome Outcome { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the V1CorrelationContexts affected by the V1Correlation - /// - [DataMember(Name = "contexts", Order = 6)] - [Description("An IReadOnlyCollection`1 containing the V1CorrelationContexts affected by the V1Correlation")] - public virtual ICollection Contexts { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationCondition.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationCondition.cs deleted file mode 100644 index f13bd62f2..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationCondition.cs +++ /dev/null @@ -1,42 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a condition of an event correlation - /// - [DataContract] - public partial class V1CorrelationCondition - { - - /// - /// An IReadOnlyCollection`1 containing the V1EventFilter used to configure the filtering of events that can fire the V1Correlation - /// - [DataMember(Name = "filters", Order = 1)] - [Description("An IReadOnlyCollection`1 containing the V1EventFilter used to configure the filtering of events that can fire the V1Correlation")] - public virtual ICollection Filters { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationContext.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationContext.cs deleted file mode 100644 index 5351227ef..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationContext.cs +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents the context of an event correlation - /// - [DataContract] - public partial class V1CorrelationContext - : Entity - { - - /// - /// An IReadOnlyDictionary`2 containing the correlations' value by key mappings - /// - [DataMember(Name = "mappings", Order = 1)] - [Description("An IReadOnlyDictionary`2 containing the correlations' value by key mappings")] - public virtual NameValueCollection Mappings { get; set; } - - /// - /// An IReadOnlyCollection`1 containing all correlated V1Events pending processing - /// - [DataMember(Name = "pendingEvents", Order = 2)] - [Description("An IReadOnlyCollection`1 containing all correlated V1Events pending processing")] - public virtual ICollection PendingEvents { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationOutcome.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationOutcome.cs deleted file mode 100644 index eca8cd4b7..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1CorrelationOutcome.cs +++ /dev/null @@ -1,49 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents the outcome of an event correlation - /// - [DataContract] - public partial class V1CorrelationOutcome - { - - /// - /// The V1CorrelationOutcomeType's type - /// - [DataMember(Name = "type", Order = 1)] - [Description("The V1CorrelationOutcomeType's type")] - public virtual V1CorrelationOutcomeType Type { get; set; } - - /// - /// The identifier of the V1CorrelationOutcome's target (a V1Workflow or a V1WorkflowInstance) - /// - [DataMember(Name = "target", Order = 2)] - [Description("The identifier of the V1CorrelationOutcome's target (a V1Workflow or a V1WorkflowInstance)")] - public virtual string Target { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1Event.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1Event.cs deleted file mode 100644 index 6fb2e2ab6..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1Event.cs +++ /dev/null @@ -1,105 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents an event - /// - [DataContract] - public partial class V1Event - { - - /// - /// The event's id - /// - [DataMember(Name = "id", Order = 1)] - [Description("The event's id")] - public virtual string Id { get; set; } - - /// - /// The event's sourceUri - /// - [DataMember(Name = "source", Order = 2)] - [Description("The event's sourceUri")] - public virtual Uri Source { get; set; } - - /// - /// The event's spec version - /// - [DataMember(Name = "specVersion", Order = 3)] - [Description("The event's spec version")] - public virtual string SpecVersion { get; set; } - - /// - /// The event's type - /// - [DataMember(Name = "type", Order = 4)] - [Description("The event's type")] - public virtual string Type { get; set; } - - /// - /// The event's data content type - /// - [DataMember(Name = "dataContentType", Order = 5)] - [Description("The event's data content type")] - public virtual string DataContentType { get; set; } - - /// - /// The event's data schema Uri, if any - /// - [DataMember(Name = "dataSchema", Order = 6)] - [Description("The event's data schema Uri, if any")] - public virtual Uri DataSchema { get; set; } - - /// - /// The event's subject - /// - [DataMember(Name = "subject", Order = 7)] - [Description("The event's subject")] - public virtual string Subject { get; set; } - - /// - /// The event's type - /// - [DataMember(Name = "time", Order = 8)] - [Description("The event's type")] - public virtual DateTime? Time { get; set; } - - /// - /// The event's data - /// - [DataMember(Name = "data", Order = 9)] - [Description("The event's data")] - public virtual Dynamic Data { get; set; } - - /// - /// An IDictionary`2 that contains the event's extension attributes key/value mappings - /// - [DataMember(Name = "extensionAttributes", Order = 10)] - [Description("An IDictionary`2 that contains the event's extension attributes key/value mappings")] - public virtual NameValueCollection ExtensionAttributes { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1EventDefinitionCollection.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1EventDefinitionCollection.cs deleted file mode 100644 index 06b9b2a8b..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1EventDefinitionCollection.cs +++ /dev/null @@ -1,65 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a managed EventDefinition collection - /// - [DataContract] - [Queryable] - public partial class V1EventDefinitionCollection - : Entity - { - - /// - /// The V1EventDefinitionCollection's name - /// - [DataMember(Name = "name", Order = 1)] - [Description("The V1EventDefinitionCollection's name")] - public virtual string Name { get; set; } - - /// - /// The V1EventDefinitionCollection's version - /// - [DataMember(Name = "version", Order = 2)] - [Description("The V1EventDefinitionCollection's version")] - public virtual string Version { get; set; } - - /// - /// The V1EventDefinitionCollection's description - /// - [DataMember(Name = "description", Order = 3)] - [Description("The V1EventDefinitionCollection's description")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection is made out of - /// - [DataMember(Name = "events", Order = 4)] - [Description("An IReadOnlyCollection`1 containing the EventDefinitions the V1EventDefinitionCollection is made out of")] - public virtual ICollection Events { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1EventFilter.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1EventFilter.cs deleted file mode 100644 index 2d8960ddc..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1EventFilter.cs +++ /dev/null @@ -1,49 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents an object used to filter events - /// - [DataContract] - public partial class V1EventFilter - { - - /// - /// An IDictionary`2 containing the attributes to filter V1Events by - /// - [DataMember(Name = "attributes", Order = 1)] - [Description("An IDictionary`2 containing the attributes to filter V1Events by")] - public virtual NameValueCollection Attributes { get; set; } - - /// - /// An IReadOnlyDictionary`2 containing the attributes key/value to use when correlating an incoming event to the V1Correlation - /// - [DataMember(Name = "correlationMappings", Order = 2)] - [Description("An IReadOnlyDictionary`2 containing the attributes key/value to use when correlating an incoming event to the V1Correlation")] - public virtual NameValueCollection CorrelationMappings { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1FunctionDefinitionCollection.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1FunctionDefinitionCollection.cs deleted file mode 100644 index 254827462..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1FunctionDefinitionCollection.cs +++ /dev/null @@ -1,65 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a managed FunctionDefinition collection - /// - [DataContract] - [Queryable] - public partial class V1FunctionDefinitionCollection - : Entity - { - - /// - /// The V1FunctionDefinitionCollection's name - /// - [DataMember(Name = "name", Order = 1)] - [Description("The V1FunctionDefinitionCollection's name")] - public virtual string Name { get; set; } - - /// - /// The V1FunctionDefinitionCollection's version - /// - [DataMember(Name = "version", Order = 2)] - [Description("The V1FunctionDefinitionCollection's version")] - public virtual string Version { get; set; } - - /// - /// The V1FunctionDefinitionCollection's description - /// - [DataMember(Name = "description", Order = 3)] - [Description("The V1FunctionDefinitionCollection's description")] - public virtual string Description { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the FunctionDefinitions the V1FunctionDefinitionCollection is made out of - /// - [DataMember(Name = "functions", Order = 4)] - [Description("An IReadOnlyCollection`1 containing the FunctionDefinitions the V1FunctionDefinitionCollection is made out of")] - public virtual ICollection Functions { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1Schedule.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1Schedule.cs deleted file mode 100644 index 2e2058161..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1Schedule.cs +++ /dev/null @@ -1,107 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a V1Workflow schedule - /// - [DataContract] - [Queryable] - public partial class V1Schedule - : Entity - { - - /// - /// The V1Schedule's activation type - /// - [DataMember(Name = "activationType", Order = 1)] - [Description("The V1Schedule's activation type")] - public virtual V1ScheduleActivationType ActivationType { get; set; } - - /// - /// The V1Schedule's status - /// - [DataMember(Name = "status", Order = 2)] - [Description("The V1Schedule's status")] - public virtual V1ScheduleStatus Status { get; set; } - - /// - /// The V1Schedule's definition - /// - [DataMember(Name = "definition", Order = 3)] - [Description("The V1Schedule's definition")] - public virtual ScheduleDefinition Definition { get; set; } - - /// - /// The id of the scheduled V1Workflow - /// - [DataMember(Name = "workflowId", Order = 4)] - [Description("The id of the scheduled V1Workflow")] - public virtual string WorkflowId { get; set; } - - /// - /// The date and time the V1Schedule has last been suspended at - /// - [DataMember(Name = "suspendedAt", Order = 5)] - [Description("The date and time the V1Schedule has last been suspended at")] - public virtual DateTime? SuspendedAt { get; set; } - - /// - /// The date and time the V1Schedule has been retired at - /// - [DataMember(Name = "retiredAt", Order = 6)] - [Description("The date and time the V1Schedule has been retired at")] - public virtual DateTime? RetiredAt { get; set; } - - /// - /// The date and time the V1Schedule has been made obsolete at - /// - [DataMember(Name = "obsoletedAt", Order = 7)] - [Description("The date and time the V1Schedule has been made obsolete at")] - public virtual DateTime? ObsoletedAt { get; set; } - - /// - /// The date and time at which the V1Schedule has last occured - /// - [DataMember(Name = "lastOccuredAt", Order = 8)] - [Description("The date and time at which the V1Schedule has last occured")] - public virtual DateTime? LastOccuredAt { get; set; } - - /// - /// The date and time at which the scheduled V1Workflow has last completed - /// - [DataMember(Name = "lastCompletedAt", Order = 9)] - [Description("The date and time at which the scheduled V1Workflow has last completed")] - public virtual DateTime? LastCompletedAt { get; set; } - - /// - /// The date and time at which the V1Schedule will next occur - /// - [DataMember(Name = "nextOccurenceAt", Order = 10)] - [Description("The date and time at which the V1Schedule will next occur")] - public virtual DateTime? NextOccurenceAt { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1Workflow.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1Workflow.cs deleted file mode 100644 index d44b1592b..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1Workflow.cs +++ /dev/null @@ -1,51 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a workflow - /// - [DataContract] - [Queryable] - public partial class V1Workflow - : Entity - { - - /// - /// The V1Workflow's definition - /// - [DataMember(Name = "definition", Order = 1)] - [Description("The V1Workflow's definition")] - public virtual WorkflowDefinition Definition { get; set; } - - /// - /// The date and time at which the V1Workflow was last instanciated - /// - [DataMember(Name = "lastInstanciated", Order = 2)] - [Description("The date and time at which the V1Workflow was last instanciated")] - public virtual DateTime? LastInstanciated { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowActivity.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowActivity.cs deleted file mode 100644 index 7272b6ee2..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowActivity.cs +++ /dev/null @@ -1,107 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Describes a workflow activity - /// - [DataContract] - [Queryable] - public partial class V1WorkflowActivity - : Entity - { - - /// - /// The id of the V1WorkflowInstance the V1WorkflowActivity belongs to - /// - [DataMember(Name = "workflowInstanceId", Order = 1)] - [Description("The id of the V1WorkflowInstance the V1WorkflowActivity belongs to")] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// The V1WorkflowActivity's type - /// - [DataMember(Name = "type", Order = 2)] - [Description("The V1WorkflowActivity's type")] - public virtual V1WorkflowActivityType Type { get; set; } - - /// - /// The V1WorkflowActivity's status - /// - [DataMember(Name = "status", Order = 3)] - [Description("The V1WorkflowActivity's status")] - public virtual V1WorkflowActivityStatus Status { get; set; } - - /// - /// The date and time at which the V1WorkflowActivity has started - /// - [DataMember(Name = "startedAt", Order = 4)] - [Description("The date and time at which the V1WorkflowActivity has started")] - public virtual DateTime? StartedAt { get; set; } - - /// - /// The date and time at which the V1WorkflowActivity has been executed Value is set when the V1WorkflowActivity has been cancelled, faults or completes - /// - [DataMember(Name = "executedAt", Order = 5)] - [Description("The date and time at which the V1WorkflowActivity has been executed Value is set when the V1WorkflowActivity has been cancelled, faults or completes")] - public virtual DateTime? ExecutedAt { get; set; } - - /// - /// The Error that caused the V1WorkflowActivity to end prematurily - /// - [DataMember(Name = "error", Order = 6)] - [Description("The Error that caused the V1WorkflowActivity to end prematurily")] - public virtual Error Error { get; set; } - - /// - /// The V1WorkflowActivity's input - /// - [DataMember(Name = "input", Order = 7)] - [Description("The V1WorkflowActivity's input")] - public virtual Dynamic Input { get; set; } - - /// - /// The V1WorkflowInstance's output - /// - [DataMember(Name = "output", Order = 8)] - [Description("The V1WorkflowInstance's output")] - public virtual Dynamic Output { get; set; } - - /// - /// The V1WorkflowActivity's metadata - /// - [DataMember(Name = "metadata", Order = 9)] - [Description("The V1WorkflowActivity's metadata")] - public virtual NameValueCollection Metadata { get; set; } - - /// - /// The id of the V1WorkflowActivity's parent, if any - /// - [DataMember(Name = "parentId", Order = 10)] - [Description("The id of the V1WorkflowActivity's parent, if any")] - public virtual string ParentId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowInstance.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowInstance.cs deleted file mode 100644 index 8a682a5e4..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowInstance.cs +++ /dev/null @@ -1,142 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represent an instance of a V1Workflow - /// - [DataContract] - [Queryable] - public partial class V1WorkflowInstance - : Entity - { - - /// - /// The id of the instanciated V1Workflow. The workflow id is used as the first out of the two components of the V1WorkflowInstance's id - /// - [DataMember(Name = "workflowId", Order = 1)] - [Description("The id of the instanciated V1Workflow. The workflow id is used as the first out of the two components of the V1WorkflowInstance's id")] - public virtual string WorkflowId { get; set; } - - /// - /// The V1WorkflowInstance's key. The key is used as the second out of the two components of the V1WorkflowInstance's id - /// - [DataMember(Name = "key", Order = 2)] - [Description("The V1WorkflowInstance's key. The key is used as the second out of the two components of the V1WorkflowInstance's id")] - public virtual string Key { get; set; } - - /// - /// The V1WorkflowInstance's activation type - /// - [DataMember(Name = "activationType", Order = 3)] - [Description("The V1WorkflowInstance's activation type")] - public virtual V1WorkflowInstanceActivationType ActivationType { get; set; } - - /// - /// The id of the V1WorkflowInstance's parent, if any - /// - [DataMember(Name = "parentId", Order = 4)] - [Description("The id of the V1WorkflowInstance's parent, if any")] - public virtual string ParentId { get; set; } - - /// - /// The V1WorkflowInstance's input - /// - [DataMember(Name = "input", Order = 5)] - [Description("The V1WorkflowInstance's input")] - public virtual Dynamic Input { get; set; } - - /// - /// An IReadOnlyCollection`1 containing descriptors of the CloudEvents that have triggered the V1WorkflowInstance - /// - [DataMember(Name = "triggerEvents", Order = 6)] - [Description("An IReadOnlyCollection`1 containing descriptors of the CloudEvents that have triggered the V1WorkflowInstance")] - public virtual ICollection TriggerEvents { get; set; } - - /// - /// The V1WorkflowInstance's status - /// - [DataMember(Name = "status", Order = 7)] - [Description("The V1WorkflowInstance's status")] - public virtual V1WorkflowInstanceStatus Status { get; set; } - - /// - /// The date and time at which the V1WorkflowInstance has started - /// - [DataMember(Name = "startedAt", Order = 8)] - [Description("The date and time at which the V1WorkflowInstance has started")] - public virtual DateTime? StartedAt { get; set; } - - /// - /// The date and time at which the V1WorkflowInstance has been executed The value is set when the V1WorkflowInstance has been cancelled, faults or completes. - /// - [DataMember(Name = "executedAt", Order = 9)] - [Description("The date and time at which the V1WorkflowInstance has been executed The value is set when the V1WorkflowInstance has been cancelled, faults or completes.")] - public virtual DateTime? ExecutedAt { get; set; } - - /// - /// The V1WorkflowInstance's V1CorrelationContext - /// - [DataMember(Name = "correlationContext", Order = 10)] - [Description("The V1WorkflowInstance's V1CorrelationContext")] - public virtual V1CorrelationContext CorrelationContext { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the sessions the V1WorkflowInstance is made out of - /// - [DataMember(Name = "sessions", Order = 11)] - [Description("An IReadOnlyCollection`1 containing the sessions the V1WorkflowInstance is made out of")] - public virtual ICollection Sessions { get; set; } - - /// - /// The currently active V1WorkflowRuntimeSession, if any - /// - [DataMember(Name = "activeSession", Order = 12)] - [Description("The currently active V1WorkflowRuntimeSession, if any")] - public virtual V1WorkflowRuntimeSession ActiveSession { get; set; } - - /// - /// An IReadOnlyCollection`1 containing the activities the V1WorkflowInstance is made out of - /// - [DataMember(Name = "activities", Order = 13)] - [Description("An IReadOnlyCollection`1 containing the activities the V1WorkflowInstance is made out of")] - public virtual ICollection Activities { get; set; } - - /// - /// The Error that caused the V1WorkflowInstance to end prematurily - /// - [DataMember(Name = "error", Order = 14)] - [Description("The Error that caused the V1WorkflowInstance to end prematurily")] - public virtual Error Error { get; set; } - - /// - /// The V1WorkflowInstance's output - /// - [DataMember(Name = "output", Order = 15)] - [Description("The V1WorkflowInstance's output")] - public virtual Dynamic Output { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowProcess.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowProcess.cs deleted file mode 100644 index 1aabbb003..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowProcess.cs +++ /dev/null @@ -1,72 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a process - /// - [DataContract] - [Queryable] - public partial class V1WorkflowProcess - : Entity - { - - /// - /// The date and time at which the V1WorkflowProcess has exited - /// - [DataMember(Name = "exitedAt", Order = 1)] - [Description("The date and time at which the V1WorkflowProcess has exited")] - public virtual DateTime? ExitedAt { get; set; } - - /// - /// The logs associated to the V1WorkflowProcess - /// - [DataMember(Name = "logs", Order = 2)] - [Description("The logs associated to the V1WorkflowProcess")] - public virtual string Logs { get; set; } - - /// - /// The V1WorkflowProcess's exit code - /// - [DataMember(Name = "exitCode", Order = 3)] - [Description("The V1WorkflowProcess's exit code")] - public virtual long? ExitCode { get; set; } - - /// - /// A boolean indicating whether or not the V1WorkflowProcess is running - /// - [DataMember(Name = "isRunning", Order = 4)] - [Description("A boolean indicating whether or not the V1WorkflowProcess is running")] - public virtual bool IsRunning { get; set; } - - /// - /// The V1WorkflowRuntimeSession's duration - /// - [DataMember(Name = "duration", Order = 5)] - [Description("The V1WorkflowRuntimeSession's duration")] - public virtual TimeSpan? Duration { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowRuntimeSession.cs b/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowRuntimeSession.cs deleted file mode 100644 index af7aeb88a..000000000 --- a/src/core/Synapse.Integration/Models/v1/Generated/V1WorkflowRuntimeSession.cs +++ /dev/null @@ -1,63 +0,0 @@ - -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents a V1WorkflowInstance's runtime sessions - /// - [DataContract] - public partial class V1WorkflowRuntimeSession - { - - /// - /// The string used to uniquely identify the process the session is bound to - /// - [DataMember(Name = "processId", Order = 1)] - [Description("The string used to uniquely identify the process the session is bound to")] - public virtual string ProcessId { get; set; } - - /// - /// The date and time at which the V1WorkflowRuntimeSession has started - /// - [DataMember(Name = "startedAt", Order = 2)] - [Description("The date and time at which the V1WorkflowRuntimeSession has started")] - public virtual DateTime StartedAt { get; set; } - - /// - /// The date and time at which the V1WorkflowRuntimeSession has ended - /// - [DataMember(Name = "endedAt", Order = 3)] - [Description("The date and time at which the V1WorkflowRuntimeSession has ended")] - public virtual DateTime? EndedAt { get; set; } - - /// - /// The logs associated to the V1WorkflowRuntimeSession - /// - [DataMember(Name = "logs", Order = 4)] - [Description("The logs associated to the V1WorkflowRuntimeSession")] - public virtual string Logs { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1ApplicationInfo.cs b/src/core/Synapse.Integration/Models/v1/V1ApplicationInfo.cs deleted file mode 100644 index 7731d50f4..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1ApplicationInfo.cs +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - ///

- /// Describes the running Synapse instance - /// - [DataContract] - public class V1ApplicationInfo - { - - /// - /// Initializes a new - /// - protected V1ApplicationInfo() - { - - } - - /// - /// Initializes a new - /// - /// The name of the running Synapse application - /// The version of the running Synapse application - /// The description of the OS the Synapse application is running on - /// The description of the .NET Framework the Synapse application is running on - /// The version of the Serverless Workflow SDK used by the Synapse application - /// The name of the Synapse application's environment - /// The name of the Synapse workflow runtime - /// A list containing all supported runtime expression languages - /// An containing the Synapse application's environment variables - /// A collection containing the metadata of all installed plugins - public V1ApplicationInfo(string name, string version, string osDescription, string frameworkDescription, string serverlessWorkflowSdkVersion, string environmentName, - string workflowRuntime, IEnumerable supportedRuntimeExpressionLanguages, IDictionary environmentVariables, IEnumerable plugins) - { - this.Name = name; - this.Version = version; - this.OSDescription = osDescription; - this.FrameworkDescription = frameworkDescription; - this.ServerlessWorkflowSdkVersion = serverlessWorkflowSdkVersion; - this.EnvironmentName = environmentName; - this.WorkflowRuntimeName = workflowRuntime; - this.SupportedRuntimeExpressionLanguages = supportedRuntimeExpressionLanguages.ToList(); - this.EnvironmentVariables = environmentVariables; - this.Plugins = plugins.ToList(); - } - - /// - /// Gets the Synapse application's name - /// - [DataMember(Order = 1)] - public virtual string Name { get; set; } - - /// - /// Gets the version of Synapse - /// - [DataMember(Order = 2)] - public virtual string Version { get; set; } - - /// - /// Gets the description of the OS the Synapse application is running on - /// - [DataMember(Order = 3)] - public virtual string OSDescription { get; set; } - - /// - /// Gets the description of the .NET Framework the Synapse application is running on - /// - [DataMember(Order = 4)] - public virtual string FrameworkDescription { get; set; } - - /// - /// Gets the version of the Serverless Workflow SDK used by the Synapse application - /// - [DataMember(Order = 5)] - public virtual string ServerlessWorkflowSdkVersion { get; set; } - - /// - /// Gets the name of the Synapse application's environment - /// - [DataMember(Order = 6)] - public virtual string EnvironmentName { get; set; } - - /// - /// Gets the name of the Synapse workflow runtime - /// - [DataMember(Order = 7)] - public virtual string WorkflowRuntimeName { get; set; } - - /// - /// Gets a list containing all supported runtime expression languages - /// - [DataMember(Order = 8)] - public virtual List SupportedRuntimeExpressionLanguages { get; set; } - - /// - /// Gets the Synapse application's environment variables - /// - [DataMember(Order = 9)] - public virtual IDictionary EnvironmentVariables { get; set; } - - /// - /// Gets a collection containing installed plugins - /// - [DataMember(Order = 10)] - public virtual ICollection Plugins { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1Correlation.cs b/src/core/Synapse.Integration/Models/v1/V1Correlation.cs deleted file mode 100644 index ea20a9697..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1Correlation.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - - public partial class V1Correlation - { - - ///

- /// Determines whether or not the specified matches one of the 's conditions - /// - /// The to check - /// A boolean indicating whether or not the specified matches one of the 's conditions - public virtual bool AppliesTo(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.GetMatchingConditionFor(e) != null; - } - - /// - /// Gets the first matching for the specified - /// - /// The to get the for - /// The first matching for the specified , if any - public virtual V1CorrelationCondition GetMatchingConditionFor(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.Conditions.FirstOrDefault(c => c.Matches(e)); - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1CorrelationCondition.cs b/src/core/Synapse.Integration/Models/v1/V1CorrelationCondition.cs deleted file mode 100644 index 2879aebba..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1CorrelationCondition.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - public partial class V1CorrelationCondition - { - - ///

- /// Determines whether or not the matches the specified - /// - /// The to match - /// A boolean indicating whether or not the matches the specified - public virtual bool Matches(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.GetMatchingFilterFor(e) != null; - } - - /// - /// Gets the matching for the specified - /// - /// The to get the matching for - /// The matching for the specified , if any - public virtual V1EventFilter GetMatchingFilterFor(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return this.Filters.FirstOrDefault(f => f.Filters(e)); - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1Event.cs b/src/core/Synapse.Integration/Models/v1/V1Event.cs deleted file mode 100644 index ebfecb679..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1Event.cs +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using CloudNative.CloudEvents; -using System.Net.Mime; - -namespace Synapse.Integration.Models -{ - - public partial class V1Event - { - - ///

- /// Gets an containing all the attributes - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyDictionary Attributes - { - get - { - return this.AttributesEnumerator.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - } - } - - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - IEnumerable> AttributesEnumerator - { - get - { - yield return new KeyValuePair(nameof(Id).ToLower(), this.Id); - yield return new KeyValuePair(nameof(Source).ToLower(), this.Source == null ? null : this.Source.ToString()); - yield return new KeyValuePair(nameof(SpecVersion).ToLower(), this.SpecVersion); - yield return new KeyValuePair(nameof(Type).ToLower(), this.Type); - if (!string.IsNullOrEmpty(this.DataContentType)) - yield return new KeyValuePair(nameof(DataContentType).ToLower(), this.DataContentType); - if (this.DataSchema != null) - yield return new KeyValuePair(nameof(DataSchema).ToLower(), this.DataSchema.ToString()); - if (!string.IsNullOrEmpty(this.Subject)) - yield return new KeyValuePair(nameof(Subject).ToLower(), this.Subject); - if (this.Time.HasValue) - yield return new KeyValuePair(nameof(Time).ToLower(), this.Time.ToString()!); - if(this.ExtensionAttributes != null) - { - foreach (var extension in this.ExtensionAttributes) - yield return new(extension.Key, extension.Value.ToString()!); - } - } - } - - /// - /// Gets an containing the default context attributes - /// - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - public virtual IReadOnlyDictionary ContextAttributes - { - get - { - return this.ContextAttributesEnumerator.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString()); - } - } - - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - IEnumerable> ContextAttributesEnumerator - { - get - { - yield return new KeyValuePair(nameof(Id).ToLower(), this.Id); - yield return new KeyValuePair(nameof(Source).ToLower(), this.Source.ToString()); - yield return new KeyValuePair(nameof(SpecVersion).ToLower(), this.SpecVersion); - yield return new KeyValuePair(nameof(Type).ToLower(), this.Type); - if (!string.IsNullOrEmpty(this.DataContentType)) - yield return new KeyValuePair(nameof(DataContentType).ToLower(), this.DataContentType); - if (this.DataSchema != null) - yield return new KeyValuePair(nameof(DataSchema).ToLower(), this.DataSchema.ToString()); - if (!string.IsNullOrEmpty(this.Subject)) - yield return new KeyValuePair(nameof(Subject).ToLower(), this.Subject); - if (this.Time.HasValue) - yield return new KeyValuePair(nameof(Time).ToLower(), this.Time.ToString()!); - } - } - - /// - /// Attempts to get the attribute with the specified name - /// - /// The name of the attribute to get - /// The value of the attribute, if any - /// A boolean indicating whether or not the attribute with the specified name is defined - public virtual bool TryGetAttribute(string name, out string value) - { - if (string.IsNullOrEmpty(name)) - throw new ArgumentNullException(nameof(name)); - return this.Attributes.TryGetValue(name, out value!); - } - - /// - /// Sets the specified attribute - /// - /// The name of the attribute to set - /// The attribute's name - public void SetAttribute(string name, string value) - { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullException(nameof(name)); - if (name.Equals(nameof(V1Event.DataContentType), StringComparison.OrdinalIgnoreCase)) this.DataContentType = value; - else if (name.Equals(nameof(V1Event.DataSchema), StringComparison.OrdinalIgnoreCase)) this.DataSchema = string.IsNullOrWhiteSpace(value) ? null : new(value); - else if (name.Equals(nameof(V1Event.Id), StringComparison.OrdinalIgnoreCase)) this.Id = value; - else if (name.Equals(nameof(V1Event.Source), StringComparison.OrdinalIgnoreCase)) this.Source = (string.IsNullOrWhiteSpace(value) ? null : new(value))!; - else if (name.Equals(nameof(V1Event.SpecVersion), StringComparison.OrdinalIgnoreCase)) this.SpecVersion = value; - else if (name.Equals(nameof(V1Event.Subject), StringComparison.OrdinalIgnoreCase)) this.Subject = value; - else if (name.Equals(nameof(V1Event.Time), StringComparison.OrdinalIgnoreCase)) this.Time = string.IsNullOrWhiteSpace(value) ? null : DateTime.Parse(value); - else if (name.Equals(nameof(V1Event.Type), StringComparison.OrdinalIgnoreCase)) this.Type = value; - else - { - var dynamicValue = value == null ? null : Dynamic.FromObject(value); - if (this.ExtensionAttributes == null) this.ExtensionAttributes = new(); - this.ExtensionAttributes[name.ToLowerInvariant()] = dynamicValue; - } - } - - /// - /// Creates a new - /// - /// A new - public static V1Event Create() - { - return new() - { - Id = Guid.NewGuid().ToString(), - SpecVersion = CloudEventsSpecVersion.Default.VersionId, - DataContentType = MediaTypeNames.Application.Json, - Data = DynamicObject.FromObject(new()) - }; - } - - /// - /// Creates a new for the specified - /// - /// The to create a new for - /// A new - public static V1Event CreateFrom(CloudEvent cloudEvent) - { - if (cloudEvent == null) - throw new ArgumentNullException(nameof(cloudEvent)); - var e = new V1Event() - { - SpecVersion = cloudEvent.SpecVersion.VersionId, - Id = cloudEvent.Id, - Source = cloudEvent.Source!, - Type = cloudEvent.Type!, - DataContentType = cloudEvent.DataContentType, - DataSchema = cloudEvent.DataSchema, - Subject = cloudEvent.Subject, - Time = cloudEvent.Time?.DateTime, - Data = cloudEvent.Data == null ? null : Dynamic.FromObject(cloudEvent.Data) - }; - if(cloudEvent.ExtensionAttributes != null) - { - e.ExtensionAttributes = new(); - foreach (var extensionsAttribute in cloudEvent.ExtensionAttributes) - { - e.ExtensionAttributes.Add(extensionsAttribute.Name, Dynamic.FromObject(extensionsAttribute.Format(cloudEvent[extensionsAttribute]!))); - } - } - return e; - } - - /// - /// Converts the into a new - /// - /// A new - public CloudEvent ToCloudEvent() - { - var e = new CloudEvent() - { - Id = this.Id, - Source = this.Source, - Type = this.Type, - Subject = this.Subject, - Time = this.Time, - DataContentType = this.DataContentType, - DataSchema = this.DataSchema, - Data = this.Data?.ToObject() - }; - if(this.ExtensionAttributes != null) - { - foreach (var attribute in this.ExtensionAttributes) - { - e[attribute.Key] = attribute.Value.ToObject(); - } - } - return e; - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1EventFilter.cs b/src/core/Synapse.Integration/Models/v1/V1EventFilter.cs deleted file mode 100644 index 23b6f6492..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1EventFilter.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Text.RegularExpressions; - -namespace Synapse.Integration.Models -{ - public partial class V1EventFilter - { - - ///

- /// Determines whether or not the filters the specified - /// - /// The to filter - /// A boolean indicating whether or not the filters the specified - public virtual bool Filters(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - foreach (var attribute in this.Attributes) - { - if (!e.TryGetAttribute(attribute.Key, out var value)) - return false; - if (!Regex.IsMatch(value, attribute.Value, RegexOptions.IgnoreCase)) - return false; - } - foreach (var mapping in this.CorrelationMappings) - { - if (!e.TryGetAttribute(mapping.Key, out var value)) - return false; - if (!string.IsNullOrWhiteSpace(mapping.Value) - && !Regex.IsMatch(value, mapping.Value, RegexOptions.IgnoreCase)) - return false; - } - return true; - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1OperationalReport.cs b/src/core/Synapse.Integration/Models/v1/V1OperationalReport.cs deleted file mode 100644 index b33ce779b..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1OperationalReport.cs +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Describes an application's operational report - /// - [ReadModel] - [DataContract] - public class V1OperationalReport - : IIdentifiable - { - - /// - /// Initializes a new - /// - protected V1OperationalReport() - { - - } - - /// - /// Initializes a new - /// - /// The date the applies to - public V1OperationalReport(DateTime date) - { - this.Id = GetIdFor(date); - this.Date = date; - } - - /// - /// Gets the 's id - /// - [DataMember(Order = 1)] - public string Id { get; set; } - - object IIdentifiable.Id => this.Id; - - /// - /// Gets the date the applies to - /// - [DataMember(Order = 2)] - public virtual DateTime Date { get; set; } - - /// - /// Gets the total amount of workflow definitions that were created during the day the applies to - /// - [DataMember(Order = 3)] - public virtual long TotalDefinitions { get; set; } - - /// - /// Gets the total amount of workflow definition versions that were created during the day the applies to - /// - [DataMember(Order = 4)] - public virtual long TotalDefinitionVersions { get; set; } - - /// - /// Gets the total amount of workflow instances that were created during the day the applies to - /// - [DataMember(Order = 5)] - public virtual long TotalInstances { get; set; } - - /// - /// Gets the total amount of running workflow instances at the moment the was created - /// - [DataMember(Order = 6)] - public virtual long RunningInstances { get; set; } - - /// - /// Gets the total amount of executed workflow instances at the moment the was created - /// - [DataMember(Order = 7)] - public virtual long ExecutedInstances { get; set; } - - /// - /// Gets the total amount of completed workflow instances at the moment the was created - /// - [DataMember(Order = 8)] - public virtual long CompletedInstances { get; set; } - - /// - /// Gets the total amount of faulted workflow instances at the moment the was created - /// - [DataMember(Order = 9)] - public virtual long FaultedInstances { get; set; } - - /// - /// Gets the total amount of cancelled workflow instances at the moment the was created - /// - [DataMember(Order = 10)] - public virtual long CancelledInstances { get; set; } - - /// - /// Gets the total amount of workflow activities that were created during the day the applies to - /// - [DataMember(Order = 11)] - public virtual long TotalActivities { get; set; } - - /// - /// Gets the total amount of running workflow activities at the moment the was created - /// - [DataMember(Order = 12)] - public virtual long RunningActivities { get; set; } - - /// - /// Gets the total amount of executed workflow activities at the moment the was created - /// - [DataMember(Order = 13)] - public virtual long ExecutedActivities { get; set; } - - /// - /// Gets the total amount of completed workflow activities at the moment the was created - /// - [DataMember(Order = 14)] - public virtual long CompletedActivities { get; set; } - - /// - /// Gets the total amount of faulted workflow activities at the moment the was created - /// - [DataMember(Order = 15)] - public virtual long FaultedActivities { get; set; } - - /// - /// Gets the total amount of cancelled workflow activities at the moment the was created - /// - [DataMember(Order = 16)] - public virtual long CancelledActivities { get; set; } - - /// - /// Gets the total amount of skipped workflow activities at the moment the was created - /// - [DataMember(Order = 17)] - public virtual long SkippedActivities { get; set; } - - /// - /// Builds a new id for the specified date - /// - /// The date to create a new id for - /// A new id for the specified date - public static string GetIdFor(DateTime date) - { - return date.ToString("yyyyMMdd"); - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1PluginInfo.cs b/src/core/Synapse.Integration/Models/v1/V1PluginInfo.cs deleted file mode 100644 index 60cc2b401..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1PluginInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - - ///

- /// Describe a plugin - /// - public class V1PluginInfo - { - - /// - /// Initializes a new - /// - protected V1PluginInfo() - { - - } - - /// - /// Initializes a new - /// - /// The plugin's location - /// The plugin's metadata - /// A boolean indicating whether or not the described plugin is loaded - public V1PluginInfo(string location, V1PluginMetadata metadata, bool isLoaded) - { - this.Location = location; - this.Metadata = metadata; - this.IsLoaded = isLoaded; - } - - /// - /// Gets the name of the plugin's location - /// - public virtual string Location { get; } - - /// - /// Gets the plugin's metadata - /// - public virtual V1PluginMetadata Metadata { get; set; } - - /// - /// Gets a boolean indicating whether or not the described plugin is loaded - /// - public virtual bool IsLoaded { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1PluginMetadata.cs b/src/core/Synapse.Integration/Models/v1/V1PluginMetadata.cs deleted file mode 100644 index 4bb6fbd14..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1PluginMetadata.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Reflection; - -namespace Synapse.Integration.Models -{ - - ///

- /// Represents an object used to describe a plugin - /// - public class V1PluginMetadata - { - - /// - /// Gets the plugin's type - /// - [System.Text.Json.Serialization.JsonPropertyName("type")] - [Newtonsoft.Json.JsonProperty("type")] - public virtual V1PluginType Type { get; set; } = V1PluginType.Generic; - - /// - /// Gets the plugin's name - /// - [Required, MinLength(1)] - [System.Text.Json.Serialization.JsonPropertyName("name")] - [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Always)] - public virtual string Name { get; protected set; } = null!; - - /// - /// Gets the plugin's description - /// - [System.Text.Json.Serialization.JsonPropertyName("description")] - [Newtonsoft.Json.JsonProperty("description")] - public virtual string Description { get; protected set; } - - /// - /// Gets the plugin's version - /// - [System.Text.Json.Serialization.JsonPropertyName("version")] - [Newtonsoft.Json.JsonProperty("version")] - public virtual string Version { get; protected set; } - - /// - /// Gets the plugin's authors - /// - [System.Text.Json.Serialization.JsonPropertyName("authors")] - [Newtonsoft.Json.JsonProperty("authors")] - public virtual string Authors { get; protected set; } - - /// - /// Gets the plugin's copyright - /// - [System.Text.Json.Serialization.JsonPropertyName("copyright")] - [Newtonsoft.Json.JsonProperty("copyright")] - public virtual string Copyright { get; protected set; } - - /// - /// Gets a containing the plugin's tags - /// - [System.Text.Json.Serialization.JsonPropertyName("tags")] - [Newtonsoft.Json.JsonProperty("tags")] - public virtual List Tags { get; protected set; } = new(); - - /// - /// Gets the plugin's license file - /// - [System.Text.Json.Serialization.JsonPropertyName("licenseUri")] - [Newtonsoft.Json.JsonProperty("licenseUri")] - public virtual Uri LicenseUri { get; protected set; } - - /// - /// Gets the plugin's readme file - /// - [System.Text.Json.Serialization.JsonPropertyName("readmeUri")] - [Newtonsoft.Json.JsonProperty("readmeUri")] - public virtual Uri ReadmeUri { get; protected set; } - - /// - /// Gets the plugin's website - /// - [System.Text.Json.Serialization.JsonPropertyName("websiteUri")] - [Newtonsoft.Json.JsonProperty("websiteUri")] - public virtual Uri WebsiteUri { get; protected set; } - - /// - /// Gets the plugin's repository - /// - [System.Text.Json.Serialization.JsonPropertyName("repositoryUri")] - [Newtonsoft.Json.JsonProperty("repositoryUri")] - public virtual Uri RepositoryUri { get; protected set; } - - /// - /// Gets the plugin's file name - /// - [Required, MinLength(1)] - [System.Text.Json.Serialization.JsonPropertyName("assemblyFile")] - [Newtonsoft.Json.JsonProperty("assemblyFile", Required = Newtonsoft.Json.Required.Always)] - public virtual string AssemblyFileName { get; protected set; } = null!; - - /// - public override string ToString() - { - if (string.IsNullOrWhiteSpace(this.Version)) - return this.Name; - else - return $"{this.Name}:{this.Version}"; - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1Schedule.cs b/src/core/Synapse.Integration/Models/v1/V1Schedule.cs deleted file mode 100644 index 46256c77c..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1Schedule.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - - public partial class V1Schedule - { - - ///

- /// Gets the total number of occurences - /// - [DataMember(Name = "totalOccurences", Order = 11)] - [Description("The total number of occurences")] - public virtual long TotalOccurences { get; set; } - - /// - /// Gets a list containing the ids of the schedule's active occurences - /// - [DataMember(Name = "activeOccurences", Order = 12)] - [Description("A list containing the ids of the schedule's active occurences")] - public virtual List ActiveOccurences { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1Workflow.cs b/src/core/Synapse.Integration/Models/v1/V1Workflow.cs deleted file mode 100644 index 25cfa352d..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1Workflow.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - - public partial class V1Workflow - { - - ///

- /// Gets the total count of the described workflow's instances - /// - [DataMember(Order = 20)] - public virtual long TotalInstanceCount { get; set; } - - /// - /// Gets the total count of the described workflow's running instances - /// - [DataMember(Order = 21)] - public virtual long RunningInstanceCount { get; set; } - - /// - /// Gets the total count of executed instances - /// - [DataMember(Order = 22)] - public virtual long ExecutedInstanceCount { get; set; } - - /// - /// Gets the total count of completed instances - /// - [DataMember(Order = 23)] - public virtual long CompletedInstanceCount { get; set; } - - /// - /// Gets the total count of faulted instances - /// - [DataMember(Order = 24)] - public virtual long FaultedInstanceCount { get; set; } - - /// - /// Gets the total count of cancelled instances - /// - [DataMember(Order = 25)] - public virtual long CancelledInstanceCount { get; set; } - - /// - /// Gets the total execution time of the described workflow's instances - /// - [DataMember(Order = 26)] - public virtual TimeSpan TotalInstanceExecutionTime { get; set; } = TimeSpan.Zero; - - /// - /// Gets the shortest execution time of the described workflow's instances - /// - [DataMember(Order = 27)] - public virtual TimeSpan? ShortestInstanceDuration { get; set; } - - /// - /// Gets the longest execution time of the described workflow's instances - /// - [DataMember(Order = 28)] - public virtual TimeSpan? LongestInstanceDuration { get; set; } - - /// - /// Gets the average duration the describe workflow's instances - /// - [IgnoreDataMember] - [Newtonsoft.Json.JsonIgnore] - [System.Text.Json.Serialization.JsonIgnore] - [YamlDotNet.Serialization.YamlIgnore] - public virtual TimeSpan AverageInstanceDuration => this.ExecutedInstanceCount < 1 ? TimeSpan.Zero : TimeSpan.FromTicks(this.TotalInstanceExecutionTime.Ticks / this.ExecutedInstanceCount); - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1WorkflowActivity.cs b/src/core/Synapse.Integration/Models/v1/V1WorkflowActivity.cs deleted file mode 100644 index 8268a8108..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1WorkflowActivity.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - public partial class V1WorkflowActivity - { - - ///

- /// Gets the workflow instance's duration - /// - public virtual TimeSpan? Duration => this.StartedAt.HasValue && this.ExecutedAt.HasValue ? this.ExecutedAt.Value.Subtract(this.StartedAt.Value) : null; - - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1WorkflowInstance.cs b/src/core/Synapse.Integration/Models/v1/V1WorkflowInstance.cs deleted file mode 100644 index 0150e1ab8..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1WorkflowInstance.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - - public partial class V1WorkflowInstance - { - - ///

- /// Gets the workflow instance's duration - /// - public virtual TimeSpan? Duration => this.ExecutedAt.HasValue && this.StartedAt.HasValue ? this.ExecutedAt.Value.Subtract(this.StartedAt.Value) : null; - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1WorkflowReference.cs b/src/core/Synapse.Integration/Models/v1/V1WorkflowReference.cs deleted file mode 100644 index 7b6357bc2..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1WorkflowReference.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - ///

- /// Describes an object used to reference workflows - /// - public class V1WorkflowReference - { - - /// - /// Initializes a new - /// - public V1WorkflowReference() - { - - } - - /// - /// Initializes a new - /// - /// The id of the workflow to reference - /// The version of the workflow to reference - public V1WorkflowReference(string id, string version = null) - { - this.Id = id; - this.Version = version; - } - - /// - /// Gets the id of the workflow to reference - /// - public virtual string Id { get; set; } - - /// - /// Gets the version of the workflow to reference - /// - public virtual string Version { get; set; } - - /// - /// Parses the specified input into a new - /// - /// The input to parse - /// A new - public static V1WorkflowReference Parse(string input) - { - var components = input.Split(':', StringSplitOptions.RemoveEmptyEntries); - var id = components.First(); - var version = string.Empty; - if (components.Length >= 2) version = input.Substring(id.Length + 1); - if (string.IsNullOrWhiteSpace(version)) version = "latest"; - return new(id, version); - } - - } - -} diff --git a/src/core/Synapse.Integration/Models/v1/V1WorkflowRuntimeSession.cs b/src/core/Synapse.Integration/Models/v1/V1WorkflowRuntimeSession.cs deleted file mode 100644 index 5a5c13880..000000000 --- a/src/core/Synapse.Integration/Models/v1/V1WorkflowRuntimeSession.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Models -{ - public partial class V1WorkflowRuntimeSession - { - - ///

- /// Gets a boolean indicating whether or not the session is active - /// - public virtual bool IsActive => !this.EndedAt.HasValue; - - /// - /// Gets the session's duration - /// - public virtual TimeSpan? Duration => this.EndedAt.HasValue ? this.EndedAt.Value.Subtract(this.StartedAt) : null; - - } - -} diff --git a/src/core/Synapse.Integration/OperationResultException.cs b/src/core/Synapse.Integration/OperationResultException.cs deleted file mode 100644 index 66e11dea1..000000000 --- a/src/core/Synapse.Integration/OperationResultException.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Synapse -{ - - /// - /// Represents an exception thrown whenever an operation returned a non-success - /// - public class OperationResultException - : Exception - { - - /// - /// Initializes a new - /// - /// The which is the cause of the - public OperationResultException(IOperationResult result) - : base($"The operation was executed with a non-success result code '{result.Code}'.{Environment.NewLine}Errors: {(result.Errors == null ? "" : string.Join(Environment.NewLine, result.Errors.Select(e => $"{e.Code}: {e.Message}")))}") - { - - } - - } - -} diff --git a/src/core/Synapse.Integration/Properties/GlobalUsings.cs b/src/core/Synapse.Integration/Properties/GlobalUsings.cs deleted file mode 100644 index 24d137e2c..000000000 --- a/src/core/Synapse.Integration/Properties/GlobalUsings.cs +++ /dev/null @@ -1,11 +0,0 @@ -global using Microsoft.AspNetCore.Http; -global using Microsoft.AspNetCore.JsonPatch; -global using Neuroglia; -global using Neuroglia.Serialization; -global using ServerlessWorkflow.Sdk.Models; -global using Synapse.Integration.Models; -global using System; -global using System.ComponentModel; -global using System.ComponentModel.DataAnnotations; -global using System.Runtime.Serialization; -global using Error = Synapse.Integration.Models.Error; \ No newline at end of file diff --git a/src/core/Synapse.Integration/Queries/V1SearchQuery.cs b/src/core/Synapse.Integration/Queries/V1SearchQuery.cs deleted file mode 100644 index d5c813fb8..000000000 --- a/src/core/Synapse.Integration/Queries/V1SearchQuery.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Queries -{ - - ///

- /// Describes a search query - /// - public class V1SearchQuery - { - - /// - /// Initializes a new - /// - protected V1SearchQuery() - { - - } - - /// - /// Initializes a new - /// - /// The term to search for - public V1SearchQuery(string term) - { - if(string.IsNullOrWhiteSpace(term)) - throw new ArgumentNullException(nameof(term)); - this.Term = term; - } - - /// - /// Initializes a new - /// - /// The term to search for - /// The optional query string - public V1SearchQuery(string term, string query) - : this(term) - { - this.Query = query; - } - - /// - /// Gets/sets the term to search for - /// - public virtual string Term { get; set; } - - /// - /// Gets/sets the optional query string - /// - public virtual string Query { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Queries/WorkflowActivities/v1/V1GetActivityParentStateDataQuery.cs b/src/core/Synapse.Integration/Queries/WorkflowActivities/v1/V1GetActivityParentStateDataQuery.cs deleted file mode 100644 index 10a7cbc5c..000000000 --- a/src/core/Synapse.Integration/Queries/WorkflowActivities/v1/V1GetActivityParentStateDataQuery.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Synapse.Integration.Queries.WorkflowActivities -{ - /// - /// Represents the query used to get the data of a workflow activity's parent state - /// - [DataContract] - public class V1GetActivityParentStateDataQuery - : DataTransferObject - { - - /// - /// Initializes a new - /// - public V1GetActivityParentStateDataQuery() - { - this.WorkflowInstanceId = null!; - this.ActivityId = null!; - } - - /// - /// Initializes a new - /// - /// The id of the workflow instance the activity to get the parent state data of belongs to - /// The id of the activity to get the parent state data of belongs to - public V1GetActivityParentStateDataQuery(string workflowInstanceId, string activityId) - { - this.WorkflowInstanceId = workflowInstanceId; - this.ActivityId = activityId; - } - - /// - /// Gets the id of the workflow instance the activity to get the parent state data of belongs to - /// - [DataMember(Order = 1)] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// Gets the id of the activity to get the parent state data of belongs to - /// - [DataMember(Order = 2)] - public virtual string ActivityId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Queries/WorkflowActivities/v1/V1GetWorkflowActivitiesQuery.cs b/src/core/Synapse.Integration/Queries/WorkflowActivities/v1/V1GetWorkflowActivitiesQuery.cs deleted file mode 100644 index 323f10d85..000000000 --- a/src/core/Synapse.Integration/Queries/WorkflowActivities/v1/V1GetWorkflowActivitiesQuery.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Integration.Queries.WorkflowActivities -{ - - ///

- /// Describes the query used to find specific workflow activities - /// - [DataContract] - public class V1GetWorkflowActivitiesQuery - : DataTransferObject - { - - /// - /// Initializes a new - /// - public V1GetWorkflowActivitiesQuery() - { - - } - - /// - /// Initializes a new - /// - /// The id of the workflow instance to get the workflow activity instances of - /// A boolean indicating whether or not to include non-operative activities - /// The id of the workflow activity to get the child activities of - public V1GetWorkflowActivitiesQuery(string workflowInstanceId, bool includeNonOperative = false, string parentId = null) - { - this.WorkflowInstanceId = workflowInstanceId; - this.IncludeNonOperative = includeNonOperative; - this.ParentId = parentId; - } - - /// - /// Gets the id of the workflow instance to get the workflow activity instances of - /// - [DataMember(Order = 1)] - public virtual string WorkflowInstanceId { get; set; } - - /// - /// Gets a boolean indicating whether or not to include non-operative activities - /// - [DataMember(Order = 2)] - public virtual bool IncludeNonOperative { get; set; } - - /// - /// Gets the id of the workflow activity to get the child activities of - /// - [DataMember(Order = 3)] - public virtual string ParentId { get; set; } - - } - -} diff --git a/src/core/Synapse.Integration/Serialization/Converters/FilteredExpandoObjectConverter.cs b/src/core/Synapse.Integration/Serialization/Converters/FilteredExpandoObjectConverter.cs deleted file mode 100644 index 6c9654be0..000000000 --- a/src/core/Synapse.Integration/Serialization/Converters/FilteredExpandoObjectConverter.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using System.Dynamic; - -namespace Synapse.Integration.Serialization.Converters -{ - - ///

- /// Represents the service used to convert s - /// - public class FilteredExpandoObjectConverter - : ExpandoObjectConverter - { - - /// - public override bool CanWrite => true; - - /// - public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) - { - var expando = (IDictionary)value; - var dictionary = expando - .Where(p => p.Value is not null) - .ToDictionary(p => p.Key, p => p.Value); - serializer.Serialize(writer, dictionary); - } - - } -} diff --git a/src/core/Synapse.Integration/Synapse.Integration.csproj b/src/core/Synapse.Integration/Synapse.Integration.csproj deleted file mode 100644 index aa789c5c2..000000000 --- a/src/core/Synapse.Integration/Synapse.Integration.csproj +++ /dev/null @@ -1,457 +0,0 @@ - - - net6.0 - enable - disable - 0.4.3 - True - The Synapse Authors - Cloud Native Computing Foundation - Copyright © 2022-Present The Synapse Authors. All Rights Reserved. - https://github.com/serverlessworkflow/synapse - git - https://github.com/serverlessworkflow/synapse - synapse integration - en - true - True - Apache-2.0 - True - synapse-headless.png - $(VersionPrefix).0 - $(VersionPrefix).0 - embedded - This package contains everything you need to integrate Synapse WFMS and its APIs - - - - \ - True - - - - - - - - - - - - - - - True - True - commands.tt - - - TextTemplatingFileGenerator - commands.tst - - - True - True - events.tt - - - TextTemplatingFileGenerator - events.tst - - - True - True - models.tt - - - TextTemplatingFileGenerator - models.tst - - - - - - - - ..\Synapse.Application\Commands\AuthenticationDefinitionCollections\v1\V1CreateFunctionDefinitionCollectionCommand.cs - - - ..\Synapse.Application\Commands\Correlations\v1\V1CorrelateEventCommand.cs - - - ..\Synapse.Application\Commands\Correlations\v1\V1CreateCorrelationCommand.cs - - - ..\Synapse.Application\Commands\Correlations\v1\V1DeleteCorrelatedEventCommand.cs - - - ..\Synapse.Application\Commands\Correlations\v1\V1DeleteCorrelationContextCommand.cs - - - ..\Synapse.Application\Commands\EventDefinitionCollections\v1\V1CreateEventDefinitionCollectionCommand.cs - - - ..\Synapse.Application\Commands\Events\v1\V1PublishEventCommand.cs - - - ..\Synapse.Application\Commands\FunctionDefinitionCollections\v1\V1CreateFunctionDefinitionCollectionCommand.cs - - - ..\Synapse.Application\Commands\Generic\v1\V1DeleteCommand.cs - - - ..\Synapse.Application\Commands\Generic\v1\V1PatchCommand.cs - - - ..\Synapse.Application\Commands\Schedules\v1\V1CompleteScheduleOccurenceCommand.cs - - - ..\Synapse.Application\Commands\Schedules\v1\V1CreateScheduleCommand.cs - - - ..\Synapse.Application\Commands\Schedules\v1\V1MakeScheduleObsoleteCommand.cs - - - ..\Synapse.Application\Commands\Schedules\v1\V1ResumeScheduleCommand.cs - - - ..\Synapse.Application\Commands\Schedules\v1\V1RetireScheduleCommand.cs - - - ..\Synapse.Application\Commands\Schedules\v1\V1SuspendScheduleCommand.cs - - - ..\Synapse.Application\Commands\Schedules\v1\V1TriggerScheduleCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1CancelWorkflowActivityCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1CompensateActivityCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1CreateWorkflowActivityCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1FaultWorkflowActivityCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1MarkActivityAsCompensateCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1MarkActivityAsCompensatedCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1SetWorkflowActivityMetadataCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1SetWorkflowActivityOutputCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1SkipWorkflowActivityCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1StartWorkflowActivityCommand.cs - - - ..\Synapse.Application\Commands\WorkflowActivities\v1\V1SuspendWorkflowActivityCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1ArchiveWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1CancelWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1ConsumeWorkflowInstancePendingEventCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1CorrelateWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1CreateWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1DeleteWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1FaultWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1MarkWorkflowInstanceAsCancelledCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1MarkWorkflowInstanceAsStartedCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1MarkWorkflowInstanceAsSuspendedCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1ResumeWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1SetWorkflowInstanceCorrelationMappingCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1SetWorkflowInstanceOutputCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1SetWorkflowInstanceStartedCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1StartWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1SuspendWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\WorkflowInstances\v1\V1TryCorrelateWorkflowInstanceCommand.cs - - - ..\Synapse.Application\Commands\Workflows\v1\V1ArchiveWorkflowCommand.cs - - - ..\Synapse.Application\Commands\Workflows\v1\V1CreateWorkflowCommand.cs - - - ..\Synapse.Application\Commands\Workflows\v1\V1DeleteWorkflowCommand.cs - - - ..\Synapse.Application\Commands\Workflows\v1\V1UploadWorkflowCommand.cs - - - ..\Synapse.Domain\Events\AuthenticationDefinitionCollections\v1\V1AuthenticationDefinitionCollectionCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\AuthenticationDefinitionCollections\v1\V1AuthenticationDefinitionCollectionDeletedDomainEvent.cs - - - ..\Synapse.Domain\Events\Correlations\v1\V1ContextAddedToCorrelationDomainEvent.cs - - - ..\Synapse.Domain\Events\Correlations\v1\V1CorrelatedEventReleasedDomainEvent.cs - - - ..\Synapse.Domain\Events\Correlations\v1\V1CorrelationContextReleasedDomainEvent.cs - - - ..\Synapse.Domain\Events\Correlations\v1\V1CorrelationCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\Correlations\v1\V1EventCorrelatedDomainEvent.cs - - - ..\Synapse.Domain\Events\EventDefinitionCollections\v1\V1EventDefinitionCollectionCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\EventDefinitionCollections\v1\V1DefinitionCollectionDeletedDomainEvent.cs - - - ..\Synapse.Domain\Events\FunctionDefinitionCollections\v1\V1FunctionDefinitionAddedToCollectionDomainEvent.cs - - - ..\Synapse.Domain\Events\FunctionDefinitionCollections\v1\V1WorkflowActivityCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\FunctionDefinitionCollections\v1\V1FunctionDefinitionCollectionDeletedDomainEvent.cs - - - ..\Synapse.Domain\Events\FunctionDefinitionCollections\v1\V1FunctionDefinitionCollectionDescriptionChangedDomainEvent.cs - - - ..\Synapse.Domain\Events\FunctionDefinitionCollections\v1\V1FunctionDefinitionRemovedFromCollectionDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleDefinitionChangedDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleDeletedDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleObsolitedDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleOccuredDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleOccurenceCompletedDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleResumedDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleRetiredDomainEvent.cs - - - ..\Synapse.Domain\Events\Schedules\v1\V1ScheduleSuspendedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCancelledDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCompensatedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCompensatingDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCompletedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityExecutedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityFaultedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityMetadataChangedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityResumedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivitySkippedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityStartedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivitySuspendedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCancelledDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCompletedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityFaultedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityResumedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivityStartedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowActivities\v1\V1WorkflowActivitySuspendedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowCorrelationContextChangedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowCorrelationMappingSetDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceCancelledDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceCancellingDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceCompletedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceDeletedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceExecutedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceFaultedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceResumedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceResumingDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceScheduledDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceSchedulingDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceStartedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceStartingDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceSuspendedDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowInstances\v1\V1WorkflowInstanceSuspendingDomainEvent.cs - - - ..\Synapse.Domain\Events\WorkflowProcesses\v1\V1WorkflowProcessExitedDomainEvent.cs - - - ..\Synapse.Domain\Events\Workflows\v1\V1WorkflowCreatedDomainEvent.cs - - - ..\Synapse.Domain\Events\Workflows\v1\V1WorkflowDeletedDomainEvent.cs - - - ..\Synapse.Domain\Events\Workflows\v1\V1WorkflowInstanciatedDomainEvent.cs - - - ..\Synapse.Domain\Models\v1\V1AuthenticationDefinitionCollection.cs - - - ..\Synapse.Domain\Models\v1\V1Correlation.cs - - - ..\Synapse.Domain\Models\v1\V1CorrelationCondition.cs - - - ..\Synapse.Domain\Models\v1\V1CorrelationContext.cs - - - ..\Synapse.Domain\Models\v1\V1CorrelationOutcome.cs - - - ..\Synapse.Domain\Models\v1\V1Event.cs - - - ..\Synapse.Domain\Models\v1\V1EventDefinitionCollection.cs - - - ..\Synapse.Domain\Models\v1\V1EventFilter.cs - - - ..\Synapse.Domain\Models\v1\V1FunctionDefinitionCollection.cs - - - ..\Synapse.Domain\Models\v1\V1Schedule.cs - - - ..\Synapse.Domain\Models\v1\V1WorkflowActivity.cs - - - ..\Synapse.Domain\Models\v1\V1Workflow.cs - - - ..\Synapse.Domain\Models\v1\V1WorkflowInstance.cs - - - ..\Synapse.Domain\Models\v1\V1WorkflowProcess.cs - - - ..\Synapse.Domain\Models\v1\V1WorkflowRuntimeSession.cs - - - \ No newline at end of file diff --git a/src/core/Synapse.Integration/Templates/TemplateGenerator.ttinclude b/src/core/Synapse.Integration/Templates/TemplateGenerator.ttinclude deleted file mode 100644 index 2259a90f9..000000000 --- a/src/core/Synapse.Integration/Templates/TemplateGenerator.ttinclude +++ /dev/null @@ -1,404 +0,0 @@ -<#@ template language="C#" #> -<#@ import namespace="System.Collections.Generic" #> -<#@ assembly name="System.Core" #> -<#@ import namespace="System.Linq" #> -<#+ - public void GenerateTemplate(string solutionName, string templateType, string classFilter) - { -#> -${ - - using System.Text; - using System.Text.RegularExpressions; - - Template(Settings settings) - { - settings.PartialRenderingMode = PartialRenderingMode.Combined; -<#+ switch(templateType) { - case "model": - case "event": #> - settings.IncludeProject("<#= solutionName #>.Domain"); -<#+ break; #> -<#+ case "command": #> - settings.IncludeProject("<#= solutionName #>.Application"); -<#+ break; }#> - settings.OutputFilenameFactory = (file) => GetOutputFilename("<#= templateType #>", file); - } - - string GetOutputFilename(string templateType, File file) - { - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(file.FullName).Directory; -<#+ switch(templateType) { - case "model": #> - System.IO.DirectoryInfo srcDirectory = GetSourcesDirectory(file); - string relativeDirectoryPath = System.IO.Path.Combine("Models", versionDirectory.Name, "Generated"); - string absoluteDirectoryPath = System.IO.Path.Combine(srcDirectory.FullName, "core", "<#= solutionName #>.Integration", relativeDirectoryPath); - string relativeFilePath = System.IO.Path.Combine("..", relativeDirectoryPath, $"{GetModelName(file.Classes.First())}.cs"); - if(!System.IO.Directory.Exists(absoluteDirectoryPath)) - System.IO.Directory.CreateDirectory(absoluteDirectoryPath); - if (file.Classes.Any()) - return relativeFilePath; -<#+ break; #> -<#+ case "event": #> - Class c = file.Classes.First(); - if(c.Name != "DomainEvent" - && c.Name.EndsWith("DomainEvent")) - { - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; - System.IO.DirectoryInfo directory = GetSourcesDirectory(file); - string directoryName = $"Events\\{aggregateDirectory.Name}\\{versionDirectory.Name}\\Generated"; - string relativePath = $"..\\{directoryName}"; - string absolutePath = System.IO.Path.Combine(directory.FullName, "core", $"<#= solutionName #>.Integration", directoryName); - if(!System.IO.Directory.Exists(absolutePath)) - System.IO.Directory.CreateDirectory(absolutePath); - return $"{relativePath}\\{GetEventName(c)}.cs"; - } -<#+ break; #> -<#+ case "command": #> - Class c = file.Classes.First(); - if(c.Name.EndsWith("Command")) - { - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; - System.IO.DirectoryInfo directory = GetSourcesDirectory(file); - string directoryName = $"Commands\\{aggregateDirectory.Name}\\{versionDirectory.Name}\\Generated"; - string relativePath = $"..\\{directoryName}"; - string absolutePath = System.IO.Path.Combine(directory.FullName, "core", $"<#= solutionName #>.Integration", directoryName); - if(!System.IO.Directory.Exists(absolutePath)) - System.IO.Directory.CreateDirectory(absolutePath); - return $"{relativePath}\\{GetCommandName(c)}.cs"; - } -<#+ break; }#> - throw new InvalidOperationException($"Failed to resolve the output file name for file '{file.FullName}'"); - } - - System.IO.DirectoryInfo GetSourcesDirectory(File file) - { - System.IO.DirectoryInfo directory = new System.IO.FileInfo(file.FullName).Directory; - while (directory.Name != "src" - && directory.Parent != null) - { - directory = directory.Parent; - } - return directory; - } - - string GetEventName(Class c) - { - return c.Name.Replace("DomainEvent", "IntegrationEvent"); - } - - string GetCommandName(Class c) - { - return c.Name; - } - - string GetAggregateName(Class c) - { - return c.BaseClass.TypeArguments.First().Name; - } - - string GetPluralizedAggregateName(Class c) - { - if(c.Name == "AuthorizationPolicy") - return "AuthorizationPolicies"; - return $"{GetAggregateName(c)}s"; - } - - string GetModelName(Class c) - { - return c.Name; - } - - string GetClassName(Class c) - { -<#+ switch(templateType) { - case "model": #> - return GetModelName(c); -<#+ break; #> -<#+ case "event": #> - return GetEventName(c); -<#+ break; #> -<#+ case "command": #> - return GetCommandName(c); -<#+ break; }#> - } - - string GetBaseClass(Class c) - { -<#+ switch(templateType) { - case "model": #> - if(c.BaseClass == null) - return "DataTransferObject"; - if(c.BaseClass.Name == "AggregateRoot" - || c.BaseClass.Name == "Entity") - return "Entity"; - else - return GetType(c.BaseClass); -<#+ break; #> -<#+ case "event": #> - return "V1IntegrationEvent"; -<#+ break; #> -<#+ case "command": #> - if(c.BaseClass.Name == "Command") - return "Command"; - else - return GetType(c.BaseClass); -<#+ break; }#> - - } - - string GetType(Type type) - { - switch(type.OriginalName) - { - case "Object": - case "Object?": - case "object": - case "object?": - case "TKey": - case "TKey?": - case "JToken": - case "JToken?": - return "Dynamic"; - case "IDictionary": - case "IDictionary?": - case "Dictionary": - case "Dictionary?": - case "ExpandoObject": - case "ExpandoObject?": - return "DynamicObject"; - case "JArray": - case "JArray?": - return "DynamicArray"; - case "Error": - case "Error?": - return "Error"; - case "JsonPatchDocument": - return "JsonPatchDocument"; - case "JObject": - return "JObject"; - case "DateTime": - return "DateTime"; - case "DateTime?": - return "DateTime?"; - case "DateTimeOffset": - return "DateTime"; - case "DateTimeOffset?": - return "DateTime?"; - case "Guid": - return "Guid"; - case "Guid?": - return "Guid?"; - case "ICollection": - case "Collection": - case "IReadOnlyCollection": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IReadOnlyDictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "IEnumerable": - return $"IEnumerable<{GetType(type.TypeArguments.First())}>"; - case "IList": - case "List": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IDictionary": - case "Dictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "Metadata": - return $"NameValueCollection"; - case "Uri": - case "Uri?": - return "Uri"; - case "JSchema": - return "JSchema"; - case "WorkflowDefinition": - return "WorkflowDefinition"; - } - if(type.IsPrimitive) - return type.OriginalName; - string typeName = type.OriginalName; - if(typeName.EndsWith("?")) - typeName = typeName.Substring(0, typeName.Length - 1); - if(typeName.EndsWith("[]")) - typeName = typeName.Substring(0, typeName.Length - 2); - if(type.OriginalName.EndsWith("[]")) - typeName += "[]"; - return typeName; - } - - string Indent(int amount, string text) - { - string indents = ""; - for(int index = 0; index < amount; index++) - { - indents += "\t"; - } - return indents + text; - } - - string SanitizeDocComments(string comments) - { - foreach(Match match in Regex.Matches(comments, @"")) - { - string value = Regex.Match(match.Value, @"(?<= -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - foreach(Match match in Regex.Matches(comments, @"[^.]*<\/see>")) - { - Match innerMatch = Regex.Match(match.Value, @"(?<=)(.*)(?=<\/see>)"); - string value = innerMatch.Groups[1].Value; - var index = value.IndexOf("<"); - if(index > -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - if(comments.StartsWith("Gets ")) - comments = comments.Substring(5); - comments = char.ToUpper(comments[0]) + comments.Substring(1); - return comments; - } - - string NamespaceDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - output.AppendLine(@"/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -"); - output.AppendLine(@"/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ -"); - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(((File)c.Parent).FullName).Directory; - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; -<#+ switch(templateType) { - case "model": #> - output.AppendLine($"namespace <#= solutionName #>.Integration.Models"); -<#+ break; #> -<#+ case "event": #> - output.AppendLine($"namespace <#= solutionName #>.Integration.Events.{aggregateDirectory.Name}"); -<#+ break; #> -<#+ case "command": #> - output.AppendLine($"namespace <#= solutionName #>.Integration.Commands.{aggregateDirectory.Name}"); -<#+ break; }#> - output.AppendLine("{"); - return output.ToString(); - } - - string ClassDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - if(c.DocComment != null) - { - output.AppendLine(Indent(1, "///

")); - output.AppendLine(Indent(1, $"/// {SanitizeDocComments(c.DocComment)}")); - output.AppendLine(Indent(1, "/// ")); - } - output.AppendLine(Indent(1, "[DataContract]")); - Attribute discriminatorAttribute = c.Attributes.FirstOrDefault(a => a.Name == "Discriminator"); - Attribute discriminatorValueAttribute = c.Attributes.FirstOrDefault(a => a.Name == "DiscriminatorValue"); - if(c.BaseClass != null && c.BaseClass.Name.StartsWith("AggregateRoot")) - output.AppendLine(Indent(1, "[Queryable]")); - if(c.IsAbstract && discriminatorAttribute != null) - output.AppendLine(Indent(1, $"[Discriminator(nameof({discriminatorAttribute.Value}))]")); - else if(!c.IsAbstract && discriminatorValueAttribute != null) - output.AppendLine(Indent(1, $"[DiscriminatorValue({discriminatorValueAttribute.Value.Replace("<#= solutionName #>.Integration.", string.Empty).Replace("<#= solutionName #>.", string.Empty)})]")); - output.Append(Indent(1, $"public ")); - if(c.IsAbstract) - output.Append("abstract "); - output.Append("partial "); - output.AppendLine($"class {GetClassName(c)}"); - if(c.BaseClass != null) - output.AppendLine(Indent(2, $": {GetBaseClass(c)}")); - output.AppendLine(Indent(1, "{")); - return output.ToString(); - } - - string PropertyDeclarations(Class c) - { - StringBuilder output = new StringBuilder(); - var order = 1; -<#+ switch(templateType) { - case "event": #> - var description = "Gets the id of the aggregate that has produced the event"; - output.AppendLine(); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {description}")); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"[DataMember(Name = \"aggregateId\", Order = {order})]")); - output.AppendLine(Indent(2, $@"[Description(""{description}"")]")); - output.AppendLine(Indent(2, $"public virtual string AggregateId {{ get; set; }}")); - order++; - description = "Gets the date and time at which the event has been produced"; - output.AppendLine(); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {description}")); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"[DataMember(Name = \"createdAt\", Order = {order})]")); - output.AppendLine(Indent(2, $@"[Description(""{description}"")]")); - output.AppendLine(Indent(2, $"public virtual DateTime CreatedAt {{ get; set; }}")); - order++; -<#+ break; }#> - foreach(Property property in c.Properties.Where(p => !p.Attributes.Any(a => a.Name == "ProjectNever") || p.Attributes.Any(a => a.Name == "Map"))) - { - output.AppendLine(); - if(property.DocComment != null) - { - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {SanitizeDocComments(property.DocComment)}")); - output.AppendLine(Indent(2, "/// ")); - } - Attribute jsonPropertyAttribute = property.Attributes.FirstOrDefault(a => a.Name == "JsonProperty"); - var propertyName = jsonPropertyAttribute == null ? property.Name : jsonPropertyAttribute.Arguments.First().Value.ToString(); - output.AppendLine(Indent(2, $"[DataMember(Name = \"{Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1)}\", Order = {order})]")); - if(jsonPropertyAttribute != null) - { - output.AppendLine(Indent(2, $"[Newtonsoft.Json.JsonProperty({jsonPropertyAttribute.Value})]")); - output.AppendLine(Indent(2, $"[System.Text.Json.Serialization.JsonPropertyName(\"{propertyName}\")]")); - output.AppendLine(Indent(2, $"[YamlDotNet.Serialization.YamlMember(Alias = \"{propertyName}\")]")); - } - if(property.DocComment != null) - output.AppendLine(Indent(2, $@"[Description(""{SanitizeDocComments(property.DocComment)}"")]")); - if(property.Type.OriginalName == "Metadata") - { - output.AppendLine(Indent(2, "[Newtonsoft.Json.JsonExtensionData]")); - output.AppendLine(Indent(2, "[System.Text.Json.Serialization.JsonExtensionData]")); - } - List attributes = new List(property.Attributes.Count); - if(property.Attributes.Any(a => a.Name == "Required")) - attributes.Add("Required"); - Attribute attribute = property.Attributes.FirstOrDefault(a => a.Name == "MinLength"); - if(attribute != null) - attributes.Add($"MinLength({string.Join(", ", attribute.Arguments.Select(a => a.Value))})"); - if(attributes.Any()) - output.AppendLine(Indent(2, $"[{string.Join(", ", attributes)}]")); - output.AppendLine(Indent(2, $"public virtual {GetType(property.Type)} {property.Name} {{ get; set; }}")); - order++; - } - return output.ToString(); - } - -}$Classes(<#= solutionName #>.<#= classFilter #>)[$NamespaceDeclaration -$ClassDeclaration$PropertyDeclarations - } - -}] -<#+ - } -#> \ No newline at end of file diff --git a/src/core/Synapse.Integration/Templates/commands.tst b/src/core/Synapse.Integration/Templates/commands.tst deleted file mode 100644 index 6b975c4d1..000000000 --- a/src/core/Synapse.Integration/Templates/commands.tst +++ /dev/null @@ -1,305 +0,0 @@ - -${ - - using System.Text; - using System.Text.RegularExpressions; - - Template(Settings settings) - { - settings.PartialRenderingMode = PartialRenderingMode.Combined; - settings.IncludeProject("Synapse.Application"); - settings.OutputFilenameFactory = (file) => GetOutputFilename("command", file); - } - - string GetOutputFilename(string templateType, File file) - { - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(file.FullName).Directory; - Class c = file.Classes.First(); - if(c.Name.EndsWith("Command")) - { - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; - System.IO.DirectoryInfo directory = GetSourcesDirectory(file); - string directoryName = $"Commands\\{aggregateDirectory.Name}\\{versionDirectory.Name}\\Generated"; - string relativePath = $"..\\{directoryName}"; - string absolutePath = System.IO.Path.Combine(directory.FullName, "core", $"Synapse.Integration", directoryName); - if(!System.IO.Directory.Exists(absolutePath)) - System.IO.Directory.CreateDirectory(absolutePath); - return $"{relativePath}\\{GetCommandName(c)}.cs"; - } - throw new InvalidOperationException($"Failed to resolve the output file name for file '{file.FullName}'"); - } - - System.IO.DirectoryInfo GetSourcesDirectory(File file) - { - System.IO.DirectoryInfo directory = new System.IO.FileInfo(file.FullName).Directory; - while (directory.Name != "src" - && directory.Parent != null) - { - directory = directory.Parent; - } - return directory; - } - - string GetEventName(Class c) - { - return c.Name.Replace("DomainEvent", "IntegrationEvent"); - } - - string GetCommandName(Class c) - { - return c.Name; - } - - string GetAggregateName(Class c) - { - return c.BaseClass.TypeArguments.First().Name; - } - - string GetPluralizedAggregateName(Class c) - { - if(c.Name == "AuthorizationPolicy") - return "AuthorizationPolicies"; - return $"{GetAggregateName(c)}s"; - } - - string GetModelName(Class c) - { - return c.Name; - } - - string GetClassName(Class c) - { - return GetCommandName(c); - } - - string GetBaseClass(Class c) - { - if(c.BaseClass.Name == "Command") - return "Command"; - else - return GetType(c.BaseClass); - - } - - string GetType(Type type) - { - switch(type.OriginalName) - { - case "Object": - case "Object?": - case "object": - case "object?": - case "TKey": - case "TKey?": - case "JToken": - case "JToken?": - return "Dynamic"; - case "IDictionary": - case "IDictionary?": - case "Dictionary": - case "Dictionary?": - case "ExpandoObject": - case "ExpandoObject?": - return "DynamicObject"; - case "JArray": - case "JArray?": - return "DynamicArray"; - case "Error": - case "Error?": - return "Error"; - case "JsonPatchDocument": - return "JsonPatchDocument"; - case "JObject": - return "JObject"; - case "DateTime": - return "DateTime"; - case "DateTime?": - return "DateTime?"; - case "DateTimeOffset": - return "DateTime"; - case "DateTimeOffset?": - return "DateTime?"; - case "Guid": - return "Guid"; - case "Guid?": - return "Guid?"; - case "ICollection": - case "Collection": - case "IReadOnlyCollection": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IReadOnlyDictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "IEnumerable": - return $"IEnumerable<{GetType(type.TypeArguments.First())}>"; - case "IList": - case "List": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IDictionary": - case "Dictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "Metadata": - return $"NameValueCollection"; - case "Uri": - case "Uri?": - return "Uri"; - case "JSchema": - return "JSchema"; - case "WorkflowDefinition": - return "WorkflowDefinition"; - } - if(type.IsPrimitive) - return type.OriginalName; - string typeName = type.OriginalName; - if(typeName.EndsWith("?")) - typeName = typeName.Substring(0, typeName.Length - 1); - if(typeName.EndsWith("[]")) - typeName = typeName.Substring(0, typeName.Length - 2); - if(type.OriginalName.EndsWith("[]")) - typeName += "[]"; - return typeName; - } - - string Indent(int amount, string text) - { - string indents = ""; - for(int index = 0; index < amount; index++) - { - indents += "\t"; - } - return indents + text; - } - - string SanitizeDocComments(string comments) - { - foreach(Match match in Regex.Matches(comments, @"")) - { - string value = Regex.Match(match.Value, @"(?<= -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - foreach(Match match in Regex.Matches(comments, @"[^.]*<\/see>")) - { - Match innerMatch = Regex.Match(match.Value, @"(?<=)(.*)(?=<\/see>)"); - string value = innerMatch.Groups[1].Value; - var index = value.IndexOf("<"); - if(index > -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - if(comments.StartsWith("Gets ")) - comments = comments.Substring(5); - comments = char.ToUpper(comments[0]) + comments.Substring(1); - return comments; - } - - string NamespaceDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - output.AppendLine(@"/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -"); - output.AppendLine(@"/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ -"); - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(((File)c.Parent).FullName).Directory; - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; - output.AppendLine($"namespace Synapse.Integration.Commands.{aggregateDirectory.Name}"); - output.AppendLine("{"); - return output.ToString(); - } - - string ClassDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - if(c.DocComment != null) - { - output.AppendLine(Indent(1, "///

")); - output.AppendLine(Indent(1, $"/// {SanitizeDocComments(c.DocComment)}")); - output.AppendLine(Indent(1, "/// ")); - } - output.AppendLine(Indent(1, "[DataContract]")); - Attribute discriminatorAttribute = c.Attributes.FirstOrDefault(a => a.Name == "Discriminator"); - Attribute discriminatorValueAttribute = c.Attributes.FirstOrDefault(a => a.Name == "DiscriminatorValue"); - if(c.BaseClass != null && c.BaseClass.Name.StartsWith("AggregateRoot")) - output.AppendLine(Indent(1, "[Queryable]")); - if(c.IsAbstract && discriminatorAttribute != null) - output.AppendLine(Indent(1, $"[Discriminator(nameof({discriminatorAttribute.Value}))]")); - else if(!c.IsAbstract && discriminatorValueAttribute != null) - output.AppendLine(Indent(1, $"[DiscriminatorValue({discriminatorValueAttribute.Value.Replace("Synapse.Integration.", string.Empty).Replace("Synapse.", string.Empty)})]")); - output.Append(Indent(1, $"public ")); - if(c.IsAbstract) - output.Append("abstract "); - output.Append("partial "); - output.AppendLine($"class {GetClassName(c)}"); - if(c.BaseClass != null) - output.AppendLine(Indent(2, $": {GetBaseClass(c)}")); - output.AppendLine(Indent(1, "{")); - return output.ToString(); - } - - string PropertyDeclarations(Class c) - { - StringBuilder output = new StringBuilder(); - var order = 1; - foreach(Property property in c.Properties.Where(p => !p.Attributes.Any(a => a.Name == "JsonIgnore") && !p.Attributes.Any(a => a.Name == "ProjectNever") || p.Attributes.Any(a => a.Name == "Map"))) - { - output.AppendLine(); - if(property.DocComment != null) - { - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {SanitizeDocComments(property.DocComment)}")); - output.AppendLine(Indent(2, "/// ")); - } - Attribute jsonPropertyAttribute = property.Attributes.FirstOrDefault(a => a.Name == "JsonProperty"); - var propertyName = jsonPropertyAttribute == null ? property.Name : jsonPropertyAttribute.Arguments.First().Value.ToString(); - output.AppendLine(Indent(2, $"[DataMember(Name = \"{propertyName}\", Order = {order})]")); - if(jsonPropertyAttribute != null) - { - output.AppendLine(Indent(2, $"[Newtonsoft.Json.JsonProperty({jsonPropertyAttribute.Value})]")); - output.AppendLine(Indent(2, $"[System.Text.Json.Serialization.JsonPropertyName(\"{propertyName}\")]")); - output.AppendLine(Indent(2, $"[YamlDotNet.Serialization.YamlMember(Alias = \"{propertyName}\")]")); - } - if(property.DocComment != null) - output.AppendLine(Indent(2, $@"[Description(""{SanitizeDocComments(property.DocComment)}"")]")); - if(property.Type.OriginalName == "Metadata") - { - output.AppendLine(Indent(2, "[Newtonsoft.Json.JsonExtensionData]")); - output.AppendLine(Indent(2, "[System.Text.Json.Serialization.JsonExtensionData]")); - } - List attributes = new List(property.Attributes.Count); - if(property.Attributes.Any(a => a.Name == "Required")) - attributes.Add("Required"); - Attribute attribute = property.Attributes.FirstOrDefault(a => a.Name == "MinLength"); - if(attribute != null) - attributes.Add($"MinLength({string.Join(", ", attribute.Arguments.Select(a => a.Value))})"); - if(attributes.Any()) - output.AppendLine(Indent(2, $"[{string.Join(", ", attributes)}]")); - output.AppendLine(Indent(2, $"public virtual {GetType(property.Type)} {property.Name} {{ get; set; }}")); - order++; - } - return output.ToString(); - } - -}$Classes(Synapse.Application.Commands.*Command)[$NamespaceDeclaration -$ClassDeclaration$PropertyDeclarations - } - -}] diff --git a/src/core/Synapse.Integration/Templates/commands.tt b/src/core/Synapse.Integration/Templates/commands.tt deleted file mode 100644 index 349ae4b84..000000000 --- a/src/core/Synapse.Integration/Templates/commands.tt +++ /dev/null @@ -1,4 +0,0 @@ -<#@ template debug="true" hostSpecific="true" language="c#" #> -<#@ output extension=".tst" #> -<#@ include file="TemplateGenerator.ttinclude" #> -<# GenerateTemplate("Synapse", "command", "Application.Commands.*Command"); #> \ No newline at end of file diff --git a/src/core/Synapse.Integration/Templates/events.tst b/src/core/Synapse.Integration/Templates/events.tst deleted file mode 100644 index 2a2211442..000000000 --- a/src/core/Synapse.Integration/Templates/events.tst +++ /dev/null @@ -1,321 +0,0 @@ - -${ - - using System.Text; - using System.Text.RegularExpressions; - - Template(Settings settings) - { - settings.PartialRenderingMode = PartialRenderingMode.Combined; - settings.IncludeProject("Synapse.Domain"); - settings.OutputFilenameFactory = (file) => GetOutputFilename("event", file); - } - - string GetOutputFilename(string templateType, File file) - { - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(file.FullName).Directory; - Class c = file.Classes.First(); - if(c.Name != "DomainEvent" - && c.Name.EndsWith("DomainEvent")) - { - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; - System.IO.DirectoryInfo directory = GetSourcesDirectory(file); - string directoryName = $"Events\\{aggregateDirectory.Name}\\{versionDirectory.Name}\\Generated"; - string relativePath = $"..\\{directoryName}"; - string absolutePath = System.IO.Path.Combine(directory.FullName, "core", $"Synapse.Integration", directoryName); - if(!System.IO.Directory.Exists(absolutePath)) - System.IO.Directory.CreateDirectory(absolutePath); - return $"{relativePath}\\{GetEventName(c)}.cs"; - } - throw new InvalidOperationException($"Failed to resolve the output file name for file '{file.FullName}'"); - } - - System.IO.DirectoryInfo GetSourcesDirectory(File file) - { - System.IO.DirectoryInfo directory = new System.IO.FileInfo(file.FullName).Directory; - while (directory.Name != "src" - && directory.Parent != null) - { - directory = directory.Parent; - } - return directory; - } - - string GetEventName(Class c) - { - return c.Name.Replace("DomainEvent", "IntegrationEvent"); - } - - string GetCommandName(Class c) - { - return c.Name; - } - - string GetAggregateName(Class c) - { - return c.BaseClass.TypeArguments.First().Name; - } - - string GetPluralizedAggregateName(Class c) - { - if(c.Name == "AuthorizationPolicy") - return "AuthorizationPolicies"; - return $"{GetAggregateName(c)}s"; - } - - string GetModelName(Class c) - { - return c.Name; - } - - string GetClassName(Class c) - { - return GetEventName(c); - } - - string GetBaseClass(Class c) - { - return "V1IntegrationEvent"; - - } - - string GetType(Type type) - { - switch(type.OriginalName) - { - case "Object": - case "Object?": - case "object": - case "object?": - case "TKey": - case "TKey?": - case "JToken": - case "JToken?": - return "Dynamic"; - case "IDictionary": - case "IDictionary?": - case "Dictionary": - case "Dictionary?": - case "ExpandoObject": - case "ExpandoObject?": - return "DynamicObject"; - case "JArray": - case "JArray?": - return "DynamicArray"; - case "Error": - case "Error?": - return "Error"; - case "JsonPatchDocument": - return "JsonPatchDocument"; - case "JObject": - return "JObject"; - case "DateTime": - return "DateTime"; - case "DateTime?": - return "DateTime?"; - case "DateTimeOffset": - return "DateTime"; - case "DateTimeOffset?": - return "DateTime?"; - case "Guid": - return "Guid"; - case "Guid?": - return "Guid?"; - case "ICollection": - case "Collection": - case "IReadOnlyCollection": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IReadOnlyDictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "IEnumerable": - return $"IEnumerable<{GetType(type.TypeArguments.First())}>"; - case "IList": - case "List": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IDictionary": - case "Dictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "Metadata": - return $"NameValueCollection"; - case "Uri": - case "Uri?": - return "Uri"; - case "JSchema": - return "JSchema"; - case "WorkflowDefinition": - return "WorkflowDefinition"; - } - if(type.IsPrimitive) - return type.OriginalName; - string typeName = type.OriginalName; - if(typeName.EndsWith("?")) - typeName = typeName.Substring(0, typeName.Length - 1); - if(typeName.EndsWith("[]")) - typeName = typeName.Substring(0, typeName.Length - 2); - if(type.OriginalName.EndsWith("[]")) - typeName += "[]"; - return typeName; - } - - string Indent(int amount, string text) - { - string indents = ""; - for(int index = 0; index < amount; index++) - { - indents += "\t"; - } - return indents + text; - } - - string SanitizeDocComments(string comments) - { - foreach(Match match in Regex.Matches(comments, @"")) - { - string value = Regex.Match(match.Value, @"(?<= -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - foreach(Match match in Regex.Matches(comments, @"[^.]*<\/see>")) - { - Match innerMatch = Regex.Match(match.Value, @"(?<=)(.*)(?=<\/see>)"); - string value = innerMatch.Groups[1].Value; - var index = value.IndexOf("<"); - if(index > -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - if(comments.StartsWith("Gets ")) - comments = comments.Substring(5); - comments = char.ToUpper(comments[0]) + comments.Substring(1); - return comments; - } - - string NamespaceDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - output.AppendLine(@"/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -"); - output.AppendLine(@"/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ -"); - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(((File)c.Parent).FullName).Directory; - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; - output.AppendLine($"namespace Synapse.Integration.Events.{aggregateDirectory.Name}"); - output.AppendLine("{"); - return output.ToString(); - } - - string ClassDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - if(c.DocComment != null) - { - output.AppendLine(Indent(1, "///

")); - output.AppendLine(Indent(1, $"/// {SanitizeDocComments(c.DocComment)}")); - output.AppendLine(Indent(1, "/// ")); - } - output.AppendLine(Indent(1, "[DataContract]")); - Attribute discriminatorAttribute = c.Attributes.FirstOrDefault(a => a.Name == "Discriminator"); - Attribute discriminatorValueAttribute = c.Attributes.FirstOrDefault(a => a.Name == "DiscriminatorValue"); - if(c.BaseClass != null && c.BaseClass.Name.StartsWith("AggregateRoot")) - output.AppendLine(Indent(1, "[Queryable]")); - if(c.IsAbstract && discriminatorAttribute != null) - output.AppendLine(Indent(1, $"[Discriminator(nameof({discriminatorAttribute.Value}))]")); - else if(!c.IsAbstract && discriminatorValueAttribute != null) - output.AppendLine(Indent(1, $"[DiscriminatorValue({discriminatorValueAttribute.Value.Replace("Synapse.Integration.", string.Empty).Replace("Synapse.", string.Empty)})]")); - output.Append(Indent(1, $"public ")); - if(c.IsAbstract) - output.Append("abstract "); - output.Append("partial "); - output.AppendLine($"class {GetClassName(c)}"); - if(c.BaseClass != null) - output.AppendLine(Indent(2, $": {GetBaseClass(c)}")); - output.AppendLine(Indent(1, "{")); - return output.ToString(); - } - - string PropertyDeclarations(Class c) - { - StringBuilder output = new StringBuilder(); - var order = 1; - var description = "Gets the id of the aggregate that has produced the event"; - output.AppendLine(); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {description}")); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"[DataMember(Name = \"AggregateId\", Order = {order})]")); - output.AppendLine(Indent(2, $@"[Description(""{description}"")]")); - output.AppendLine(Indent(2, $"public virtual string AggregateId {{ get; set; }}")); - order++; - description = "Gets the date and time at which the event has been produced"; - output.AppendLine(); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {description}")); - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"[DataMember(Name = \"CreatedAt\", Order = {order})]")); - output.AppendLine(Indent(2, $@"[Description(""{description}"")]")); - output.AppendLine(Indent(2, $"public virtual DateTime CreatedAt {{ get; set; }}")); - order++; - foreach(Property property in c.Properties.Where(p => !p.Attributes.Any(a => a.Name == "JsonIgnore") && !p.Attributes.Any(a => a.Name == "ProjectNever") || p.Attributes.Any(a => a.Name == "Map"))) - { - output.AppendLine(); - if(property.DocComment != null) - { - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {SanitizeDocComments(property.DocComment)}")); - output.AppendLine(Indent(2, "/// ")); - } - Attribute jsonPropertyAttribute = property.Attributes.FirstOrDefault(a => a.Name == "JsonProperty"); - var propertyName = jsonPropertyAttribute == null ? property.Name : jsonPropertyAttribute.Arguments.First().Value.ToString(); - output.AppendLine(Indent(2, $"[DataMember(Name = \"{propertyName}\", Order = {order})]")); - if(jsonPropertyAttribute != null) - { - output.AppendLine(Indent(2, $"[Newtonsoft.Json.JsonProperty({jsonPropertyAttribute.Value})]")); - output.AppendLine(Indent(2, $"[System.Text.Json.Serialization.JsonPropertyName(\"{propertyName}\")]")); - output.AppendLine(Indent(2, $"[YamlDotNet.Serialization.YamlMember(Alias = \"{propertyName}\")]")); - } - if(property.DocComment != null) - output.AppendLine(Indent(2, $@"[Description(""{SanitizeDocComments(property.DocComment)}"")]")); - if(property.Type.OriginalName == "Metadata") - { - output.AppendLine(Indent(2, "[Newtonsoft.Json.JsonExtensionData]")); - output.AppendLine(Indent(2, "[System.Text.Json.Serialization.JsonExtensionData]")); - } - List attributes = new List(property.Attributes.Count); - if(property.Attributes.Any(a => a.Name == "Required")) - attributes.Add("Required"); - Attribute attribute = property.Attributes.FirstOrDefault(a => a.Name == "MinLength"); - if(attribute != null) - attributes.Add($"MinLength({string.Join(", ", attribute.Arguments.Select(a => a.Value))})"); - if(attributes.Any()) - output.AppendLine(Indent(2, $"[{string.Join(", ", attributes)}]")); - output.AppendLine(Indent(2, $"public virtual {GetType(property.Type)} {property.Name} {{ get; set; }}")); - order++; - } - return output.ToString(); - } - -}$Classes(Synapse.Domain.Events.*)[$NamespaceDeclaration -$ClassDeclaration$PropertyDeclarations - } - -}] diff --git a/src/core/Synapse.Integration/Templates/events.tt b/src/core/Synapse.Integration/Templates/events.tt deleted file mode 100644 index 053932ef6..000000000 --- a/src/core/Synapse.Integration/Templates/events.tt +++ /dev/null @@ -1,4 +0,0 @@ -<#@ template debug="true" hostSpecific="true" language="c#" #> -<#@ output extension=".tst" #> -<#@ include file="TemplateGenerator.ttinclude" #> -<# GenerateTemplate("Synapse", "event", "Domain.Events.*"); #> \ No newline at end of file diff --git a/src/core/Synapse.Integration/Templates/models.tst b/src/core/Synapse.Integration/Templates/models.tst deleted file mode 100644 index a5994c998..000000000 --- a/src/core/Synapse.Integration/Templates/models.tst +++ /dev/null @@ -1,304 +0,0 @@ - -${ - - using System.Text; - using System.Text.RegularExpressions; - - Template(Settings settings) - { - settings.PartialRenderingMode = PartialRenderingMode.Combined; - settings.IncludeProject("Synapse.Domain"); - settings.OutputFilenameFactory = (file) => GetOutputFilename("model", file); - } - - string GetOutputFilename(string templateType, File file) - { - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(file.FullName).Directory; - System.IO.DirectoryInfo srcDirectory = GetSourcesDirectory(file); - string relativeDirectoryPath = System.IO.Path.Combine("Models", versionDirectory.Name, "Generated"); - string absoluteDirectoryPath = System.IO.Path.Combine(srcDirectory.FullName, "core", "Synapse.Integration", relativeDirectoryPath); - string relativeFilePath = System.IO.Path.Combine("..", relativeDirectoryPath, $"{GetModelName(file.Classes.First())}.cs"); - if(!System.IO.Directory.Exists(absoluteDirectoryPath)) - System.IO.Directory.CreateDirectory(absoluteDirectoryPath); - if (file.Classes.Any()) - return relativeFilePath; - throw new InvalidOperationException($"Failed to resolve the output file name for file '{file.FullName}'"); - } - - System.IO.DirectoryInfo GetSourcesDirectory(File file) - { - System.IO.DirectoryInfo directory = new System.IO.FileInfo(file.FullName).Directory; - while (directory.Name != "src" - && directory.Parent != null) - { - directory = directory.Parent; - } - return directory; - } - - string GetEventName(Class c) - { - return c.Name.Replace("DomainEvent", "IntegrationEvent"); - } - - string GetCommandName(Class c) - { - return c.Name; - } - - string GetAggregateName(Class c) - { - return c.BaseClass.TypeArguments.First().Name; - } - - string GetPluralizedAggregateName(Class c) - { - if(c.Name == "AuthorizationPolicy") - return "AuthorizationPolicies"; - return $"{GetAggregateName(c)}s"; - } - - string GetModelName(Class c) - { - return c.Name; - } - - string GetClassName(Class c) - { - return GetModelName(c); - } - - string GetBaseClass(Class c) - { - if(c.BaseClass == null) - return "DataTransferObject"; - if(c.BaseClass.Name == "AggregateRoot" - || c.BaseClass.Name == "Entity") - return "Entity"; - else - return GetType(c.BaseClass); - - } - - string GetType(Type type) - { - switch(type.OriginalName) - { - case "Object": - case "Object?": - case "object": - case "object?": - case "TKey": - case "TKey?": - case "JToken": - case "JToken?": - return "Dynamic"; - case "IDictionary": - case "IDictionary?": - case "Dictionary": - case "Dictionary?": - case "ExpandoObject": - case "ExpandoObject?": - return "DynamicObject"; - case "JArray": - case "JArray?": - return "DynamicArray"; - case "Error": - case "Error?": - return "Error"; - case "JsonPatchDocument": - return "JsonPatchDocument"; - case "JObject": - return "JObject"; - case "DateTime": - return "DateTime"; - case "DateTime?": - return "DateTime?"; - case "DateTimeOffset": - return "DateTime"; - case "DateTimeOffset?": - return "DateTime?"; - case "Guid": - return "Guid"; - case "Guid?": - return "Guid?"; - case "ICollection": - case "Collection": - case "IReadOnlyCollection": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IReadOnlyDictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "IEnumerable": - return $"IEnumerable<{GetType(type.TypeArguments.First())}>"; - case "IList": - case "List": - return $"ICollection<{GetType(type.TypeArguments.First())}>"; - case "IDictionary": - case "Dictionary": - return $"NameValueCollection<{GetType(type.TypeArguments.Last())}>"; - case "Metadata": - return $"NameValueCollection"; - case "Uri": - case "Uri?": - return "Uri"; - case "JSchema": - return "JSchema"; - case "WorkflowDefinition": - return "WorkflowDefinition"; - } - if(type.IsPrimitive) - return type.OriginalName; - string typeName = type.OriginalName; - if(typeName.EndsWith("?")) - typeName = typeName.Substring(0, typeName.Length - 1); - if(typeName.EndsWith("[]")) - typeName = typeName.Substring(0, typeName.Length - 2); - if(type.OriginalName.EndsWith("[]")) - typeName += "[]"; - return typeName; - } - - string Indent(int amount, string text) - { - string indents = ""; - for(int index = 0; index < amount; index++) - { - indents += "\t"; - } - return indents + text; - } - - string SanitizeDocComments(string comments) - { - foreach(Match match in Regex.Matches(comments, @"")) - { - string value = Regex.Match(match.Value, @"(?<= -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - foreach(Match match in Regex.Matches(comments, @"[^.]*<\/see>")) - { - Match innerMatch = Regex.Match(match.Value, @"(?<=)(.*)(?=<\/see>)"); - string value = innerMatch.Groups[1].Value; - var index = value.IndexOf("<"); - if(index > -1) - value = value.Substring(0, index); - comments = comments.Replace(match.Value, value); - } - if(comments.StartsWith("Gets ")) - comments = comments.Substring(5); - comments = char.ToUpper(comments[0]) + comments.Substring(1); - return comments; - } - - string NamespaceDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - output.AppendLine(@"/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the ""License""); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an ""AS IS"" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -"); - output.AppendLine(@"/* ----------------------------------------------------------------------- - * This file has been automatically generated by a tool - * ----------------------------------------------------------------------- - */ -"); - System.IO.DirectoryInfo versionDirectory = new System.IO.FileInfo(((File)c.Parent).FullName).Directory; - System.IO.DirectoryInfo aggregateDirectory = versionDirectory.Parent; - output.AppendLine($"namespace Synapse.Integration.Models"); - output.AppendLine("{"); - return output.ToString(); - } - - string ClassDeclaration(Class c) - { - StringBuilder output = new StringBuilder(); - if(c.DocComment != null) - { - output.AppendLine(Indent(1, "///

")); - output.AppendLine(Indent(1, $"/// {SanitizeDocComments(c.DocComment)}")); - output.AppendLine(Indent(1, "/// ")); - } - output.AppendLine(Indent(1, "[DataContract]")); - Attribute discriminatorAttribute = c.Attributes.FirstOrDefault(a => a.Name == "Discriminator"); - Attribute discriminatorValueAttribute = c.Attributes.FirstOrDefault(a => a.Name == "DiscriminatorValue"); - if(c.BaseClass != null && c.BaseClass.Name.StartsWith("AggregateRoot")) - output.AppendLine(Indent(1, "[Queryable]")); - if(c.IsAbstract && discriminatorAttribute != null) - output.AppendLine(Indent(1, $"[Discriminator(nameof({discriminatorAttribute.Value}))]")); - else if(!c.IsAbstract && discriminatorValueAttribute != null) - output.AppendLine(Indent(1, $"[DiscriminatorValue({discriminatorValueAttribute.Value.Replace("Synapse.Integration.", string.Empty).Replace("Synapse.", string.Empty)})]")); - output.Append(Indent(1, $"public ")); - if(c.IsAbstract) - output.Append("abstract "); - output.Append("partial "); - output.AppendLine($"class {GetClassName(c)}"); - if(c.BaseClass != null) - output.AppendLine(Indent(2, $": {GetBaseClass(c)}")); - output.AppendLine(Indent(1, "{")); - return output.ToString(); - } - - string PropertyDeclarations(Class c) - { - StringBuilder output = new StringBuilder(); - var order = 1; - foreach(Property property in c.Properties.Where(p => !p.Attributes.Any(a => a.Name == "ProjectNever") || p.Attributes.Any(a => a.Name == "Map"))) - { - output.AppendLine(); - if(property.DocComment != null) - { - output.AppendLine(Indent(2, "/// ")); - output.AppendLine(Indent(2, $"/// {SanitizeDocComments(property.DocComment)}")); - output.AppendLine(Indent(2, "/// ")); - } - Attribute jsonPropertyAttribute = property.Attributes.FirstOrDefault(a => a.Name == "JsonProperty"); - var propertyName = jsonPropertyAttribute == null ? property.Name : jsonPropertyAttribute.Arguments.First().Value.ToString(); - output.AppendLine(Indent(2, $"[DataMember(Name = \"{Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1)}\", Order = {order})]")); - if(jsonPropertyAttribute != null) - { - output.AppendLine(Indent(2, $"[Newtonsoft.Json.JsonProperty({jsonPropertyAttribute.Value})]")); - output.AppendLine(Indent(2, $"[System.Text.Json.Serialization.JsonPropertyName(\"{propertyName}\")]")); - output.AppendLine(Indent(2, $"[YamlDotNet.Serialization.YamlMember(Alias = \"{propertyName}\")]")); - } - if(property.DocComment != null) - output.AppendLine(Indent(2, $@"[Description(""{SanitizeDocComments(property.DocComment)}"")]")); - if(property.Type.OriginalName == "Metadata") - { - output.AppendLine(Indent(2, "[Newtonsoft.Json.JsonExtensionData]")); - output.AppendLine(Indent(2, "[System.Text.Json.Serialization.JsonExtensionData]")); - } - List attributes = new List(property.Attributes.Count); - if(property.Attributes.Any(a => a.Name == "Required")) - attributes.Add("Required"); - Attribute attribute = property.Attributes.FirstOrDefault(a => a.Name == "MinLength"); - if(attribute != null) - attributes.Add($"MinLength({string.Join(", ", attribute.Arguments.Select(a => a.Value))})"); - if(attributes.Any()) - output.AppendLine(Indent(2, $"[{string.Join(", ", attributes)}]")); - output.AppendLine(Indent(2, $"public virtual {GetType(property.Type)} {property.Name} {{ get; set; }}")); - order++; - } - return output.ToString(); - } - -}$Classes(Synapse.Domain.Models.*)[$NamespaceDeclaration -$ClassDeclaration$PropertyDeclarations - } - -}] diff --git a/src/core/Synapse.Integration/Templates/models.tt b/src/core/Synapse.Integration/Templates/models.tt deleted file mode 100644 index 043414071..000000000 --- a/src/core/Synapse.Integration/Templates/models.tt +++ /dev/null @@ -1,4 +0,0 @@ -<#@ template debug="true" hostSpecific="true" language="c#" #> -<#@ output extension=".tst" #> -<#@ include file="TemplateGenerator.ttinclude" #> -<# GenerateTemplate("Synapse", "model", "Domain.Models.*"); #> \ No newline at end of file diff --git a/src/core/Synapse.Integration/UriHelper.cs b/src/core/Synapse.Integration/UriHelper.cs deleted file mode 100644 index 0e223088a..000000000 --- a/src/core/Synapse.Integration/UriHelper.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Defines methods to handle - /// - public static class UriHelper - { - - /// - /// Combines the specified base with a relative uri string - /// - /// The base to combine to the specified relative uri - /// The relative to combine to the specified base uri - /// A new , result of the combination of both the base and relative uris - public static Uri Combine(Uri baseUri, string relativeUri) - { - if (baseUri == null) - throw new ArgumentNullException(nameof(baseUri)); - if (string.IsNullOrWhiteSpace(relativeUri)) - return baseUri; - return new($"{baseUri.ToString().TrimEnd('/')}/{relativeUri.TrimStart('/')}"); - } - - /// - /// Combines the specified base with an array of relative paths - /// - /// The base to combine to the specified relative paths - /// An array containing the relative paths to combine to the specified base uri - /// A new , result of the combination of both the base and relative paths - public static Uri Combine(Uri baseUri, params string[] relativePaths) - { - if (baseUri == null) - throw new ArgumentNullException(nameof(baseUri)); - if (relativePaths.Length == 0) - return baseUri; - var currentUrl = Combine(baseUri, relativePaths[0]); - return Combine(currentUrl, relativePaths.Skip(1).ToArray()); - } - - } - -} diff --git a/src/core/Synapse.Integration/V1WorkflowActivityMetadata.cs b/src/core/Synapse.Integration/V1WorkflowActivityMetadata.cs deleted file mode 100644 index 58e185f1b..000000000 --- a/src/core/Synapse.Integration/V1WorkflowActivityMetadata.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Synapse -{ - - /// - /// Exposes constants about -related metadata - /// - public static class V1WorkflowActivityMetadata - { - - /// - /// Gets the name of the 'action' metadata, used to store the name of the action the activity belongs to - /// - public const string Action = "action"; - /// - /// Gets the name of the 'branch' metadata, used to store the name of the branch the activity belongs to - /// - public const string Branch = "branch"; - /// - /// Gets the name of the 'case' metadata, used to store the name of the switch case the activity belongs to - /// - public const string Case = "case"; - /// - /// Gets the name of the 'compensation-source' metadata, used to reference the activity that is being compensated - /// - public const string CompensationSource= "compensation-source"; - /// - /// Gets the name of the 'event' metadata, used to store the name of the action the activity belongs to - /// - public const string Event = "event"; - /// - /// Gets the name of the 'iteration' metadata, used to store the index of the iteration to process - /// - public const string Iteration = "iteration"; - /// - /// Gets the name of the 'state' metadata, used to store the name of the state the activity belongs to - /// - public const string State = "state"; - /// - /// Gets the name of the 'subflow' metadata, used to store the id of the subflow's instance - /// - public const string Subflow = "subflow"; - /// - /// Gets the name of the 'next-state' metadata, used to define the state to explicitely transition to - /// - public const string NextState = "next-state"; - /// - /// Gets the name of the 'trigger' metadata, used to store the name of the event trigger the activity belongs to - /// - public const string Trigger = "trigger"; - - } - -} diff --git a/src/core/Synapse.Integration/V1WorkflowRuntimeMetadata.cs b/src/core/Synapse.Integration/V1WorkflowRuntimeMetadata.cs deleted file mode 100644 index c5f72c7d4..000000000 --- a/src/core/Synapse.Integration/V1WorkflowRuntimeMetadata.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse -{ - - ///

- /// Exposes constants about workflow runtime metadata - /// - public static class V1WorkflowRuntimeMetadata - { - - /// - /// Gets the name of the metadata that contains the id of the workflow instance executed by the described runtime - /// - public const string WorkflowInstanceId = "workflow-instance-id"; - - } - -} diff --git a/src/correlator/Synapse.Correlator/Commands/CloudEvents/IngestCloudEventCommand.cs b/src/correlator/Synapse.Correlator/Commands/CloudEvents/IngestCloudEventCommand.cs new file mode 100644 index 000000000..e6749d13f --- /dev/null +++ b/src/correlator/Synapse.Correlator/Commands/CloudEvents/IngestCloudEventCommand.cs @@ -0,0 +1,66 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Correlator.Commands.CloudEvents; + +/// +/// Represents the command used to ingest a cloud event +/// +[DataContract] +public class IngestCloudEventCommand + : Command +{ + + /// + /// Initializes a new + /// + protected IngestCloudEventCommand() { } + + /// + /// Initializes a new + /// + /// The to publish + public IngestCloudEventCommand(CloudEvent e) + { + this.Event = e; + } + + /// + /// Gets the to publish + /// + public virtual CloudEvent Event { get; protected set; } = null!; + +} + + +/// +/// Represents the service used to handle s +/// +/// The service used to stream input and output s +public class IngestCloudEventCommandHandler(ICloudEventBus cloudEventBus) + : ICommandHandler +{ + + /// + /// Gets the service used to stream input and output s + /// + protected ICloudEventBus CloudEventBus { get; } = cloudEventBus; + + /// + public virtual async Task HandleAsync(IngestCloudEventCommand command, CancellationToken cancellationToken = default) + { + this.CloudEventBus.InputStream.OnNext(command.Event); + return await Task.FromResult(this.Ok()); + } + +} \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Configuration/CorrelatorOptions.cs b/src/correlator/Synapse.Correlator/Configuration/CorrelatorOptions.cs new file mode 100644 index 000000000..16f9bab7e --- /dev/null +++ b/src/correlator/Synapse.Correlator/Configuration/CorrelatorOptions.cs @@ -0,0 +1,41 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Correlator.Configuration; + +/// +/// Represents the options used to configure a Synapse Correlator application +/// +public class CorrelatorOptions +{ + + /// + /// Initializes a new + /// + public CorrelatorOptions() + { + Namespace = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Correlator.Namespace)!; + Name = Environment.GetEnvironmentVariable(SynapseDefaults.EnvironmentVariables.Correlator.Name)!; + } + + /// + /// Gets/sets the correlator's namespace + /// + public virtual string Namespace { get; set; } + + /// + /// Gets/sets the correlator's name + /// + public virtual string Name { get; set; } + +} diff --git a/src/correlator/Synapse.Correlator/Controllers/CloudEventsController.cs b/src/correlator/Synapse.Correlator/Controllers/CloudEventsController.cs new file mode 100644 index 000000000..2d3df31d8 --- /dev/null +++ b/src/correlator/Synapse.Correlator/Controllers/CloudEventsController.cs @@ -0,0 +1,44 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Correlator.Controllers; + +/// +/// Represents the service used to manage s +/// +/// The service used to mediate calls +[Route("api/v1/events")] +public class CloudEventsController(IMediator mediator) + : Controller +{ + + /// + /// Gets the service used to mediate calls + /// + protected IMediator Mediator { get; } = mediator; + + /// + /// Publishes the specified to the correlator + /// + /// The to publish + /// A + /// A new that describes the result of the operation + [HttpPost("pub")] + [ProducesResponseType((int)HttpStatusCode.Accepted)] + [ProducesDefaultResponseType(typeof(Microsoft.AspNetCore.Mvc.ProblemDetails))] + public async Task Pub([FromBody] CloudEvent e, CancellationToken cancellationToken) + { + var result = await this.Mediator.ExecuteAsync(new IngestCloudEventCommand(e), cancellationToken).ConfigureAwait(false); + return this.Process(result, (int)HttpStatusCode.Accepted); + } +} diff --git a/src/correlator/Synapse.Correlator/Dockerfile b/src/correlator/Synapse.Correlator/Dockerfile new file mode 100644 index 000000000..ccf055c49 --- /dev/null +++ b/src/correlator/Synapse.Correlator/Dockerfile @@ -0,0 +1,27 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER root +RUN apt-get update +RUN apt-get install -y jq +USER app +WORKDIR /app +EXPOSE 8080 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["src/correlator/Synapse.Correlator/Synapse.Correlator.csproj", "src/correlator/Synapse.Correlator/"] +COPY ["src/core/Synapse.Core.Infrastructure/Synapse.Core.Infrastructure.csproj", "src/core/Synapse.Core.Infrastructure/"] +COPY ["src/core/Synapse.Core/Synapse.Core.csproj", "src/core/Synapse.Core/"] +RUN dotnet restore "./src/correlator/Synapse.Correlator/Synapse.Correlator.csproj" +COPY . . +WORKDIR "/src/src/correlator/Synapse.Correlator" +RUN dotnet build "./Synapse.Correlator.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Synapse.Correlator.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Synapse.Correlator.dll"] \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Program.cs b/src/correlator/Synapse.Correlator/Program.cs new file mode 100644 index 000000000..a6b9d6cbe --- /dev/null +++ b/src/correlator/Synapse.Correlator/Program.cs @@ -0,0 +1,113 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +if (args.Length != 0 && args.Contains("--debug") && !Debugger.IsAttached) Debugger.Launch(); + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.Configure(builder.Configuration); +builder.Services.AddLogging(builder => +{ + builder.AddSimpleConsole(options => + { + options.TimestampFormat = "[HH:mm:ss] "; + }); +}); +builder.Services.AddSingleton(); +builder.Services.AddSynapse(builder.Configuration); +builder.Services.AddJQExpressionEvaluator(); +builder.Services.AddJavaScriptExpressionEvaluator(); + +builder.Services.AddCloudEventBus(); +builder.Services.AddTransient, IngestCloudEventCommandHandler>(); + +builder.Services.AddScoped(); +builder.Services.AddScoped(provider => provider.GetRequiredService()); + +builder.Services.AddScoped(); +builder.Services.AddScoped>(provider => provider.GetRequiredService()); + +builder.Services.AddHostedService(); +builder.Services.AddResponseCompression(); +builder.Services.AddControllers() + .AddJsonOptions(options => + { + JsonSerializer.DefaultOptionsConfiguration(options.JsonSerializerOptions); + }); +builder.Services.AddSwaggerGen(builder => +{ + builder.CustomOperationIds(o => + { + var action = (ControllerActionDescriptor)o.ActionDescriptor; + return $"{action.ActionName}".ToCamelCase(); + }); + builder.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); + builder.SwaggerDoc("v1", new OpenApiInfo + { + Title = "Synapse Correlator REST API", + Version = "v1", + Description = "The Open API documentation for the Synapse Correlator REST API", + License = new OpenApiLicense() + { + Name = "Apache-2.0", + Url = new("https://raw.githubusercontent.com/synapse/dsl/main/LICENSE") + }, + Contact = new() + { + Name = "The Synapse Authors", + Url = new Uri("https://github.com/serverlessworkflow/synapse") + } + }); + builder.IncludeXmlComments(typeof(Workflow).Assembly.Location.Replace(".dll", ".xml")); +}); + +using var app = builder.Build(); + +app.UseResponseCompression(); +app.UseCloudEvents(); +app.UseRouting(); +app.UseExceptionHandler(handler => +{ + handler.Run(async context => + { + var exceptionHandlerPathFeature = context.Features.Get(); + var problemDetails = exceptionHandlerPathFeature?.Error switch + { + ProblemDetailsException problemDetailsException => problemDetailsException.Problem, + _ => new Neuroglia.ProblemDetails(ErrorType.Runtime, ErrorTitle.Runtime, ErrorStatus.Runtime, exceptionHandlerPathFeature?.Error.Message) + }; + var json = context.RequestServices.GetRequiredService().SerializeToText(problemDetails); + context.Response.ContentType = MediaTypeNames.Application.Json; + context.Response.StatusCode = problemDetails.Status; + await context.Response.WriteAsync(json).ConfigureAwait(false); + }); +}); +app.UseSwagger(builder => +{ + builder.RouteTemplate = "api/{documentName}/doc/oas.{json|yaml}"; +}); +app.UseSwaggerUI(builder => +{ + builder.DocExpansion(DocExpansion.None); + builder.SwaggerEndpoint("/api/v1/doc/oas.json", "Synapse API v1"); + builder.RoutePrefix = "api/doc"; + builder.DisplayOperationId(); +}); +app.MapControllers(); + +await app.RunAsync(); + +/// +/// The Correlator's program +/// +public partial class Program { } \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Properties/launchSettings.json b/src/correlator/Synapse.Correlator/Properties/launchSettings.json new file mode 100644 index 000000000..f35fcf4a5 --- /dev/null +++ b/src/correlator/Synapse.Correlator/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "Synapse.Correlator": { + "commandName": "Project", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker" + } + } +} \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Services/CorrelationController.cs b/src/correlator/Synapse.Correlator/Services/CorrelationController.cs new file mode 100644 index 000000000..e700cb70b --- /dev/null +++ b/src/correlator/Synapse.Correlator/Services/CorrelationController.cs @@ -0,0 +1,189 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Concurrent; + +namespace Synapse.Correlator.Services; + +/// +/// Represents the service used to manage resources +/// +/// The current +/// The service used to create s +/// The service used to access the current +/// The service used to manage s +/// The service used to provide s +/// The current +/// The service used to access the current +public class CorrelationController(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IOptions> controllerOptions, IResourceRepository resources, IExpressionEvaluatorProvider expressionEvaluatorProvider, IOptions correlatorOptions, ICorrelatorController correlatorAccessor) + : ResourceController(loggerFactory, controllerOptions, resources) +{ + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider { get; } = serviceProvider; + + /// + /// Gets the service used to provide s + /// + protected IExpressionEvaluatorProvider ExpressionEvaluatorProvider { get; } = expressionEvaluatorProvider; + + /// + /// Gets the running 's options + /// + protected CorrelatorOptions CorrelatorOptions { get; } = correlatorOptions.Value; + + /// + /// Gets the service used to monitor the current + /// + protected IResourceMonitor Correlator => correlatorAccessor.Correlator; + + /// + /// Gets a that contains current s + /// + protected ConcurrentDictionary Correlators { get; } = []; + + /// + public override async Task StartAsync(CancellationToken cancellationToken) + { + await base.StartAsync(cancellationToken).ConfigureAwait(false); + this.Correlator!.Select(b => b.Resource.Spec.Selector).SubscribeAsync(this.OnResourceSelectorChangedAsync, cancellationToken: cancellationToken); + await this.OnResourceSelectorChangedAsync(this.Correlator!.Resource.Spec.Selector).ConfigureAwait(false); + } + + /// + protected override Task ReconcileAsync(CancellationToken cancellationToken = default) + { + this.Options.LabelSelectors = this.Correlator?.Resource.Spec.Selector?.Select(s => new LabelSelector(s.Key, LabelSelectionOperator.Equals, s.Value)).ToList(); + return base.ReconcileAsync(cancellationToken); + } + + /// + /// Creates a new for the specified workflow + /// + /// The resource of the workflow to create a new scheduler for + /// A + /// A new + protected virtual async Task CreateCorrelatorAsync(Correlation correlation, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(correlation); + var expressionEvaluator = this.ExpressionEvaluatorProvider.GetEvaluator(correlation.Spec.Expressions.Language) ?? throw new NullReferenceException($"Failed to find a registered expression evaluator that supports the configured language '{correlation.Spec.Expressions.Language}'"); + var correlationMonitor = new ResourceMonitor(this.Watch ?? await this.Repository.WatchAsync(cancellationToken: cancellationToken).ConfigureAwait(false), correlation, this.Watch != null); + var correlator = ActivatorUtilities.CreateInstance(this.ServiceProvider, correlationMonitor, expressionEvaluator); + if (!this.Correlators.TryAdd(correlation.GetQualifiedName(), correlator)) await correlator.DisposeAsync().ConfigureAwait(false); + return correlator; + } + + /// + /// Attempts to claim the specified + /// + /// The to try to claim + /// A + /// A boolean indicating whether or not the instance could be claimed + protected virtual async Task TryClaimAsync(Correlation correlation, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(correlation); + if (this.Correlator == null) throw new Exception("The controller must be started before attempting any operation"); + if (correlation.Metadata.Labels != null && correlation.Metadata.Labels.TryGetValue(SynapseDefaults.Resources.Labels.Correlator, out var correlatorQualifiedName) && correlatorQualifiedName == this.Correlator.Resource.GetQualifiedName()) return true; + try + { + var originalResource = correlation.Clone(); + correlation.Metadata.Labels ??= new Dictionary(); + correlation.Metadata.Labels[SynapseDefaults.Resources.Labels.Correlator] = this.Correlator.Resource.GetQualifiedName(); + var patch = JsonPatchUtility.CreateJsonPatchFromDiff(originalResource, correlation); + correlation = await this.Repository.PatchAsync(new(PatchType.JsonPatch, patch), correlation.GetName(), correlation.GetNamespace(), null, false, cancellationToken).ConfigureAwait(false); + if (correlation.Status?.Phase == null || correlation.Status.Phase == CorrelationStatusPhase.Pending) + { + originalResource = correlation.Clone(); + correlation.Status ??= new(); + correlation.Status.Phase = CorrelationStatusPhase.Active; + correlation.Status.LastModified = DateTimeOffset.Now; + originalResource = await this.Repository.PatchStatusAsync(new(PatchType.JsonPatch, JsonPatchUtility.CreateJsonPatchFromDiff(originalResource, correlation)), correlation.GetName(), correlation.GetNamespace(), null, false, cancellationToken).ConfigureAwait(false); + } + return true; + } + catch (ConcurrencyException) + { + return false; + } + } + + /// + /// Attempts to release the specified + /// + /// The to try to release + /// A + /// A boolean indicating whether or not the instance could be released + protected virtual async Task TryReleaseAsync(Correlation correlation, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(correlation); + if (correlation.Metadata.Labels != null && correlation.Metadata.Labels.TryGetValue(SynapseDefaults.Resources.Labels.Correlator, out var correlatorQualifiedName) && correlatorQualifiedName == this.Correlator.Resource.GetQualifiedName()) return true; + try + { + var originalResource = correlation.Clone(); + if (correlation.Status?.Phase == null || correlation.Status.Phase == CorrelationStatusPhase.Active) + { + correlation.Status ??= new(); + correlation.Status.Phase = CorrelationStatusPhase.Pending; + correlation.Status.LastModified = DateTimeOffset.Now; + originalResource = await this.Repository.PatchStatusAsync(new(PatchType.JsonPatch, JsonPatchUtility.CreateJsonPatchFromDiff(originalResource, correlation)), correlation.GetName(), correlation.GetNamespace(), null, false, cancellationToken).ConfigureAwait(false); + correlation = originalResource.Clone()!; + } + correlation.Metadata.Labels ??= new Dictionary(); + correlation.Metadata.Labels.Remove(SynapseDefaults.Resources.Labels.Correlator); + if (correlation.Metadata.Labels.Count < 1) correlation.Metadata.Labels = null; + var patch = JsonPatchUtility.CreateJsonPatchFromDiff(originalResource, correlation); + correlation = await this.Repository.PatchAsync(new(PatchType.JsonPatch, patch), correlation.GetName(), correlation.GetNamespace(), null, false, cancellationToken).ConfigureAwait(false); + return true; + } + catch (ConcurrencyException) + { + return false; + } + } + + /// + public override async Task StopAsync(CancellationToken cancellationToken) + { + await foreach (var resource in this.Repository.GetAllAsync(labelSelectors: [new LabelSelector(SynapseDefaults.Resources.Labels.Correlator, LabelSelectionOperator.Equals, [this.Correlator.Resource.GetQualifiedName()])], cancellationToken: cancellationToken)) + { + await this.TryReleaseAsync(resource, cancellationToken).ConfigureAwait(false); + } + await base.StopAsync(cancellationToken).ConfigureAwait(false); + } + + /// + protected override async Task OnResourceCreatedAsync(Correlation correlation, CancellationToken cancellationToken = default) + { + await base.OnResourceCreatedAsync(correlation, cancellationToken).ConfigureAwait(false); + if (!await this.TryClaimAsync(correlation, cancellationToken).ConfigureAwait(false)) return; + var correlator = await this.CreateCorrelatorAsync(correlation,cancellationToken).ConfigureAwait(false); + await correlator.HandleAsync(cancellationToken).ConfigureAwait(false); + } + + /// + protected override async Task OnResourceDeletedAsync(Correlation correlation, CancellationToken cancellationToken = default) + { + await base.OnResourceDeletedAsync(correlation, cancellationToken).ConfigureAwait(false); + if (this.Correlators.TryRemove(correlation.GetQualifiedName(), out var handler)) await handler.DisposeAsync().ConfigureAwait(false); + } + + /// + /// Handles changes to the current correlator's subscription selector + /// + /// A key/value mapping of the labels both correlations and correlation instances to select must define + /// A new awaitable + protected virtual Task OnResourceSelectorChangedAsync(IDictionary? selector) => this.ReconcileAsync(this.CancellationTokenSource.Token); + +} diff --git a/src/correlator/Synapse.Correlator/Services/CorrelationHandler.cs b/src/correlator/Synapse.Correlator/Services/CorrelationHandler.cs new file mode 100644 index 000000000..97e41eff0 --- /dev/null +++ b/src/correlator/Synapse.Correlator/Services/CorrelationHandler.cs @@ -0,0 +1,427 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Correlator.Services; + +/// +/// Represents the service used to handle a specific +/// +/// The service used to perform logging +/// The service used to manage resources +/// The service used to stream input and output s +/// The service used to evaluate runtime expressions +/// The service used to serialize/deserialize objects to/from JSON +/// The resource of the correlation to handle +public class CorrelationHandler(ILogger logger, IResourceRepository resources, ICloudEventBus cloudEventBus, IExpressionEvaluator expressionEvaluator, IJsonSerializer serializer, IResourceMonitor correlation) + : IDisposable, IAsyncDisposable +{ + + bool _disposed; + + /// + /// Gets the service used to perform logging + /// + protected ILogger Logger { get; } = logger; + + /// + /// Gets the service used to manage resources + /// + protected IResourceRepository Resources { get; } = resources; + + /// + /// Gets the service used to stream input and output s + /// + protected ICloudEventBus CloudEventBus { get; } = cloudEventBus; + + /// + /// Gets the service used to evaluate runtime expressions + /// + protected IExpressionEvaluator ExpressionEvaluator { get; } = expressionEvaluator; + + /// + /// Gets the service used to serialize/deserialize objects to/from JSON + /// + protected IJsonSerializer Serializer { get; } = serializer; + + /// + /// Gets the service used to monitor the resource of the correlation to manage the scheduling of + /// + protected IResourceMonitor Correlation { get; } = correlation; + + /// + /// Gets the handler's subscription + /// + protected IDisposable? Subscription { get; set; } + + /// + /// Attempt to perform the defined correlation + /// + /// A + /// A new awaitable + public virtual Task HandleAsync(CancellationToken cancellationToken = default) + { + this.Subscription = this.CloudEventBus.InputStream.SubscribeAsync(async e => await this.CorrelateEventAsync(e, cancellationToken).ConfigureAwait(false)); + return Task.CompletedTask; + } + + /// + /// Attempts to correlate the specified input event + /// + /// The event to attempt to correlate + /// A + /// A new awaitable + protected virtual async Task CorrelateEventAsync(CloudEvent e, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(e); + try + { + this.Correlation.Resource.Status ??= new(); + Dictionary? matchingFilters = null; + if (this.Correlation.Resource.Spec.Events.All != null) + { + matchingFilters = (await this.Correlation.Resource.Spec.Events.All!.ToAsyncEnumerable().Select((Filter, Index) => new KeyValuePair(Index, Filter)).WhereAwait(async f => await this.TryFilterEventAsync(f.Value, e, cancellationToken).ConfigureAwait(false)).ToListAsync(cancellationToken).ConfigureAwait(false)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + else if (this.Correlation.Resource.Spec.Events.Any != null) + { + matchingFilters = (await this.Correlation.Resource.Spec.Events.Any!.ToAsyncEnumerable().Select((Filter, Index) => new KeyValuePair(Index, Filter)).WhereAwait(async f => await this.TryFilterEventAsync(f.Value, e, cancellationToken).ConfigureAwait(false)).ToListAsync(cancellationToken).ConfigureAwait(false)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + else if (this.Correlation.Resource.Spec.Events.One != null) + { + if (await this.TryFilterEventAsync(this.Correlation.Resource.Spec.Events.One!, e, cancellationToken).ConfigureAwait(false)) matchingFilters = new() { { 0, this.Correlation.Resource.Spec.Events.One! } }; + } + else throw new Exception("The correlation's event consumption strategy must be set"); + if (matchingFilters == null || matchingFilters.Count < 1) return; + var contextCorrelationResults = await this.Correlation.Resource.Status.Contexts.ToAsyncEnumerable() + .Where(c => c.Status == CorrelationContextStatus.Active) + .SelectAwait(async c => await this.TryCorrelateToContextAsync(c, e, matchingFilters, cancellationToken).ConfigureAwait(false)) + .Where(c => c.Succeeded) + .ToListAsync(cancellationToken).ConfigureAwait(false); + var filter = matchingFilters.First(); + var (Succeeded, CorrelationKeys) = await this.TryExtractCorrelationKeysAsync(e, filter.Value.Correlate, cancellationToken).ConfigureAwait(false); + if (!Succeeded) return; + CorrelationContext? context; + switch (this.Correlation.Resource.Spec.Lifetime) + { + case CorrelationLifetime.Ephemeral: + var contextCorrelationResult = contextCorrelationResults.SingleOrDefault(); + if (contextCorrelationResult == default) + { + this.Logger.LogInformation("Failed to find a matching correlation context"); + if (this.Correlation.Resource.Status.Contexts.Count != 0) throw new Exception("Failed to correlate event: a context with unmatched keys has already been associated with the ephemeral correlation"); + this.Logger.LogInformation("Creating a new correlation context..."); + context = new CorrelationContext() + { + Id = Guid.NewGuid().ToString("N")[..15], + Events = [new(filter.Key, e)], + Keys = CorrelationKeys == null ? new() : new(CorrelationKeys) + }; + this.Logger.LogInformation("Correlation context with id '{contextId}' successfully created", context.Id); + this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", context.Id); + } + else + { + context = contextCorrelationResult.Context.Clone()!; + if (CorrelationKeys != null) + { + foreach (var kvp in CorrelationKeys) + { + if (context.Keys.TryGetValue(kvp.Key, out var value) && !string.IsNullOrWhiteSpace(value)) continue; + context.Keys[kvp.Key] = kvp.Value; + } + } + context.Events[contextCorrelationResult.FilterIndex!] = e; + } + await this.CreateOrUpdateContextAsync(context, cancellationToken).ConfigureAwait(false); + break; + case CorrelationLifetime.Durable: + if (contextCorrelationResults.Count < 1) + { + this.Logger.LogInformation("Failed to find a matching correlation context"); + this.Logger.LogInformation("Creating a new correlation context..."); + context = new CorrelationContext() + { + Id = Guid.NewGuid().ToString("N")[..15], + Events = [new(filter.Key, e)], + Keys = CorrelationKeys == null ? new() : new(CorrelationKeys) + }; + await this.CreateOrUpdateContextAsync(context, cancellationToken).ConfigureAwait(false); + this.Logger.LogInformation("Correlation context with id '{contextId}' successfully created", context.Id); + this.Logger.LogInformation("Event successfully correlated to context with id '{contextId}'", context.Id); + } + else + { + this.Logger.LogInformation("Found {matchingContextCount} matching correlation contexts", contextCorrelationResults.Count); + foreach (var result in contextCorrelationResults) + { + context = result.Context.Clone()!; + if (result.CorrelationKeys != null) + { + foreach (var kvp in result.CorrelationKeys) + { + if (context.Keys.TryGetValue(kvp.Key, out var value) && !string.IsNullOrWhiteSpace(value)) continue; + context.Keys[kvp.Key] = kvp.Value; + } + } + context.Events[result.FilterIndex!] = e; + await this.CreateOrUpdateContextAsync(context, cancellationToken).ConfigureAwait(false); + } + } + break; + default: throw new NotSupportedException($"The specified correlation lifetime '{this.Correlation.Resource.Spec.Lifetime}' is not supported"); + } + } + catch(Exception ex) + { + this.Logger.LogError("An error occurred while attempting to correlate the cloud event with id '{eventId}': {ex}", e.Id, ex.Message); + } + } + + /// + /// Attempts to filter the specified + /// + /// The filter to use + /// The event to filter + /// A + /// A new awaitable + protected virtual async Task TryFilterEventAsync(EventFilterDefinition filter, CloudEvent e, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(filter); + ArgumentNullException.ThrowIfNull(e); + if (filter.With?.Count > 0) + { + foreach(var attribute in filter.With) + { + if (!e.TryGetAttribute(attribute.Key, out var value) || value == null) return false; + if (attribute.Key == CloudEventAttributes.Data && value != null && value is not string) + { + var valueDictionary = attribute.Value.ConvertTo>()!; + foreach(var property in valueDictionary) + { + var valueStr = property.Value.ToString(); + if (valueStr?.IsRuntimeExpression() == true) + { + if (!await this.ExpressionEvaluator.EvaluateConditionAsync(valueStr, e, cancellationToken: cancellationToken)) return false; + } + else if (!string.IsNullOrWhiteSpace(valueStr) && !Regex.IsMatch(value.ToString() ?? string.Empty, valueStr, RegexOptions.IgnoreCase)) return false; + } + } + else + { + var valueStr = attribute.Value.ToString(); + if (valueStr?.IsRuntimeExpression() == true) + { + if (!await this.ExpressionEvaluator.EvaluateConditionAsync(valueStr, e, cancellationToken: cancellationToken)) return false; + } + else if (!string.IsNullOrWhiteSpace(valueStr) && !Regex.IsMatch(value?.ToString() ?? string.Empty, valueStr, RegexOptions.IgnoreCase)) return false; + } + } + } + if (filter.Correlate?.Count > 0) + { + foreach (var keyDefinition in filter.Correlate.Where(k => !k.Value.From.IsRuntimeExpression())) + { + if (!e.TryGetAttribute(keyDefinition.Value.From, out _)) return false; + } + } + return true; + } + + /// + /// Attempts to correlate a to the specified + /// + /// The to attempt to correlate the specified to + /// The to attempt correlating + /// A list containing the filters to match + /// A + /// A boolean indicating whether or not the specified context could be correlated + protected virtual async Task<(CorrelationContext Context, bool Succeeded, IDictionary? CorrelationKeys, int? FilterIndex)> TryCorrelateToContextAsync(CorrelationContext context, CloudEvent e, IDictionary filters, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(e); + ArgumentNullException.ThrowIfNull(filters); + var correlationKeys = new Dictionary(); + foreach (var filter in filters.Where(f => !context.Events.ContainsKey(f.Key))) + { + if (filter.Value.Correlate?.Count > 0) + { + foreach (var keyDefinition in filter.Value.Correlate) + { + var correlationTerm = (keyDefinition.Value.From.IsRuntimeExpression() + ? (await this.ExpressionEvaluator.EvaluateAsync(keyDefinition.Value.From, e, cancellationToken: cancellationToken).ConfigureAwait(false))?.ToString() + : e.GetAttribute(keyDefinition.Value.From)?.ToString()) + ?? string.Empty; + if (string.IsNullOrWhiteSpace(keyDefinition.Value.Expect)) + { + if (context.Keys.TryGetValue(keyDefinition.Key, out var existingCorrelationTerm) && existingCorrelationTerm != correlationTerm) continue; + } + else + { + if (keyDefinition.Value.Expect.IsRuntimeExpression() && !await this.ExpressionEvaluator.EvaluateAsync(keyDefinition.Value.Expect, correlationTerm, cancellationToken: cancellationToken).ConfigureAwait(false)) continue; + else if (!keyDefinition.Value.Expect.Equals(correlationTerm, StringComparison.OrdinalIgnoreCase)) continue; + } + correlationKeys[keyDefinition.Key] = correlationTerm; + return (context, true, correlationKeys, filter.Key); + } + break; + } + else return (context, true, correlationKeys, filter.Key); + } + return (context, false, null, null); + } + + /// + /// Attempts extracting the specified correlation keys from the specified cloud event + /// + /// The cloud event to extract correlation keys from + /// A name/definition mapping of the correlation keys to extract + /// A + /// An object used to describe the result of the operation + protected virtual async Task<(bool Succeeded, IDictionary? CorrelationKeys)> TryExtractCorrelationKeysAsync(CloudEvent e, IDictionary? keyDefinitions, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(e); + var correlationKeys = new Dictionary(); + if (keyDefinitions == null || keyDefinitions.Count < 1) return (true, correlationKeys); + foreach (var keyDefinition in keyDefinitions) + { + var correlationTerm = (keyDefinition.Value.From.IsRuntimeExpression() + ? (await this.ExpressionEvaluator.EvaluateAsync(keyDefinition.Value.From, e, cancellationToken: cancellationToken).ConfigureAwait(false))?.ToString() + : e.GetAttribute(keyDefinition.Value.From)?.ToString()) + ?? string.Empty; + if (!string.IsNullOrWhiteSpace(keyDefinition.Value.Expect)) + { + if (keyDefinition.Value.Expect.IsRuntimeExpression()) + { + if (!await this.ExpressionEvaluator.EvaluateAsync(keyDefinition.Value.Expect, correlationTerm, cancellationToken: cancellationToken).ConfigureAwait(false)) return (false, null); + } + else if (!keyDefinition.Value.Expect.Equals(correlationTerm, StringComparison.OrdinalIgnoreCase)) return (false, null); + } + correlationKeys[keyDefinition.Key] = correlationTerm; + } + return (true, correlationKeys); + } + + /// + /// Creates or updates the specified + /// + /// The to create or update + /// A + /// A new awaitable + protected virtual async Task CreateOrUpdateContextAsync(CorrelationContext context, CancellationToken cancellationToken =default) + { + ArgumentNullException.ThrowIfNull(context); + JsonPatch patch; + var originalResource = this.Correlation.Resource.Clone()!; + var updatedResource = this.Correlation.Resource.Clone()!; + updatedResource.Status ??= new(); + updatedResource.Status.LastModified = DateTimeOffset.Now; + var completed = context.Satisfies(updatedResource.Spec.Events); + var existingContext = updatedResource.Status.Contexts.FirstOrDefault(c => c.Id == context.Id); + if (existingContext == null) + { + if (!completed) updatedResource.Status.Contexts.Add(context); + } + else + { + var index = updatedResource.Status.Contexts.IndexOf(existingContext); + updatedResource.Status.Contexts.Remove(existingContext); + if (!completed) updatedResource.Status.Contexts.Insert(index, context); + } + if (completed) + { + context.Status = CorrelationContextStatus.Completed; + if (updatedResource.Spec.Lifetime == CorrelationLifetime.Ephemeral) + { + updatedResource.Status.Phase = CorrelationStatusPhase.Completed; + this.Subscription?.Dispose(); + } + WorkflowInstance? workflowInstance; + switch (this.Correlation.Resource.Spec.Outcome.Type) + { + case CorrelationOutcomeType.Correlate: + var qualifiedName = this.Correlation.Resource.Spec.Outcome.Correlate!.Instance.Trim().Split('.', StringSplitOptions.RemoveEmptyEntries); + var name = qualifiedName[0]; + var @namespace = qualifiedName[1]; + workflowInstance = await this.Resources.GetAsync(name, @namespace, cancellationToken).ConfigureAwait(false) ?? throw new ProblemDetailsException(ResourceProblemDetails.ResourceNotFound(new ResourceReference(name, @namespace))); + var updatedWorkflowInstance = workflowInstance.Clone()!; + updatedWorkflowInstance.Status ??= new(); + updatedWorkflowInstance.Status.Correlation ??= new(); + updatedWorkflowInstance.Status.Correlation.Contexts ??= []; + updatedWorkflowInstance.Status.Correlation.Contexts[this.Correlation.Resource.Spec.Outcome.Correlate!.Task] = context; + patch = JsonPatchUtility.CreateJsonPatchFromDiff(workflowInstance, updatedWorkflowInstance); + await this.Resources.PatchStatusAsync(new(PatchType.JsonPatch, patch), workflowInstance.GetName(), workflowInstance.GetNamespace(), null, false, cancellationToken).ConfigureAwait(false); + break; + case CorrelationOutcomeType.Start: + var input = this.Correlation.Resource.Spec.Outcome.Start!.Input == null ? [] : await this.ExpressionEvaluator.EvaluateAsync>(this.Correlation.Resource.Spec.Outcome.Start!.Input!, context, cancellationToken: cancellationToken).ConfigureAwait(false); + workflowInstance = new() + { + Metadata = new() + { + Namespace = this.Correlation.Resource.Spec.Outcome.Start!.Workflow.Namespace, + Name = $"{this.Correlation.Resource.Spec.Outcome.Start!.Workflow.Namespace}-" + }, + Spec = new() + { + Definition = this.Correlation.Resource.Spec.Outcome.Start!.Workflow, + Input = input + } + }; + await this.Resources.AddAsync(workflowInstance, false, cancellationToken).ConfigureAwait(false); + break; + default: throw new NotSupportedException($"The specified correlation outcome type is not supported '{this.Correlation.Resource.Spec.Outcome.Type}'"); + } + } + patch = JsonPatchUtility.CreateJsonPatchFromDiff(originalResource, updatedResource); + await this.Resources.PatchStatusAsync(new(PatchType.JsonPatch, patch), originalResource.GetName(), originalResource.GetNamespace(), null, false, cancellationToken).ConfigureAwait(false); + } + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + /// A new awaitable + protected virtual async ValueTask DisposeAsync(bool disposing) + { + if (!disposing || this._disposed) return; + await this.Correlation.DisposeAsync().ConfigureAwait(false); + this.Subscription?.Dispose(); + this._disposed = true; + await Task.CompletedTask.ConfigureAwait(false); + } + + /// + public async ValueTask DisposeAsync() + { + await this.DisposeAsync(true).ConfigureAwait(false); + GC.SuppressFinalize(this); + } + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + protected virtual void Dispose(bool disposing) + { + if (!disposing || this._disposed) return; + this.Correlation.Dispose(); + this.Subscription?.Dispose(); + this._disposed = true; + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Services/CorrelatorApplication.cs b/src/correlator/Synapse.Correlator/Services/CorrelatorApplication.cs new file mode 100644 index 000000000..78bcee67c --- /dev/null +++ b/src/correlator/Synapse.Correlator/Services/CorrelatorApplication.cs @@ -0,0 +1,58 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Correlator.Services; + +/// +/// Represents the correlator's application service +/// +/// The current +public class CorrelatorApplication(IServiceProvider serviceProvider) + : IHostedService, IDisposable +{ + + readonly IServiceScope _scope = serviceProvider.CreateScope(); + IServiceProvider ServiceProvider => this._scope.ServiceProvider; + + CorrelatorController _correlatorController = null!; + CorrelationController _correlationController = null!; + + /// + public async Task StartAsync(CancellationToken cancellationToken) + { + this._correlatorController = this.ServiceProvider.GetRequiredService(); + this._correlationController = this.ServiceProvider.GetRequiredService(); + await this._correlatorController.StartAsync(cancellationToken).ConfigureAwait(false); + await Task.WhenAll( + [ + this._correlatorController.StartAsync(cancellationToken), + this._correlationController.StartAsync(cancellationToken), + ]).ConfigureAwait(false); + } + + /// + public async Task StopAsync(CancellationToken cancellationToken) + { + await Task.WhenAll( + [ + this._correlatorController.StopAsync(cancellationToken), + ]).ConfigureAwait(false); + } + + void IDisposable.Dispose() + { + this._scope.Dispose(); + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Services/CorrelatorController.cs b/src/correlator/Synapse.Correlator/Services/CorrelatorController.cs new file mode 100644 index 000000000..52bc6a975 --- /dev/null +++ b/src/correlator/Synapse.Correlator/Services/CorrelatorController.cs @@ -0,0 +1,95 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Correlator.Services; + +/// +/// Represents the default implementation of the interface +/// +/// The service used to manage s +/// The current +public class CorrelatorController(IResourceRepository repository, IOptionsMonitor options) + : ICorrelatorController +{ + + /// + /// Gets the service used to manage s + /// + protected IResourceRepository Repository { get; } = repository; + + /// + /// Gets the current + /// + protected CorrelatorOptions Options => options.CurrentValue; + + /// + public IResourceMonitor Correlator { get; protected set; } = null!; + + /// + public virtual async Task StartAsync(CancellationToken cancellationToken) + { + Resources.Correlator? correlator = null; + try + { + correlator = await this.Repository.GetAsync(this.Options.Name, this.Options.Namespace, cancellationToken).ConfigureAwait(false); + } + catch (ProblemDetailsException ex) when (ex.Problem.Status == (int)HttpStatusCode.NotFound) { } + finally + { + if (correlator == null) + { + correlator = new Resources.Correlator(new ResourceMetadata(this.Options.Name, this.Options.Namespace), new CorrelatorSpec() + { + + }); + await this.Repository.AddAsync(correlator, false, cancellationToken).ConfigureAwait(false); + } + this.Correlator = await this.Repository.MonitorAsync(this.Options.Name, this.Options.Namespace, false, cancellationToken).ConfigureAwait(false); + await this.SetCorrelatorStatusPhaseAsync(CorrelatorStatusPhase.Running, cancellationToken).ConfigureAwait(false); + this.Correlator.Where(e => e.Type == ResourceWatchEventType.Updated).Select(o => o.Resource.Spec).DistinctUntilChanged().Subscribe(_ => this.OnCorrelatorSpecChanged(), token: cancellationToken); + this.OnCorrelatorSpecChanged(); + } + } + + /// + /// Sets the 's status phase + /// + /// The 's status phase + /// A + /// A new awaitable + protected virtual async Task SetCorrelatorStatusPhaseAsync(string phase, CancellationToken cancellationToken = default) + { + if (this.Correlator.Resource.Status?.Phase == phase) return; + var updatedResource = this.Correlator.Resource.Clone()!; + var originalResource = this.Correlator.Resource.Clone()!; + updatedResource.Status ??= new(); + updatedResource.Status.Phase = phase; + var patch = JsonPatchUtility.CreateJsonPatchFromDiff(originalResource, updatedResource); + await this.Repository.PatchStatusAsync(new(PatchType.JsonPatch, patch), updatedResource.GetName(), updatedResource.GetNamespace(), null, false, cancellationToken).ConfigureAwait(false); + } + + /// + /// Handles changes to the operator spec, and update the operator options accordingly + /// + protected virtual void OnCorrelatorSpecChanged() + { + + } + + /// + public async Task StopAsync(CancellationToken cancellationToken) + { + await this.SetCorrelatorStatusPhaseAsync(CorrelatorStatusPhase.Stopped, cancellationToken).ConfigureAwait(false); + } + +} \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Services/Interfaces/ICorrelatorController.cs b/src/correlator/Synapse.Correlator/Services/Interfaces/ICorrelatorController.cs new file mode 100644 index 000000000..9d6e4bdde --- /dev/null +++ b/src/correlator/Synapse.Correlator/Services/Interfaces/ICorrelatorController.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Correlator.Services; + +/// +/// Defines the fundamentals of the service used to access the current Synapse Correlator +/// +public interface ICorrelatorController + : IHostedService +{ + + /// + /// Gets the service used to monitor the current + /// + IResourceMonitor Correlator { get; } + +} \ No newline at end of file diff --git a/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj b/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj new file mode 100644 index 000000000..c5f664f38 --- /dev/null +++ b/src/correlator/Synapse.Correlator/Synapse.Correlator.csproj @@ -0,0 +1,51 @@ + + + + net8.0 + enable + enable + Exe + en + True + 1.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix) + The Synapse Authors + Cloud Native Computing Foundation + Copyright © 2024-Present The Synapse Authors. All Rights Reserved. + https://github.com/serverlessworkflow/synapse + git + https://github.com/serverlessworkflow/synapse + synapse correlator + en + Apache-2.0 + True + $(VersionPrefix).0 + $(VersionPrefix).0 + embedded + ghcr.io/serverlessworkflow/synapse/correlator + Linux + ..\..\.. + ..\..\..\docker-compose.dcproj + + + + + + + + + + + + + + + + + + + + + diff --git a/src/correlator/Synapse.Correlator/Usings.cs b/src/correlator/Synapse.Correlator/Usings.cs new file mode 100644 index 000000000..66a757269 --- /dev/null +++ b/src/correlator/Synapse.Correlator/Usings.cs @@ -0,0 +1,53 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using Json.Patch; +global using Microsoft.AspNetCore.Diagnostics; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.Controllers; +global using Microsoft.Extensions.Options; +global using Microsoft.OpenApi.Models; +global using Neuroglia; +global using Neuroglia.Data; +global using Neuroglia.Data.Expressions; +global using Neuroglia.Data.Expressions.JavaScript; +global using Neuroglia.Data.Expressions.JQ; +global using Neuroglia.Data.Expressions.Services; +global using Neuroglia.Data.Infrastructure.ResourceOriented; +global using Neuroglia.Data.Infrastructure.ResourceOriented.Configuration; +global using Neuroglia.Data.Infrastructure.ResourceOriented.Services; +global using Neuroglia.Eventing.CloudEvents; +global using Neuroglia.Eventing.CloudEvents.AspNetCore; +global using Neuroglia.Eventing.CloudEvents.Infrastructure; +global using Neuroglia.Eventing.CloudEvents.Infrastructure.Services; +global using Neuroglia.Mediation; +global using Neuroglia.Mediation.AspNetCore; +global using Neuroglia.Reactive; +global using Neuroglia.Security.Services; +global using Neuroglia.Serialization; +global using Neuroglia.Serialization.Json; +global using ServerlessWorkflow.Sdk; +global using ServerlessWorkflow.Sdk.Models; +global using Swashbuckle.AspNetCore.SwaggerUI; +global using Synapse; +global using Synapse.Core.Infrastructure.Services; +global using Synapse.Correlator.Commands.CloudEvents; +global using Synapse.Correlator.Configuration; +global using Synapse.Correlator.Services; +global using Synapse.Resources; +global using System.Diagnostics; +global using System.Net; +global using System.Net.Mime; +global using System.Reactive.Linq; +global using System.Runtime.Serialization; +global using System.Text.RegularExpressions; diff --git a/src/correlator/Synapse.Correlator/appsettings.Development.json b/src/correlator/Synapse.Correlator/appsettings.Development.json new file mode 100644 index 000000000..5da0cca8f --- /dev/null +++ b/src/correlator/Synapse.Correlator/appsettings.Development.json @@ -0,0 +1,13 @@ +{ + "ConnectionStrings": { + "redis": "localhost:6379" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "Namespace": "default", + "Name": "correlator-1" +} diff --git a/src/correlator/Synapse.Correlator/appsettings.json b/src/correlator/Synapse.Correlator/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/src/correlator/Synapse.Correlator/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/ActionContext.cs b/src/dashboard/Synapse.Dashboard.StateManagement/ActionContext.cs new file mode 100644 index 000000000..be974be03 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/ActionContext.cs @@ -0,0 +1,38 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +/// +/// Initializes a new +/// +/// The current +/// The current +/// The action to dispatch +public class ActionContext(IServiceProvider services, IStore store, object action) + : IActionContext +{ + + /// + public IServiceProvider Services { get; } = services; + + /// + public IStore Store { get; } = store; + + /// + public object Action { get; set; } = action; + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/EffectAttribute.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/EffectAttribute.cs new file mode 100644 index 000000000..652404fbc --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/EffectAttribute.cs @@ -0,0 +1,26 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the attribute used to mark a method or all the methods of a type as flux effect +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] +public class EffectAttribute + : Attribute +{ + + + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/FeatureAttribute.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/FeatureAttribute.cs new file mode 100644 index 000000000..2b448cfa7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/FeatureAttribute.cs @@ -0,0 +1,26 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the attribute used to mark a method as a flux feature +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] +public class FeatureAttribute + : Attribute +{ + + + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/ReducerAttribute.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/ReducerAttribute.cs new file mode 100644 index 000000000..a5d0010dd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Attributes/ReducerAttribute.cs @@ -0,0 +1,26 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the attribute used to mark a method or all the methods of a type as flux reducer +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] +public class ReducerAttribute + : Attribute +{ + + + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/ComponentStore.cs b/src/dashboard/Synapse.Dashboard.StateManagement/ComponentStore.cs new file mode 100644 index 000000000..33000d65f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/ComponentStore.cs @@ -0,0 +1,134 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Reactive.Linq; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the base class for all component stores +/// +/// The type of the component store's state +/// +/// Initializes a new +/// +/// The store's initial state +public abstract class ComponentStore(TState state) + : IComponentStore +{ + + readonly BehaviorSubject _subject = new(state); + TState _state = state; + bool _disposed; + + /// + /// Gets the 's + /// + protected CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource(); + + /// + public virtual Task InitializeAsync() => Task.CompletedTask; + + /// + /// Sets the 's state + /// + /// The updated state to set + protected virtual void Set(TState state) + { + this._state = state; + this._subject.OnNext(this._state); + } + + /// + /// Patches the 's state + /// + /// A used to reduce the 's state + protected virtual void Reduce(Func reducer) + { + this.Set(reducer(this._state)); + } + + /// + /// Get the 's state + /// + /// + protected virtual TState Get() + { + return this._state; + } + + /// + /// Get a 's state slice for the provided projection + /// + /// + protected virtual T Get(Func project) + { + return project(this._state); + } + + /// + public virtual IDisposable Subscribe(IObserver observer) => this._subject.Throttle(TimeSpan.FromMicroseconds(1)).Subscribe(observer); + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + protected virtual void Dispose(bool disposing) + { + if (!this._disposed) + { + if (disposing) + { + this.CancellationTokenSource.Cancel(); + this.CancellationTokenSource.Dispose(); + this._subject.Dispose(); + } + this._disposed = true; + } + } + + /// + public void Dispose() + { + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes of the + /// + /// A boolean indicating whether or not the is being disposed of + /// A new awaitable + protected virtual ValueTask DisposeAsync(bool disposing) + { + if (!this._disposed) + { + if (disposing) + { + this.CancellationTokenSource.Cancel(); + this.CancellationTokenSource.Dispose(); + this._subject.Dispose(); + } + this._disposed = true; + } + return ValueTask.CompletedTask; + } + + /// + public async ValueTask DisposeAsync() + { + await this.DisposeAsync(disposing: true); + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/FluxOptions.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/FluxOptions.cs new file mode 100644 index 000000000..414f19928 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/FluxOptions.cs @@ -0,0 +1,80 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; + +namespace Synapse.Dashboard.StateManagement.Configuration; + +/// +/// Represents the options used to configure Flux +/// +public class FluxOptions +{ + + /// + /// Gets/sets a containing the assemblies to scan for Flux components + /// + public virtual List AssembliesToScan { get; set; } = []; + + /// + /// Gets/sets a boolean indicating whether or not to automatically register scanned s and s + /// + public virtual bool AutoRegisterFeatures { get; set; } = true; + + /// + /// Gets/sets a boolean indicating whether or not to automatically register scanned s + /// + public virtual bool AutoRegisterEffects { get; set; } = true; + + /// + /// Gets/sets a boolean indicating whether or not to automatically register scanned s + /// + public virtual bool AutoRegisterMiddlewares { get; set; } = false; + + /// + /// Gets/sets the type of to use + /// + public virtual Type DispatcherType { get; set; } = typeof(Dispatcher); + + /// + /// Gets/sets the type of to use + /// + public virtual Type StoreFactoryType { get; set; } = typeof(StoreFactory); + + /// + /// Gets/sets the type of to use + /// + public virtual Type StoreType { get; set; } = typeof(Store); + + /// + /// Gets/sets a containing the types of the s to use + /// + public virtual List Features { get; set; } = []; + + /// + /// Gets/sets a containing the types of the s to use + /// + public virtual List Middlewares { get; set; } = []; + + /// + /// Gets/sets a containing the types of the s to use + /// + public virtual List Effects { get; set; } = []; + + /// + /// Gets/sets the lifetime of all Flux services + /// + public virtual ServiceLifetime ServiceLifetime { get; set; } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/FluxOptionsBuilder.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/FluxOptionsBuilder.cs new file mode 100644 index 000000000..e7c0a3d89 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/FluxOptionsBuilder.cs @@ -0,0 +1,162 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Copyright © 2021 Neuroglia SPRL. All rights reserved. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; + +namespace Synapse.Dashboard.StateManagement.Configuration +{ + + ///

+ /// Represents the default implementation of the interface + /// + /// + /// Initializes a new + /// + /// The to configure + public class FluxOptionsBuilder(IServiceCollection services) + : IFluxOptionsBuilder + { + + /// + public IServiceCollection Services { get; } = services; + + /// + /// Gets the to configure + /// + protected FluxOptions Options { get; } = new(); + + /// + public virtual IFluxOptionsBuilder ScanAssembly(Assembly assembly) + { + ArgumentNullException.ThrowIfNull(assembly); + this.Options.AssembliesToScan.Add(assembly); + return this; + } + + /// + public virtual IFluxOptionsBuilder AutoRegisterFeatures(bool autoRegister = true) + { + this.Options.AutoRegisterFeatures = autoRegister; + return this; + } + + /// + public virtual IFluxOptionsBuilder AutoRegisterEffects(bool autoRegister = true) + { + this.Options.AutoRegisterEffects = autoRegister; + return this; + } + + /// + public virtual IFluxOptionsBuilder AutoRegisterMiddlewares(bool autoRegister = true) + { + this.Options.AutoRegisterMiddlewares = autoRegister; + return this; + } + + /// + public virtual IFluxOptionsBuilder UseDispatcher(Type dispatcherType) + { + ArgumentNullException.ThrowIfNull(dispatcherType); + if (!typeof(IDispatcher).IsAssignableFrom(dispatcherType)) throw new ArgumentException($"The specified type must implement the {nameof(IDispatcher)} interface", nameof(dispatcherType)); + this.Options.DispatcherType = dispatcherType; + return this; + } + + /// + public virtual IFluxOptionsBuilder UseStoreFactory(Type storeFactoryType) + { + ArgumentNullException.ThrowIfNull(storeFactoryType); + if (!typeof(IStoreFactory).IsAssignableFrom(storeFactoryType)) throw new ArgumentException($"The specified type must implement the {nameof(IStoreFactory)} interface", nameof(storeFactoryType)); + this.Options.StoreFactoryType = storeFactoryType; + return this; + } + + /// + public virtual IFluxOptionsBuilder UseStore(Type storeType) + { + ArgumentNullException.ThrowIfNull(storeType); + if (!typeof(IStore).IsAssignableFrom(storeType)) throw new ArgumentException($"The specified type must implement the {nameof(IStore)} interface", nameof(storeType)); + this.Options.StoreType = storeType; + return this; + } + + /// + public virtual IFluxOptionsBuilder WithServiceLifetime(ServiceLifetime lifetime) + { + this.Options.ServiceLifetime = lifetime; + return this; + } + + /// + public virtual IFluxOptionsBuilder AddFeature(TState state) + { + if (state == null) throw new ArgumentNullException(nameof(state)); + this.Options.Features.Add(new Feature(state)); + return this; + } + + /// + public virtual IFluxOptionsBuilder AddFeature() + where TState : new() + { + return this.AddFeature(new TState()); + } + + /// + public virtual IFluxOptionsBuilder AddMiddleware() + where TMiddleware : IMiddleware + { + this.Options.Middlewares.Add(typeof(TMiddleware)); + return this; + } + + /// + public virtual IFluxOptionsBuilder AddEffect(IEffect effect) + { + ArgumentNullException.ThrowIfNull(effect); + this.Options.Effects.Add(effect); + return this; + } + + /// + public virtual FluxOptions Build() + { + this.Services.AddSingleton(Microsoft.Extensions.Options.Options.Create(this.Options)); + this.Services.Add(new(typeof(IDispatcher), this.Options.DispatcherType, this.Options.ServiceLifetime)); + this.Services.Add(new(typeof(IStoreFactory), this.Options.StoreFactoryType, this.Options.ServiceLifetime)); + this.Services.Add(new(typeof(IStore), provider => provider.GetRequiredService().CreateStore(), this.Options.ServiceLifetime)); + return this.Options; + } + + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/IFluxOptionsBuilder.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/IFluxOptionsBuilder.cs new file mode 100644 index 000000000..71ec15730 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Configuration/IFluxOptionsBuilder.cs @@ -0,0 +1,143 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * Copyright © 2021 Neuroglia SPRL. All rights reserved. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +using Microsoft.Extensions.DependencyInjection; +using System.Reflection; + +namespace Synapse.Dashboard.StateManagement.Configuration +{ + + ///

+ /// Defines the fundamentals of a service used to build + /// + public interface IFluxOptionsBuilder + { + + /// + /// Gets the to configure + /// + IServiceCollection Services { get; } + + /// + /// Configures the to scan the specified in order to find Flux components + /// + /// The to scan + /// The configured + IFluxOptionsBuilder ScanAssembly(Assembly assembly); + + /// + /// Configures the to automatically register scanned s and s + /// + /// A boolean indicating whether or not to automatically register scanned s and s + /// The configured + IFluxOptionsBuilder AutoRegisterFeatures(bool autoRegister = true); + + /// + /// Configures the to automatically register scanned s + /// + /// A boolean indicating whether or not to automatically register scanned s + /// The configured + IFluxOptionsBuilder AutoRegisterEffects(bool autoRegister = true); + + /// + /// Configures the to automatically register scanned s + /// + /// A boolean indicating whether or not to automatically register scanned s + /// The configured + IFluxOptionsBuilder AutoRegisterMiddlewares(bool autoRegister = true); + + /// + /// Configures the to use the specified type + /// + /// The type of the to use + /// The configured + IFluxOptionsBuilder UseDispatcher(Type dispatcherType); + + /// + /// Configures the to use the specified type + /// + /// The type of the to use + /// The configured + IFluxOptionsBuilder UseStoreFactory(Type storeFactoryType); + + /// + /// Configures the to use the specified type + /// + /// The type of the to use + /// The configured + IFluxOptionsBuilder UseStore(Type storeType); + + /// + /// Configures the lifetime of all Flux services + /// + /// The lifetime of all Flux services + /// The configures + IFluxOptionsBuilder WithServiceLifetime(ServiceLifetime lifetime); + + /// + /// Configures the to use the specified + /// + /// The type of the state of the to use + /// The initial state of the to add + /// The configured + IFluxOptionsBuilder AddFeature(TState state); + + /// + /// Configures the to use the specified + /// + /// The type of the state of the to use + /// The configured + IFluxOptionsBuilder AddFeature() + where TState : new(); + + /// + /// Configures the to use the specified + /// + /// The type of to use + /// The configured + IFluxOptionsBuilder AddMiddleware() + where TMiddleware : IMiddleware; + + /// + /// Configures the to use the specified + /// + /// The to add + /// The configured + IFluxOptionsBuilder AddEffect(IEffect effect); + + /// + /// Builds new + /// + /// New + FluxOptions Build(); + + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/DispatchDelegate.cs b/src/dashboard/Synapse.Dashboard.StateManagement/DispatchDelegate.cs new file mode 100644 index 000000000..83ed8f6ce --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/DispatchDelegate.cs @@ -0,0 +1,21 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the delegate method used to dispatch an action +/// +/// The in which to invoke the delegate +/// The result of the dispatched action +public delegate Task DispatchDelegate(IActionContext context); diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Dispatcher.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Dispatcher.cs new file mode 100644 index 000000000..89cfbcae5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Dispatcher.cs @@ -0,0 +1,44 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using System.Reactive.Subjects; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +public class Dispatcher + : IDispatcher +{ + + /// + /// Gets the used to stream actions + /// + protected Subject Stream { get; } = new(); + + /// + public void Dispatch(object action) + { + ArgumentNullException.ThrowIfNull(action); + this.Stream.OnNext(action); + } + + /// + public virtual IDisposable Subscribe(IObserver observer) + { + ArgumentNullException.ThrowIfNull(observer); + return this.Stream.Subscribe(observer); + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Effect.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Effect.cs new file mode 100644 index 000000000..4a7e4ac35 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Effect.cs @@ -0,0 +1,59 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +/// The type of action to apply the effect to +public class Effect + : IEffect +{ + + /// + /// Initializes a new + /// + /// The effect + public Effect(Func effectFunction) + { + if(effectFunction == null) + throw new ArgumentNullException(nameof(effectFunction)); + this.EffectFunction = effectFunction; + } + + /// + /// Gets the effect + /// + protected Func EffectFunction { get; } + + /// + public virtual bool AppliesTo(object action) + { + if(action == null) + throw new ArgumentNullException(nameof(action)); + return action is TAction; + } + + /// + public virtual async Task ApplyAsync(TAction action, IEffectContext context) + { + await this.EffectFunction(action, context); + } + + async Task IEffect.ApplyAsync(object action, IEffectContext context) + { + await this.ApplyAsync((TAction)action, context).ConfigureAwait(false); + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/EffectContext.cs b/src/dashboard/Synapse.Dashboard.StateManagement/EffectContext.cs new file mode 100644 index 000000000..29cbb5a90 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/EffectContext.cs @@ -0,0 +1,40 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +public class EffectContext + : IEffectContext +{ + + /// + /// Initializes a new + /// + /// The current + /// The current + public EffectContext(IServiceProvider services, IDispatcher dispatcher) + { + this.Services = services; + this.Dispatcher = dispatcher; + } + + /// + public IServiceProvider Services { get; } + + /// + public IDispatcher Dispatcher { get; } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IFluxOptionsBuilderExtensions.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IFluxOptionsBuilderExtensions.cs new file mode 100644 index 000000000..71c1f9906 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IFluxOptionsBuilderExtensions.cs @@ -0,0 +1,75 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Dashboard.StateManagement.Configuration; +using System.Reflection; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines extensions for s +/// +public static class IFluxOptionsBuilderExtensions +{ + + /// + /// Configures the to scan the the markup type belongs to + /// + /// The markup type to scan the parent of + /// The to configure + /// The configured + public static IFluxOptionsBuilder ScanMarkupTypeAssembly(this IFluxOptionsBuilder builder) + { + builder.ScanAssembly(typeof(TMarkup).Assembly); + return builder; + } + + /// + /// Configures the to use the specified type + /// + /// The type of to use + /// The to configure + /// The configured + public static IFluxOptionsBuilder UseDispatcher(this IFluxOptionsBuilder builder) + { + builder.UseDispatcher(typeof(TDispatcher)); + return builder; + } + + /// + /// Configures the to use the specified type + /// + /// The type of to use + /// The to configure + /// The configured + public static IFluxOptionsBuilder UseStoreFactory(this IFluxOptionsBuilder builder) + where TFactory : IStoreFactory + { + builder.UseStoreFactory(typeof(TFactory)); + return builder; + } + + /// + /// Configures the to use the specified type + /// + /// The type of to use + /// The to configure + /// The configured + public static IFluxOptionsBuilder UseStore(this IFluxOptionsBuilder builder) + where TStore : class, IStore + { + builder.UseStore(typeof(TStore)); + return builder; + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IServiceCollectionExtensions.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IServiceCollectionExtensions.cs new file mode 100644 index 000000000..7ab450e15 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IServiceCollectionExtensions.cs @@ -0,0 +1,47 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Dashboard.StateManagement.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines extensions for s +/// +public static class IServiceCollectionExtensions +{ + + /// + /// Adds and configures Flux services + /// + /// The to configure + /// An used to setup Flux + /// The configured + public static IServiceCollection AddFlux(this IServiceCollection services, Action setup) + { + var builder = new FluxOptionsBuilder(services); + setup(builder); + builder.Build(); + return services; + } + + + /// + /// Adds and configures Flux services + /// + /// The to configure + /// The configured + public static IServiceCollection AddFlux(this IServiceCollection services) => services.AddFlux(_ => { }); + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IStoreExtensions.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IStoreExtensions.cs new file mode 100644 index 000000000..3f87d6fa3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Extensions/IStoreExtensions.cs @@ -0,0 +1,73 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + + +/// +/// Defines extensions for s +/// +public static class IStoreExtensions +{ + + /// + /// Adds a new to the store + /// + /// The type of state managed by the to add + /// The to add the to + /// The value of the to add + /// An array that contains the s to initialize the to add with + public static void AddFeature(this IStore store, TState state, params IReducer[] reducers) + { + var feature = new Feature(state); + foreach (var reducer in reducers) + { + feature.AddReducer(reducer); + } + store.AddFeature(feature); + } + + /// + /// Adds a new to the store + /// + /// The type of state of the to add + /// The to add the to + /// An array that contains the s to initialize the to add with + public static void AddFeature(this IStore store, params IReducer[] reducers) + { + store.AddFeature(Activator.CreateInstance(), reducers); + } + + /// + /// Adds a new to the store + /// + /// The type of to add + /// The to add the to + public static void AddMiddleware(this IStore store) + where TMiddleware : IMiddleware + { + store.AddMiddleware(typeof(TMiddleware)); + } + + /// + /// Adds a new to the store + /// + /// The type of to add + /// The to add the to + public static void AddEffect(this IStore store) + where TEffect : IEffect + { + store.AddEffect(Activator.CreateInstance()); + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Feature.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Feature.cs new file mode 100644 index 000000000..5c734d7e8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Feature.cs @@ -0,0 +1,118 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia; +using System.Reactive.Subjects; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +/// The type of the 's state +public class Feature + : IFeature +{ + + /// + /// Initializes a new + /// + /// The 's value + public Feature(TState value) + { + if(value == null) + throw new ArgumentNullException(nameof(value)); + this.Stream = new(value); + } + + /// + public TState State + { + get => this.Stream.Value; + set + { + this.Stream.OnNext(value); + } + } + + object IFeature.State => this.State!; + + /// + /// Gets the used to stream the changes + /// + protected BehaviorSubject Stream { get; } + + /// + /// Gets a containing the type/s mappings + /// + protected Dictionary>> Reducers { get; } = []; + + /// + public virtual void AddReducer(IReducer reducer) + { + ArgumentNullException.ThrowIfNull(reducer); + var genericReducerType = reducer.GetType().GetGenericType(typeof(IReducer<,>)) ?? throw new Exception($"The specified {nameof(IReducer)} '{reducer.GetType()}' does not implement the '{typeof(IReducer<,>)}' interface"); + var actionType = genericReducerType.GetGenericArguments()[1]; + if (this.Reducers.TryGetValue(actionType, out var reducers)) reducers.Add(reducer); + else this.Reducers.Add(actionType, [reducer]); + } + + void IFeature.AddReducer(IReducer reducer) + { + ArgumentNullException.ThrowIfNull(reducer); + this.AddReducer((IReducer)reducer); + } + + /// + public virtual IDisposable Subscribe(IObserver observer) =>this.Stream.Subscribe(observer); + + /// + public virtual bool ShouldReduceStateFor(object action) + { + ArgumentNullException.ThrowIfNull(action); + return this.Reducers.ContainsKey(action.GetType()); + } + + /// + public virtual async Task ReduceStateAsync(IActionContext context, Func reducerPipelineBuilder) + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(reducerPipelineBuilder); + var pipeline = reducerPipelineBuilder(ApplyReducersAsync); + this.State = (TState)await pipeline(context); + } + + /// + /// Applies all the matching the specified + /// + /// The to apply the s to + /// The reduced 's state + protected virtual async Task ApplyReducersAsync(IActionContext context) + { + return (context == null + ? throw new ArgumentNullException(nameof(context)) + : (object?)await Task.Run(() => + { + var newState = this.State; + if (this.Reducers.TryGetValue(context.Action.GetType(), out var reducers)) + { + foreach (var reducer in reducers) + { + newState = reducer.Reduce(newState, context.Action); + } + } + return newState; + }))!; + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IActionContext.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IActionContext.cs new file mode 100644 index 000000000..bb161079c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IActionContext.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a Flux action context +/// +public interface IActionContext +{ + + /// + /// Gets the current + /// + IServiceProvider Services { get; } + + /// + /// Gets the current + /// + IStore Store { get; } + + /// + /// Gets/sets the Flux action to dispatch + /// + object Action { get; set; } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IComponentStore.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IComponentStore.cs new file mode 100644 index 000000000..056ac5fb5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IComponentStore.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a service used to store a component's state +/// +public interface IComponentStore + : IObservable, IDisposable, IAsyncDisposable +{ + + /// + /// Initializes the + /// + /// A new awaitable + Task InitializeAsync(); + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IDispatcher.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IDispatcher.cs new file mode 100644 index 000000000..393b5cbc8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IDispatcher.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a service used to dispatch Flux actions +/// +public interface IDispatcher + : IObservable +{ + + /// + /// Dispatches the specified action + /// + /// The action to dispatch + void Dispatch(object action); + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IEffect.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IEffect.cs new file mode 100644 index 000000000..b1167f738 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IEffect.cs @@ -0,0 +1,55 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of an effect +/// +public interface IEffect +{ + + /// + /// Determines whether or not to apply the effect + /// + /// THe action to apply the effect to + /// A boolean indicating whether or not to apply the effect to the specified action + bool AppliesTo(object action); + + /// + /// Applies the effect to the specified action + /// + /// The action to apply the effect to + /// The context + /// A new awaitable + Task ApplyAsync(object action, IEffectContext context); + +} + +/// +/// Defines the fundamentals of an effect +/// +/// The type of the action to apply the effect to +public interface IEffect + : IEffect +{ + + /// + /// Applies the effect to the specified action + /// + /// The action to apply the effect to + /// The context + /// A new awaitable + Task ApplyAsync(TAction action, IEffectContext context); + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IEffectContext.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IEffectContext.cs new file mode 100644 index 000000000..a6c7105cd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IEffectContext.cs @@ -0,0 +1,32 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of an context +/// +public interface IEffectContext +{ + + /// + /// Gets the current + /// + IServiceProvider Services { get; } + + /// + /// Gets the current + /// + IDispatcher Dispatcher { get; } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IFeature.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IFeature.cs new file mode 100644 index 000000000..76c2fb2d4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IFeature.cs @@ -0,0 +1,69 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a Flux feature +/// +public interface IFeature +{ + + /// + /// Gets the 's state + /// + object State { get; } + + /// + /// Determines whether or not the defines s for the specified action + /// + /// The action to get check for s + /// A boolean indicating whether or not the 's state is reduced by the specified action + bool ShouldReduceStateFor(object action); + + /// + /// Adds the specified to the + /// + /// The to add + void AddReducer(IReducer reducer); + + /// + /// Reduces the 's state + /// + /// The in which to reduce the feature + /// A used to build the reducer pîpeline + /// A new awaitable + Task ReduceStateAsync(IActionContext context, Func reducerPipelineBuilder); + +} + +/// +/// Defines the fundamentals of a Flux feature +/// +/// The type of the 's state +public interface IFeature + : IFeature, IObservable +{ + + /// + /// Gets the 's state + /// + new TState State { get; } + + /// + /// Adds the specified to the + /// + /// The to add + void AddReducer(IReducer reducer); + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IMiddleware.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IMiddleware.cs new file mode 100644 index 000000000..3d7cd92c5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IMiddleware.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a middleware +/// +public interface IMiddleware +{ + + /// + /// Invokes the + /// + /// The in which to invoke the + /// The result of the dispatched Flux action + Task InvokeAsync(IActionContext context); + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IReducer.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IReducer.cs new file mode 100644 index 000000000..3f8f6c66a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IReducer.cs @@ -0,0 +1,67 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a Flux reducer +/// +public interface IReducer +{ + + /// + /// Reduces the specified state, for the specified action + /// + /// The state to reduce + /// The Flux action the reducer applies to + /// A new object that represents the reduced state + object Reduce(object state, object action); + +} + +/// +/// Defines the fundamentals of a Flux reducer +/// +/// The type of state to reduce +public interface IReducer + : IReducer +{ + + /// + /// Reduces the specified state, for the specified action + /// + /// The state to reduce + /// The Flux action the reducer applies to + /// A new object that represents the reduced state + TState Reduce(TState state, object action); + +} + +/// +/// Defines the fundamentals of a Flux reducer +/// +/// The type of state to reduce +/// The type of flux action the reducer applies to +public interface IReducer + : IReducer +{ + + /// + /// Reduces the specified state, for the specified action + /// + /// The state to reduce + /// The Flux action the reducer applies to + /// A new object that represents the reduced state + TState Reduce(TState state, TAction action); + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IState.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IState.cs new file mode 100644 index 000000000..7f612d1cf --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IState.cs @@ -0,0 +1,61 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a Flux state +/// +public interface IState +{ + + /// + /// Gets the 's value + /// + object Value { get; } + + /// + /// Attempts to dispatch the specified Flux action + /// + /// The Flux action to dispatch + /// A boolean indicating whether or not the Flux action could be dispatched + bool TryDispatch(object action); + + /// + /// Adds an to the state + /// + /// The to add + void AddReducer(IReducer reducer); + +} + +/// +/// Defines the fundamentals of a Flux state +/// +/// The type of the 's value +public interface IState + : IState, IObservable +{ + + /// + /// Gets the 's value + /// + new TState Value { get; } + + /// + /// Adds an to the state + /// + /// The to add + void AddReducer(IReducer reducer); + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IStore.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IStore.cs new file mode 100644 index 000000000..6408d501f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IStore.cs @@ -0,0 +1,53 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Defines the fundamentals of a Flux store +/// +public interface IStore + : IObservable +{ + + /// + /// Gets the 's state + /// + object State { get; } + + /// + /// Adds a new to the store + /// + /// The type of state managed by the to add + /// The to add + void AddFeature(IFeature feature); + + /// + /// Adds a new to the store + /// + void AddMiddleware(Type middlewareType); + + /// + /// Adds a new to the store + /// + /// The to add + void AddEffect(IEffect effect); + + /// + /// Gets the of the specified type + /// + /// The type of state managed by the to get + /// The with the specified state type + IFeature GetFeature(); + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IStoreFactory.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IStoreFactory.cs new file mode 100644 index 000000000..8b2cfd78d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Interfaces/IStoreFactory.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + + +/// +/// Defines the fundamentals of a service used to create s +/// +public interface IStoreFactory +{ + + /// + /// Creates a new + /// + /// A new + IStore CreateStore(); + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Reducer.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Reducer.cs new file mode 100644 index 000000000..f971bcb9c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Reducer.cs @@ -0,0 +1,50 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +/// The type of state to reduce +/// The type of flux action the reducer applies to +/// +/// Initializes a new +/// +/// The function used to reduce the state +public class Reducer(Func reduceFunction) + : IReducer +{ + + /// + /// Gets the function used to reduce the state + /// + protected Func ReduceFunction { get; } = reduceFunction; + + /// + public TState Reduce(TState state, TAction action) + { + return this.ReduceFunction(state, action); + } + + TState IReducer.Reduce(TState state, object action) + { + return this.Reduce(state, (TAction)action); + } + + object IReducer.Reduce(object state, object action) + { + return this.Reduce((TState)state, (TAction)action)!; + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/State.cs b/src/dashboard/Synapse.Dashboard.StateManagement/State.cs new file mode 100644 index 000000000..f418c961e --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/State.cs @@ -0,0 +1,81 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia; +using System.Reactive.Subjects; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +/// The type of the 's value +public class State + : IState +{ + + /// + /// Initializes a new + /// + /// The 's value + public State(TState value) + { + if(value == null) throw new ArgumentNullException(nameof(value)); + this.Value = value; + } + + /// + public TState Value { get; protected set; } + + object IState.Value => this.Value!; + + /// + /// Gets the used to stream state values + /// + protected Subject Stream { get; } = new(); + + /// + /// Gets a containing the type/s mappings + /// + protected Dictionary>> Reducers { get; } = []; + + /// + public virtual void AddReducer(IReducer reducer) + { + ArgumentNullException.ThrowIfNull(reducer); + var reducerGenericType = reducer.GetType().GetGenericType(typeof(IReducer<,>)) ?? throw new Exception($"The specified {nameof(IReducer)} '{reducer.GetType()}' does not implement the '{typeof(IReducer<,>)}' interface"); + var actionType = reducerGenericType.GetGenericArguments()[1]; + if (this.Reducers.TryGetValue(actionType, out var reducers)) reducers.Add(reducer); + else this.Reducers.Add(actionType, [reducer]); + } + + void IState.AddReducer(IReducer reducer) + { + ArgumentNullException.ThrowIfNull(reducer); + this.AddReducer((IReducer)reducer); + } + + /// + public virtual IDisposable Subscribe(IObserver observer) => this.Stream.Subscribe(observer); + + /// + public virtual bool TryDispatch(object action) + { + ArgumentNullException.ThrowIfNull(action); + if (!this.Reducers.TryGetValue(action.GetType(), out var reducers))return false; + foreach(var reducer in reducers) this.Value = reducer.Reduce(this.Value, action); + this.Stream.OnNext(this.Value!); + return true; + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Store.cs b/src/dashboard/Synapse.Dashboard.StateManagement/Store.cs new file mode 100644 index 000000000..876ae8384 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Store.cs @@ -0,0 +1,188 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Neuroglia.Reactive; +using System.Reactive.Subjects; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +public class Store + : IStore +{ + + /// + /// Initializes a new + /// + /// The current + /// The service used to perform logging + /// The service used to dispatch actions + public Store(IServiceProvider serviceProvider, ILogger logger, IDispatcher dispatcher) + { + this.ServiceProvider = serviceProvider; + this.Logger = logger; + this.Dispatcher = dispatcher; + this.Dispatcher.SubscribeAsync(this.DispatchAsync); + } + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider { get; } + + /// + /// Gets the service used to perform logging + /// + protected ILogger Logger { get; } + + /// + /// Gets the service used to dispatch actions + /// + protected IDispatcher Dispatcher { get; } + + /// + /// Gets the used to stream actions + /// + protected BehaviorSubject Stream { get; } = new(default!); + + /// + /// Gets a containing the 's s + /// + protected List Features { get; } = []; + + /// + /// Gets a containing the types of the 's s + /// + protected List Middlewares { get; } = []; + + /// + /// Gets a containing the 's s + /// + protected List Effects { get; } = []; + + /// + public virtual object State => this.Features.ToDictionary(f => f.State.GetType().Name, f => f.State); + + IDisposable IObservable.Subscribe(IObserver observer) + { + return observer == null ? throw new ArgumentNullException(nameof(observer)) : this.Stream.Subscribe(observer); + } + + /// + public virtual void AddFeature(IFeature feature) + { + ArgumentNullException.ThrowIfNull(feature); + this.Features.Add(feature); + feature.Subscribe(this.OnNextState); + } + + /// + public virtual void AddMiddleware(Type middlewareType) + { + this.Middlewares.Add(middlewareType); + } + + /// + public virtual void AddEffect(IEffect effect) + { + ArgumentNullException.ThrowIfNull(effect); + this.Effects.Add(effect); + } + + /// + public virtual IFeature GetFeature() + { + return (IFeature)this.Features.First(f => f.State.GetType() == typeof(TState)); + } + + /// + /// Dispatches the specified action + /// + /// The action to dispatch + protected virtual async Task DispatchAsync(object action) + { + try + { + DispatchDelegate pipelineBuilder(DispatchDelegate reducer) => this.Middlewares + .AsEnumerable() + .Reverse() + .Aggregate(reducer, (next, type) => this.InstantiateMiddleware(type, next).InvokeAsync); + var context = new ActionContext(this.ServiceProvider, this, action); + foreach (var feature in this.Features + .Where(f => f.ShouldReduceStateFor(action))) + { + await feature.ReduceStateAsync(context, pipelineBuilder); + } + this.OnApplyEffects(action); + } + catch(Exception ex) + { + this.Logger.LogError("An error occurred while dispatching an action of type '{actionType}': {ex}", action.GetType().Name, ex.ToString()); + throw; + } + } + + /// + /// Creates a new instance of the specified + /// + /// The type of to instantiate + /// The next in the pipeline + /// A new + protected virtual IMiddleware InstantiateMiddleware(Type type, DispatchDelegate next) + { + var constructor = type.GetConstructor([]); + if (constructor != null) return (IMiddleware)constructor.Invoke([]); + var parameters = new List(1); + constructor = type.GetConstructors().First(); + if (constructor.GetParameters().Any(p => p.ParameterType == typeof(DispatchDelegate))) parameters.Add(next); + return (IMiddleware)ActivatorUtilities.CreateInstance(this.ServiceProvider, type, [.. parameters]); + } + + /// + /// Applies s + /// + /// The action to apply s to + protected virtual void OnApplyEffects(object action) + { + var applyEffectTasks = new List(); + foreach (var effect in this.Effects) applyEffectTasks.Add(effect.ApplyAsync(action, new EffectContext(this.ServiceProvider, this.Dispatcher))); + var exceptions = new List(); + Task.Run(async () => + { + try + { + await Task.WhenAll(applyEffectTasks); + } + catch(Exception ex) + { + exceptions.Add(ex); + } + if(exceptions.Count != 0) throw new AggregateException(exceptions); + }); + } + + /// + /// Handles the next feature value + /// + /// The type of to handle the next value for + /// The next value + protected virtual void OnNextState(TState feature) + { + this.Stream.OnNext(feature!); + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/StoreFactory.cs b/src/dashboard/Synapse.Dashboard.StateManagement/StoreFactory.cs new file mode 100644 index 000000000..678f0c603 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/StoreFactory.cs @@ -0,0 +1,184 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Dashboard.StateManagement.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Neuroglia; +using System.Linq.Expressions; +using System.Reflection; + +namespace Synapse.Dashboard.StateManagement; + +/// +/// Represents the default implementation of the interface +/// +/// +/// Initializes a new +/// +/// The current +/// The current +public class StoreFactory(IServiceProvider serviceProvider, IOptions fluxOptions) + : IStoreFactory +{ + + static readonly MethodInfo AddFeatureMethod = typeof(IStoreExtensions).GetMethods().First(m => m.Name == nameof(IStoreExtensions.AddFeature) && m.GetParameters().Length == 2); + + /// + /// Gets the current + /// + protected IServiceProvider ServiceProvider { get; } = serviceProvider; + + /// + /// Gets the current + /// + protected FluxOptions FluxOptions { get; } = fluxOptions.Value; + + /// + public virtual IStore CreateStore() + { + var store = (IStore)ActivatorUtilities.CreateInstance(this.ServiceProvider, this.FluxOptions.StoreType); + this.ConfigureStoreFeatures(store); + this.ConfigureStoreEffects(store); + this.ConfigureStoreMiddlewares(store); + return store; + } + + /// + /// Finds and configures s for the specified + /// + /// The to add s for + protected virtual void ConfigureStoreFeatures(IStore store) + { + ArgumentNullException.ThrowIfNull(store); + foreach (var feature in this.FluxOptions.Features) store.AddFeature(feature); + if (!this.FluxOptions.AutoRegisterFeatures) return; + var reducersPerState = this.FindAndMapReducersPerState(); + foreach (var stateType in TypeCacheUtil.FindFilteredTypes("nflux-features", + t => t.IsClass && !t.IsAbstract && !t.IsInterface && !t.IsGenericType && t.TryGetCustomAttribute(out _))) + { + if (!reducersPerState.TryGetValue(stateType, out var reducersPerFeature)) continue; + AddFeatureMethod.MakeGenericMethod(stateType).Invoke(null, [store, reducersPerFeature.OfType(typeof(IReducer<>).MakeGenericType(stateType)).OfType().ToArray()]); + } + } + + /// + /// Finds and configures s for the specified + /// + /// The to add s for + protected virtual void ConfigureStoreEffects(IStore store) + { + ArgumentNullException.ThrowIfNull(store); + foreach (var effect in this.FluxOptions.Effects) store.AddEffect(effect); + if (!this.FluxOptions.AutoRegisterEffects)return; + foreach (var effectDeclaringType in TypeCacheUtil.FindFilteredTypes("nflux-effects", + t => t.TryGetCustomAttribute< EffectAttribute>(out _) || t.GetMethods().Any(m => m.TryGetCustomAttribute(out _)), this.FluxOptions.AssembliesToScan?.ToArray()!)) + { + foreach (var effectMethod in effectDeclaringType.GetMethods() + .Where(m => (effectDeclaringType.TryGetCustomAttribute(out _) && m.IsStatic && m.GetParameters().Length == 2 && m.GetParameters()[1].ParameterType == typeof(IEffectContext)) || m.TryGetCustomAttribute(out _))) + { + if(effectMethod.ReturnType != typeof(Task)) throw new Exception($"The method '{effectMethod.Name}' in type '{effectMethod.DeclaringType!.FullName}' must return a task to be used as a Flux effect"); + if (!effectMethod.IsStatic) throw new Exception($"The method '{effectMethod.Name}' in type '{effectMethod.DeclaringType!.FullName}' must be static to be used as a Flux effect"); + if (effectMethod.GetParameters().Length != 2) throw new Exception($"The method '{effectMethod.Name}' in type '{effectMethod.DeclaringType!.FullName}' must declare exactly 2 parameters to be used as a Flux effect"); + var actionType = effectMethod.GetParameters()[0].ParameterType; + var effectType = typeof(Effect<>).MakeGenericType(actionType); + var effectLambda = this.BuildEffectLambda(actionType, effectMethod); + var effectFunction = effectLambda.Compile(); + var effect = (IEffect)ActivatorUtilities.CreateInstance(this.ServiceProvider, effectType, effectFunction); + store.AddEffect(effect); + } + } + } + + /// + /// Finds and configures s for the specified + /// + /// The to add s for + protected virtual void ConfigureStoreMiddlewares(IStore store) + { + ArgumentNullException.ThrowIfNull(store); + foreach (var middlewareType in this.FluxOptions.Middlewares) store.AddMiddleware(middlewareType); + if (!this.FluxOptions.AutoRegisterMiddlewares) return; + foreach (var middlewareType in TypeCacheUtil.FindFilteredTypes("nflux-middlewares", + t => t.IsClass && !t.IsInterface && !t.IsAbstract && !t.IsGenericType && typeof(IMiddleware).IsAssignableFrom(t), this.FluxOptions.AssembliesToScan?.ToArray()!)) + { + store.AddMiddleware(middlewareType); + } + } + + /// + /// Finds and maps scanned s per state type + /// + /// A new containing scanned s mapped by type + protected virtual IDictionary> FindAndMapReducersPerState() + { + var reducersPerState = new Dictionary>(); + foreach (var reducerDeclaringType in TypeCacheUtil.FindFilteredTypes("nflux-reducers", + t => t.TryGetCustomAttribute(out _) || t.GetMethods().Any(m => m.TryGetCustomAttribute(out _)), this.FluxOptions.AssembliesToScan?.ToArray()!)) + { + foreach (var reducerMethod in reducerDeclaringType.GetMethods() + .Where(m => (reducerDeclaringType.TryGetCustomAttribute(out _) && m.IsStatic && m.GetParameters().Length == 2 && m.ReturnType == m.GetParameters()[0].ParameterType) || m.TryGetCustomAttribute(out _))) + { + if (!reducerMethod.IsStatic) throw new Exception($"The method '{reducerMethod.Name}' in type '{reducerMethod.DeclaringType!.FullName}' must be static to be used as a Flux reducer"); + if (reducerMethod.GetParameters().Length != 2) throw new Exception($"The method '{reducerMethod.Name}' in type '{reducerMethod.DeclaringType!.FullName}' must declare exactly 2 parameters to be used as a Flux reducer"); + if (reducerMethod.ReturnType != reducerMethod.GetParameters()[0].ParameterType) throw new Exception($"The method '{reducerMethod.Name}' in type '{reducerMethod.DeclaringType!.FullName}' must return a type matching its first parameter's to be used as a Flux reducer"); + var stateType = reducerMethod.GetParameters()[0].ParameterType; + var actionType = reducerMethod.GetParameters()[1].ParameterType; + var reducerType = typeof(Reducer<,>).MakeGenericType(stateType, actionType); + var reducerLambda = this.BuildReducerLambda(stateType, actionType, reducerMethod); + var reducerFunction = reducerLambda.Compile(); + var reducer = (IReducer)ActivatorUtilities.CreateInstance(this.ServiceProvider, reducerType, reducerFunction); + if (reducersPerState.TryGetValue(stateType, out var reducers)) reducers.Add(reducer); + else reducersPerState.Add(stateType, [reducer]); + } + } + return reducersPerState; + } + + /// + /// Builds a new reducer for the specified state type, action type and reducer method + /// + /// The type of state to create the reducer for + /// The type of action to create the reducer for + /// The reducer method + /// A new reducer + protected virtual LambdaExpression BuildReducerLambda(Type stateType, Type actionType, MethodInfo reducerMethod) + { + ArgumentNullException.ThrowIfNull(stateType); + ArgumentNullException.ThrowIfNull(actionType); + ArgumentNullException.ThrowIfNull(reducerMethod); + var stateParam = Expression.Parameter(stateType, "state"); + var actionParam = Expression.Parameter(actionType, "action"); + var methodCall = Expression.Call(null, reducerMethod, stateParam, actionParam); + var lambda = Expression.Lambda(methodCall, stateParam, actionParam); + return lambda; + } + + /// + /// Builds a new effect for the specified action type and reducer method + /// + /// The type of action to create the effect for + /// The effect method + /// A new effect + protected virtual LambdaExpression BuildEffectLambda(Type actionType, MethodInfo effectMethod) + { + ArgumentNullException.ThrowIfNull(actionType); + ArgumentNullException.ThrowIfNull(effectMethod); + var actionParam = Expression.Parameter(actionType, "action"); + var contextParam = Expression.Parameter(typeof(IEffectContext), "context"); + var methodCall = Expression.Call(null, effectMethod, actionParam, contextParam); + var lambda = Expression.Lambda(methodCall, actionParam, contextParam); + return lambda; + } + +} diff --git a/src/dashboard/Synapse.Dashboard.StateManagement/Synapse.Dashboard.StateManagement.csproj b/src/dashboard/Synapse.Dashboard.StateManagement/Synapse.Dashboard.StateManagement.csproj new file mode 100644 index 000000000..527d6709f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard.StateManagement/Synapse.Dashboard.StateManagement.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + en + True + + + + + + + + + + + + + diff --git a/src/dashboard/Synapse.Dashboard/App.razor b/src/dashboard/Synapse.Dashboard/App.razor index 75d210bac..9b67e929c 100644 --- a/src/dashboard/Synapse.Dashboard/App.razor +++ b/src/dashboard/Synapse.Dashboard/App.razor @@ -1,12 +1,12 @@ -@* - Copyright © 2022-Present The Synapse Authors -

+@* + Copyright © 2024-Present The Synapse Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -

+ http://www.apache.org/licenses/LICENSE-2.0 -

+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,68 +14,66 @@ limitations under the License. *@ -@using Microsoft.AspNetCore.SignalR.Client -@using Neuroglia.Data.Flux -@using Neuroglia.Data.Flux.Components -@using System.Reactive.Linq -@using Newtonsoft.Json.Linq -@using Synapse.Dashboard.CloudEvents -@using Synapse.Integration.Events.Correlations -@using Synapse.Integration.Events.WorkflowActivities -@using Synapse.Integration.Events.WorkflowInstances -@using Synapse.Integration.Events.Workflows @implements IDisposable -@inject IServiceProvider ServiceProvider -@inject HubConnection HubConnection -@inject IIntegrationEventStream IntegrationEventStream -@inject IDispatcher Dispatcher; -@inject Neuroglia.Mapping.IMapper Mapper +@inject IApplicationLayout Layout - - - - - - - - Not found - -

Sorry, there's nothing at this address.

- - - + + + + @RouterWithLayout(typeof(MainLayout)) + + + @RouterWithLayout(typeof(EmptyLayout)) + + + @code { - - private readonly List subscriptions = new(); + // see https://github.com/dotnet/aspnetcore/issues/39456 + RenderFragment RouterWithLayout(Type layoutType) => __builder => + { + + + Synapse - @Layout.Title?.ChildContent + + + @if (context.User.Identity?.IsAuthenticated != true) + { + + } + else + { +

You are not authorized to access this resource.

+ } +
+
+ +
+ + Not found + +

Sorry, there's nothing at this address.

+
+
+
+ }; protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - this.InitializeCloudEventSubscriptions(); - await this.HubConnection.StartAsync(); + this.Layout.PropertyChanged += this.OnLayoutPropertyChanged; } - public void InitializeCloudEventSubscriptions() + void OnLayoutPropertyChanged(object? sender, PropertyChangedEventArgs e) { - foreach(var subscriptionType in TypeCacheUtil.FindFilteredTypes("syndash:ce-subs", t => t.IsClass && !t.IsAbstract && !t.IsInterface && typeof(ICloudEventSubscription).IsAssignableFrom(t))) - { - var subscription = (ICloudEventSubscription)ActivatorUtilities.CreateInstance(this.ServiceProvider, subscriptionType); - var handle = this.IntegrationEventStream - .Where(subscription.Filters) - .Subscribe(subscription.Handle); - this.subscriptions.Add(handle); - } + this.StateHasChanged(); } - public virtual void Dispose() + public void Dispose() { - foreach (var subscription in this.subscriptions.ToList()) - { - subscription.Dispose(); - this.subscriptions.Remove(subscription); - } + this.Layout.PropertyChanged -= this.OnLayoutPropertyChanged; + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/CloudEventSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/CloudEventSubscription.cs deleted file mode 100644 index 9780c3a32..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/CloudEventSubscription.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents -{ - - /// - /// Represents the default generic implementation of the - /// - /// The type of the to subscribe to - public abstract class CloudEventSubscription - : ICloudEventSubscription - where TEntity : Entity - where TEvent : V1IntegrationEvent - { - - /// - /// Initializes a new - /// - /// The service used to create s - /// The service used to dispatch Flux actions - /// The service used to map objects - protected CloudEventSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - { - this.EventType = Synapse.CloudEvents.TypeOf(); - this.Logger = loggerFactory.CreateLogger(this.GetType()); - this.Dispatcher = dispatcher; - this.Mapper = mapper; - } - - /// - /// Gets the type of s the filters - /// - protected string EventType { get; } - - /// - /// Gets the service used to perform logging - /// - protected ILogger Logger { get; } - - /// - /// Gets the service used to dispatch Flux actions - /// - protected IDispatcher Dispatcher { get; } - - /// - /// Gets the service used to map objects - /// - protected IMapper Mapper { get; } - - /// - public virtual bool Filters(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - return e.Type.Equals(this.EventType, StringComparison.OrdinalIgnoreCase); - } - - /// - public virtual void Handle(V1Event e) - { - if (e == null) - throw new ArgumentNullException(nameof(e)); - this.Handle(e.Data.ToObject()); - } - - public abstract void Handle(TEvent e); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/ICloudEventSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/ICloudEventSubscription.cs deleted file mode 100644 index 4dcf926fc..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/ICloudEventSubscription.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents -{ - - ///

- /// Defines the fundamentals of a subscription - /// - public interface ICloudEventSubscription - { - - /// - /// Attempts to filter the specified - /// - /// The to attempt to filter - /// A boolean indicating whether or not the specified has been filtered - bool Filters(V1Event e); - - /// - /// Handles the specified - /// - /// The to handle - void Handle(V1Event e); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1ContextAddedToCorrelationSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1ContextAddedToCorrelationSubscription.cs deleted file mode 100644 index bbe7a0614..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1ContextAddedToCorrelationSubscription.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.Correlations; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.Correlations -{ - - class V1ContextAddedToCorrelationSubscription - : CloudEventSubscription - { - - public V1ContextAddedToCorrelationSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1ContextAddedToCorrelationIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new AddContextToV1Correlation(e.AggregateId, e.Context)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1CorrelationContextReleasedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1CorrelationContextReleasedSubscription.cs deleted file mode 100644 index 60a6b06c1..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1CorrelationContextReleasedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.Correlations; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.Correlations -{ - class V1CorrelationContextReleasedSubscription - : CloudEventSubscription - { - - public V1CorrelationContextReleasedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1CorrelationContextReleasedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new RemoveContextFromV1Correlation(e.AggregateId, e.ContextId)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1EventCorrelatedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1EventCorrelatedSubscription.cs deleted file mode 100644 index ba2e9120c..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Correlations/V1EventCorrelatedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.Correlations; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.Correlations -{ - class V1EventCorrelatedSubscription - : CloudEventSubscription - { - - public V1EventCorrelatedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1EventCorrelatedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new CorrelateV1Event(e.AggregateId, e.ContextId, e.Event)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/FunctionDefinitionCollections/V1FunctionDefinitionCollectionCreatedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/FunctionDefinitionCollections/V1FunctionDefinitionCollectionCreatedSubscription.cs deleted file mode 100644 index d5072eca4..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/FunctionDefinitionCollections/V1FunctionDefinitionCollectionCreatedSubscription.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Dashboard.Pages.Resources.Collections; -using Synapse.Integration.Events.FunctionDefinitionCollections; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.FunctionDefinitionCollections -{ - class V1FunctionDefinitionCollectionCreatedSubscription - : CloudEventSubscription - { - - public V1FunctionDefinitionCollectionCreatedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1FunctionDefinitionCollectionCreatedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new AddV1FunctionDefinitionCollection(new() - { - Id = e.AggregateId, - CreatedAt = e.CreatedAt, - LastModified = e.CreatedAt, - Name = e.Name, - Version = e.Version, - Description = e.Description, - Functions = e.Functions - })); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - - class V1FunctionDefinitionCollectionDeletedSubscription - : CloudEventSubscription - { - - public V1FunctionDefinitionCollectionDeletedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1FunctionDefinitionCollectionDeletedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new RemoveV1FunctionDefinitionCollection(e.AggregateId)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCancelledSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCancelledSubscription.cs deleted file mode 100644 index a068085f5..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCancelledSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowActivityCancelledSubscription - : CloudEventSubscription - { - - public V1WorkflowActivityCancelledSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivityCancelledIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowActivityAsCancelled(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCompensatedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCompensatedSubscription.cs deleted file mode 100644 index a5c6e9732..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCompensatedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowActivityCompensatedSubscription - : CloudEventSubscription - { - - public V1WorkflowActivityCompensatedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivityCompensatedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowActivityAsCompensated(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCompensatingSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCompensatingSubscription.cs deleted file mode 100644 index d750ba0b8..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCompensatingSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowActivityCompensatingSubscription - : CloudEventSubscription - { - - public V1WorkflowActivityCompensatingSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivityCompensatingIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowActivityAsCompensating(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCreatedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCreatedSubscription.cs deleted file mode 100644 index 7d4310a90..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityCreatedSubscription.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - - class V1WorkflowActivityCreatedSubscription - : CloudEventSubscription - { - - public V1WorkflowActivityCreatedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivityCreatedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new AddV1WorkflowActivity(this.Mapper.Map(e))); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityExecutedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityExecutedSubscription.cs deleted file mode 100644 index bccdc6252..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityExecutedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowActivityExecutedSubscription - : CloudEventSubscription - { - - public V1WorkflowActivityExecutedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivityExecutedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowActivityAsExecuted(e.AggregateId, e.CreatedAt, e.Status, e.Error, e.Output)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityFaultedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityFaultedSubscription.cs deleted file mode 100644 index 6641f6700..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityFaultedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowActivityFaultedSubscription - : CloudEventSubscription - { - - public V1WorkflowActivityFaultedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivityFaultedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowActivityAsFaulted(e.AggregateId, e.CreatedAt, e.Error)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityStartedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityStartedSubscription.cs deleted file mode 100644 index fab45e1ba..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivityStartedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowActivityStartedSubscription - : CloudEventSubscription - { - - public V1WorkflowActivityStartedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivityStartedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowActivityAsStarted(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivitySuspendedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivitySuspendedSubscription.cs deleted file mode 100644 index 5ed24b5dc..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowActivities/V1WorkflowActivitySuspendedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowActivitySuspendedSubscription - : CloudEventSubscription - { - - public V1WorkflowActivitySuspendedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowActivitySuspendedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowActivityAsSuspended(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCancelledSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCancelledSubscription.cs deleted file mode 100644 index 01801e7d6..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCancelledSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceCancelledSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceCancelledSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceCancelledIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsCancelled(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCancellingSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCancellingSubscription.cs deleted file mode 100644 index 0e11226b4..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCancellingSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceCancellingSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceCancellingSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceCancellingIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsCancelling(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCompletedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCompletedSubscription.cs deleted file mode 100644 index 2d30487ef..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCompletedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceCompletedSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceCompletedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceCompletedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowAsCompleted(e.AggregateId, e.CreatedAt, e.Output)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCreatedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCreatedSubscription.cs deleted file mode 100644 index 1553c8454..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceCreatedSubscription.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - - class V1WorkflowInstanceCreatedSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceCreatedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceCreatedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new AddV1WorkflowInstance(this.Mapper.Map(e))); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceDeletedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceDeletedSubscription.cs deleted file mode 100644 index 867bf0c9f..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceDeletedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceDeletedSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceDeletedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceDeletedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new DeleteV1WorkflowInstance(e.AggregateId)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceFaultedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceFaultedSubscription.cs deleted file mode 100644 index 67574afae..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceFaultedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceFaultedSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceFaultedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceFaultedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsFaulted(e.AggregateId, e.CreatedAt, e.Error)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceResumingSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceResumingSubscription.cs deleted file mode 100644 index b0a621ceb..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceResumingSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceResumingSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceResumingSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceResumingIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsResuming(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceStartedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceStartedSubscription.cs deleted file mode 100644 index 4d6fb9e90..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceStartedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceStartedSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceStartedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceStartedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsStarted(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceStartingSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceStartingSubscription.cs deleted file mode 100644 index c6495ab6a..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceStartingSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceStartingSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceStartingSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceStartingIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsStarting(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceSuspendedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceSuspendedSubscription.cs deleted file mode 100644 index a591e5ff8..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceSuspendedSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceSuspendedSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceSuspendedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceSuspendedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsSuspended(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceSuspendingSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceSuspendingSubscription.cs deleted file mode 100644 index e4cb4c941..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/WorkflowInstances/V1WorkflowInstanceSuspendingSubscription.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.WorkflowInstances -{ - class V1WorkflowInstanceSuspendingSubscription - : CloudEventSubscription - { - - public V1WorkflowInstanceSuspendingSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowInstanceSuspendingIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new MarkV1WorkflowInstanceAsSuspending(e.AggregateId, e.CreatedAt)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Workflows/V1WorkflowCreatedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Workflows/V1WorkflowCreatedSubscription.cs deleted file mode 100644 index 298d0380f..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Workflows/V1WorkflowCreatedSubscription.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.Workflows; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.Workflows -{ - - class V1WorkflowCreatedSubscription - : CloudEventSubscription - { - - public V1WorkflowCreatedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowCreatedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new AddV1Workflow(this.Mapper.Map(e))); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Workflows/V1WorkflowDeletedSubscription.cs b/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Workflows/V1WorkflowDeletedSubscription.cs deleted file mode 100644 index a0cb0adf5..000000000 --- a/src/dashboard/Synapse.Dashboard/CloudEvents/Subscriptions/Workflows/V1WorkflowDeletedSubscription.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Synapse.Integration.Events.Workflows; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.CloudEvents.Subscriptions.Workflows -{ - class V1WorkflowDeletedSubscription - : CloudEventSubscription - { - - public V1WorkflowDeletedSubscription(ILoggerFactory loggerFactory, IDispatcher dispatcher, IMapper mapper) - : base(loggerFactory, dispatcher, mapper) - { - - } - - public override void Handle(V1WorkflowDeletedIntegrationEvent e) - { - try - { - this.Dispatcher.Dispatch(new RemoveV1Workflow(e.AggregateId)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/Breadcrumb.razor b/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/Breadcrumb.razor new file mode 100644 index 000000000..4cdc74354 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/Breadcrumb.razor @@ -0,0 +1,64 @@ +@* + Copyright © 2024-Present The Synapse Authors +

+ Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at +

+ http://www.apache.org/licenses/LICENSE-2.0 +

+ Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@inject IBreadcrumbManager BreadcrumbService +@implements IDisposable +

+ +@code { + [Parameter] public string? Class { get; set; } + + protected virtual string? ClassNames => Class; + + protected override void OnInitialized() + { + base.OnInitialized(); + this.BreadcrumbService.PropertyChanged += this.OnBreadcrumbChanged; + } + + protected void OnBreadcrumbChanged(object? sender, PropertyChangedEventArgs e) => this.StateHasChanged(); + + public void Dispose() + { + if (this.BreadcrumbService != null) this.BreadcrumbService.PropertyChanged -= this.OnBreadcrumbChanged; + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/BreadcrumbItem.cs b/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/BreadcrumbItem.cs new file mode 100644 index 000000000..bd5c4bf77 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/BreadcrumbItem.cs @@ -0,0 +1,67 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a breadcrumb element +/// +/// +/// Initializes a new with the provided data +/// +public class BreadcrumbItem +{ + + /// + /// Initializes a new with the provided data + /// + /// The breadcrumb's label + /// The link associated to the breadcrumb + /// The breadcrumb's icon, if any + public BreadcrumbItem(string label, string link, string? icon = null) + { + Label = label; + Link = link; + Icon = icon; + } + + /// + /// Initializes a new with the provided template + /// + /// The breadcrumb's template + public BreadcrumbItem(RenderFragment template) + { + Template = template; + } + + /// + /// Gets the breadcrumb's label, if any + /// + public string? Label { get; } + + /// + /// Gets the link associated to the breadcrumb, if any + /// + public string? Link { get; } + + /// + /// Gets the breadcrumb's icon, if any + /// + public string? Icon { get; } + + /// + /// Get the breadcrumb's , if any + /// + public RenderFragment? Template { get; } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/Breadcrumbs.cs b/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/Breadcrumbs.cs new file mode 100644 index 000000000..091e41174 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/Breadcrumb/Breadcrumbs.cs @@ -0,0 +1,56 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse.Dashboard.Components; + +/// +/// Exposes the default breadcrumbs for all pages of the application +/// +public static class Breadcrumbs +{ + /// + /// Holds the breadcrumb items for related routes + /// + public static BreadcrumbItem[] Workflows = [new("Workflows", "/workflows")]; + /// + /// Holds the breadcrumb items for related routes + /// + public static BreadcrumbItem[] WorkflowInstances = [new("Workflows Instances", "/workflow-instances")]; + /// + /// Holds the breadcrumb items for the user profile related routes + /// + public static BreadcrumbItem[] UserProfile = [new("User Profile", "/users/profile")]; + /// + /// Holds the breadcrumb items for related routes + /// + public static BreadcrumbItem[] Operators = [new("Operators", "/operators")]; + /// + /// Holds the breadcrumb items for related routes + /// + public static BreadcrumbItem[] Namespaces = [new("Namespaces", "/operators")]; + /// + /// Holds the breadcrumb items for related routes + /// + public static BreadcrumbItem[] Correlators = [new("Correlators", "/correlators")]; + /// + /// Holds the breadcrumb items for related routes + /// + public static BreadcrumbItem[] Correlations = [new("Correlations", "/correlations")]; + /// + /// Holds the breadcrumb items for about related routes + /// + public static BreadcrumbItem[] About = [new("About", "/about")]; + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/CorrelationContextDetails/CorrelationContextDetails.razor b/src/dashboard/Synapse.Dashboard/Components/CorrelationContextDetails/CorrelationContextDetails.razor new file mode 100644 index 000000000..da6d31127 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/CorrelationContextDetails/CorrelationContextDetails.razor @@ -0,0 +1,29 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@inject IJsonSerializer JsonSerializer + +@if (CorrelationContext != null) +{ +
+        @JsonSerializer.SerializeToText(CorrelationContext);
+        
+} + +@code { + [Parameter] public CorrelationContext? CorrelationContext { get; set; } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/DocumentDetails.razor b/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/DocumentDetails.razor new file mode 100644 index 000000000..570f834f8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/DocumentDetails.razor @@ -0,0 +1,147 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@using Synapse.Dashboard.Components.DocumentDetailsStateManagement +@inherits StatefulComponent + +
+ @if (LabelTemplate != null) { + @LabelTemplate + } + else { + @Label + } + @if (Store.Collapse != null) + { + + } +
+ + @if (isExpanded) // prevents early rendering when collapsed + { + @if (reference == null && Document == null) + { +

No document

+ } + else if (!loaded) + { + + } + else + { +
+ +
+ + } + @if (problemDetails != null) + { +
+ + @problemDetails.Detail + + @if (problemDetails.Errors != null && problemDetails.Errors.Any()) + { + foreach (KeyValuePair errorContainer in problemDetails.Errors) + { + +
    + @foreach (string error in errorContainer.Value) + { +
  • @error
  • + } +
+
+ } + } +
+ } + } + else // mocks some content to enable opening animation + { +
+ } +
+@code { + + protected string? ClassNames => Class; + [Parameter] public string? Class { get; set; } + /// + /// The label to display + /// + [Parameter] public string? Label { get; set; } + /// + /// The label to display + /// + [Parameter] public RenderFragment? LabelTemplate { get; set; } + /// + /// The reference of the document to load + /// + [Parameter] public string? Reference { get; set; } + /// + /// The reference of the document to display + /// + [Parameter] public object? Document { get; set; } + + /// + /// The state of the + /// + bool isExpanded = false; + /// + /// The internal reference + /// + string? reference; + /// + /// The internal boolean indicating if the resource already loaded + /// + bool loaded = false; + /// + /// The that occurred when trying to save the resource, if any + /// + ProblemDetails? problemDetails = null; + + object? __documentShadow; + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync().ConfigureAwait(false); + Store.Reference.Subscribe(value => OnStateChanged(_ => reference = value), token: CancellationTokenSource.Token); + Store.Loaded.Subscribe(value => OnStateChanged(_ => loaded = value), token: CancellationTokenSource.Token); + Store.IsExpanded.Subscribe(value => OnStateChanged(_ => isExpanded = value), token: CancellationTokenSource.Token); + Store.ProblemDetails.Subscribe(value => OnStateChanged(c_mp => problemDetails = value), token: CancellationTokenSource.Token); + } + + /// + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + await this.Store.HideAsync(); + if (Document != __documentShadow) + { + Store.SetDocument(Document); + __documentShadow = Document; + } + if (reference != Reference) + { + Store.SetReference(Reference); + } + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/State.cs b/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/State.cs new file mode 100644 index 000000000..40af13524 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/State.cs @@ -0,0 +1,71 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components.DocumentDetailsStateManagement; + +/// +/// Represents the state of a +/// +public record DocumentDetailsState +{ + + /// + /// Gets/sets the label to display + /// + public string Label { get; set; } = string.Empty; + + /// + /// Gets/sets the reference to load + /// + public string? Reference { get; set; } = string.Empty; + + /// + /// Gets/sets the JSON representation of the referenced document + /// + public string DocumentJson { get; set; } = string.Empty; + + /// + /// Gets/sets a boolean indicating the reference has been loaded + /// + public bool Loaded { get; set; } = false; + + /// + /// Gets/sets a boolean indicating if the logs panel is expanded + /// + public bool IsExpanded { get; set; } = false; + + /// + /// Gets/sets the type that occurred when trying to save the resource, if any + /// + public Uri? ProblemType { get; set; } = null; + + /// + /// Gets/sets the title that occurred when trying to save the resource, if any + /// + public string ProblemTitle { get; set; } = string.Empty; + + /// + /// Gets/sets the details that occurred when trying to save the resource, if any + /// + public string ProblemDetail { get; set; } = string.Empty; + + /// + /// Gets/sets the status that occurred when trying to save the resource, if any + /// + public int ProblemStatus { get; set; } = 0; + + /// + /// Gets/sets the list of errors that occurred when trying to save the resource, if any + /// + public EquatableDictionary ProblemErrors { get; set; } = new EquatableDictionary(); +} diff --git a/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/Store.cs b/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/Store.cs new file mode 100644 index 000000000..d864bc26f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/DocumentDetails/Store.cs @@ -0,0 +1,387 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Services; +using Synapse.Dashboard.Components.WorkflowInstanceLogsStateManagement; + +namespace Synapse.Dashboard.Components.DocumentDetailsStateManagement; + +/// +/// Represents the of a +/// +/// The service used interact with Synapse API +/// The service used from JS interop +/// The service used ease Monaco Editor interactions +/// The service used to serialize and deserialize JSON +/// The service used to serialize and deserialize YAML +public class DocumentDetailsStore( + ISynapseApiClient apiClient, + IJSRuntime jsRuntime, + IMonacoEditorHelper monacoEditorHelper, + IJsonSerializer jsonSerializer, + IYamlSerializer yamlSerializer +) + : ComponentStore(new()) +{ + + private TextModel? _textModel = null; + private readonly string _textModelUri = monacoEditorHelper.GetResourceUri(); + + /// + /// Gets the service used to interact with the Synapse API + /// + protected ISynapseApiClient ApiClient { get; } = apiClient; + /// + /// Gets the service used for JS interop + /// + protected IJSRuntime JSRuntime { get; } = jsRuntime; + + /// + /// Gets the service used ease Monaco Editor interactions + /// + protected IMonacoEditorHelper MonacoEditorHelper { get; } = monacoEditorHelper; + + /// + /// Gets the service used to serialize and deserialize JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the service used to serialize and deserialize YAML + /// + protected IYamlSerializer YamlSerializer { get; } = yamlSerializer; + + /// + /// The provider function + /// + public Func StandaloneEditorConstructionOptions = monacoEditorHelper.GetStandaloneEditorConstructionOptions(string.Empty, true, monacoEditorHelper.PreferredLanguage); + + /// + /// The reference + /// + public StandaloneCodeEditor? TextEditor { get; set; } + + /// + /// Gets/sets the logs panel + /// + public Collapse? Collapse { get; set; } + + #region Selectors + /// + /// Gets an used to observe changes + /// + public IObservable Label => this.Select(state => state.Label).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Reference => this.Select(state => state.Reference).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable DocumentJson => this.Select(state => state.DocumentJson).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Loaded => this.Select(state => state.Loaded).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable IsExpanded => this.Select(state => state.IsExpanded).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemType => this.Select(state => state.ProblemType).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemTitle => this.Select(state => state.ProblemTitle).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemDetail => this.Select(state => state.ProblemDetail).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemStatus => this.Select(state => state.ProblemStatus).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable> ProblemErrors => this.Select(state => state.ProblemErrors).DistinctUntilChanged(); + + /// + /// Gets an used to observe computed + /// + public IObservable ProblemDetails => Observable.CombineLatest( + this.ProblemType, + this.ProblemTitle, + this.ProblemStatus, + this.ProblemDetail, + this.ProblemErrors, + (type, title, status, details, errors) => + { + if (string.IsNullOrWhiteSpace(title)) + { + return null; + } + return new ProblemDetails(type ?? new Uri("unknown://"), title, status, details, null, errors, null); + } + ); + #endregion + + #region Setters + /// + /// Sets the state's + /// + /// The new value + public void SetLabel(string label) + { + this.Reduce(state => state with + { + Label = label + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetReference(string? reference) + { + var documentJson = this.Get(state => state.DocumentJson); + this.Reduce(state => state with + { + Reference = reference, + DocumentJson = reference != null ? string.Empty : documentJson, + Loaded = false, + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetDocument(object? document) + { + try + { + var documentJson = document != null ? this.JsonSerializer.SerializeToText(document) : string.Empty; + this.Reduce(state => state with + { + DocumentJson = documentJson, + Loaded = true, + }); + } + catch (Exception ex) + { + // todo: handle ex + Console.WriteLine(ex.ToString()); + } + } + #endregion + + #region Actions + /// + /// Toggles the panel + /// + public async Task ToggleAsync() + { + if (this.Collapse != null) + { + var isExpanded = !this.Get(state => state.IsExpanded); + await (isExpanded ? this.Collapse.ShowAsync() : this.Collapse.HideAsync()); + this.Reduce(state => state with + { + IsExpanded = isExpanded + }); + } + } + + /// + /// Toggles the panel + /// + public async Task HideAsync() + { + if (this.Collapse != null) + { + await this.Collapse.HideAsync(); + this.Reduce(state => state with + { + IsExpanded = false + }); + } + } + + /// + /// Loads the referenced documents + /// + /// + public async Task LoadReferencedDocumentAsync() + { + var reference = this.Get(state => state.Reference); + var loaded = this.Get(state => state.Loaded); + if (loaded) return; + if (string.IsNullOrWhiteSpace(reference)) + { + this.Reduce(state => state with + { + Loaded = true + }); + return; + } + try + { + var document = await this.ApiClient.Documents.GetAsync(reference); + string documentText = this.JsonSerializer.SerializeToText(document.Content); + this.Reduce(state => state with + { + DocumentJson = documentText, + Loaded = true + }); + } + catch (ProblemDetailsException ex) + { + if (ex.Problem != null) + { + this.Reduce(state => state with + { + ProblemType = ex.Problem?.Type, + ProblemTitle = ex.Problem?.Title ?? string.Empty, + ProblemStatus = ex.Problem?.Status ?? 0, + ProblemDetail = ex.Problem?.Detail ?? string.Empty, + ProblemErrors = new EquatableDictionary(ex.Problem?.Errors ?? new Dictionary()) + }); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + } + + /// + /// Handles changed of the text editor's language + /// + /// + /// + public async Task ToggleTextBasedEditorLanguageAsync(string _) + { + await this.OnTextBasedEditorInitAsync(); + } + + /// + /// Handles initialization of the text editor + /// + /// + public async Task OnTextBasedEditorInitAsync() + { + await this.SetTextBasedEditorLanguageAsync(); + await this.SetTextEditorValueAsync(); + } + + /// + /// Sets the language of the text editor + /// + /// + public async Task SetTextBasedEditorLanguageAsync() + { + try + { + var language = this.MonacoEditorHelper.PreferredLanguage; + if (this.TextEditor != null) + { + this._textModel = await Global.GetModel(this.JSRuntime, this._textModelUri); + this._textModel ??= await Global.CreateModel(this.JSRuntime, "", language, this._textModelUri); + await Global.SetModelLanguage(this.JSRuntime, this._textModel, language); + await this.TextEditor!.SetModel(this._textModel); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + } + + /// + /// Changes the value of the text editor + /// + /// + async Task SetTextEditorValueAsync() + { + var document = this.Get(state => state.DocumentJson); + var language = this.MonacoEditorHelper.PreferredLanguage; + if (this.TextEditor != null && !string.IsNullOrWhiteSpace(document)) + { + try + { + if (language == PreferredLanguage.YAML) + { + document = this.YamlSerializer.ConvertFromJson(document); + } + await this.TextEditor.SetValue(document); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + await this.MonacoEditorHelper.ChangePreferredLanguageAsync(language == PreferredLanguage.YAML ? PreferredLanguage.JSON : PreferredLanguage.YAML); + } + } + } + #endregion + + /// + public override Task InitializeAsync() + { + this.DocumentJson.SubscribeAsync(async (_) => { + await this.SetTextEditorValueAsync(); + }, cancellationToken: this.CancellationTokenSource.Token); + return base.InitializeAsync(); + } + + private bool disposed; + /// + /// Disposes of the store + /// + /// A boolean indicating whether or not the dispose of the store + protected override void Dispose(bool disposing) + { + if (!this.disposed) + { + if (disposing) + { + if (this._textModel != null) + { + this._textModel.DisposeModel(); + this._textModel = null; + } + if (this.TextEditor != null) + { + this.TextEditor.Dispose(); + this.TextEditor = null; + } + } + this.disposed = true; + } + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/HorizontalCollapsible/HorizontalCollapsible.razor b/src/dashboard/Synapse.Dashboard/Components/HorizontalCollapsible/HorizontalCollapsible.razor new file mode 100644 index 000000000..4ee9727f7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/HorizontalCollapsible/HorizontalCollapsible.razor @@ -0,0 +1,80 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ +@namespace Synapse.Dashboard.Components + +
+
+ + @if(isCollapsed) + { + + } + else + { + + } + + @Label + @if (OnClose.HasDelegate) + { + + } +
+ @if (!isCollapsed) + { +
+ @Content +
+ } +
+ +@code { + private bool isCollapsed = false; + + protected string? ClassNames => Class; + [Parameter] public string? Class { get; set; } + [Parameter] public RenderFragment Label { get; set; } = default!; + [Parameter] public RenderFragment Content { get; set; } = default!; + [Parameter] public EventCallback OnShown { get; set; } + [Parameter] public EventCallback OnHidden { get; set; } + [Parameter] public EventCallback OnClose { get; set; } + + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + } + + async Task OnToggleAsync() + { + isCollapsed = !isCollapsed; + if (isCollapsed && OnHidden.HasDelegate) + { + await OnHidden.InvokeAsync(); + } + if (!isCollapsed && OnShown.HasDelegate) + { + await OnShown.InvokeAsync(); + } + } + + async Task OnCloseAsync() + { + if (OnClose.HasDelegate) + { + await OnClose.InvokeAsync(); + } + } + } diff --git a/src/dashboard/Synapse.Dashboard/Components/Loader/Loader.razor b/src/dashboard/Synapse.Dashboard/Components/Loader/Loader.razor new file mode 100644 index 000000000..be14574c1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/Loader/Loader.razor @@ -0,0 +1,23 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components + +
+
+ Loading... +
+
\ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorHelper.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorHelper.cs new file mode 100644 index 000000000..fbcd7d105 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorHelper.cs @@ -0,0 +1,73 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a delegate that is used to handle events related to changes in a user's preferred language. +/// +/// The new preferred language. +/// A task representing the asynchronous operation of handling the event. +public delegate Task PreferredLanguageChangedEventHandler(string newLanguage); + +/// +/// Represents a service used to facilitate the Monaco editor configuration +/// +public interface IMonacoEditorHelper +{ + /// + /// The preferred editor language + /// + string PreferredLanguage { get; } + + /// + /// Emits when the editor language changes + /// + event PreferredLanguageChangedEventHandler? PreferredLanguageChanged; + + /// + /// A function used to facilitate the construction of + /// + /// The text of the editor + /// Defines if the editor should be in read only + /// The default preferred language + /// A function used to build + Func GetStandaloneEditorConstructionOptions(string value = "", bool readOnly = false, string language = "json"); + + /// + /// A function used to facilitate the construction of + /// + /// Defines if the editor should be in read only + /// A function used to build + Func GetDiffEditorConstructionOptions(bool readOnly = true); + + /// + /// Changes the preferred editor language + /// + /// The new language to use + /// A task representing the asynchronous operation + Task ChangePreferredLanguageAsync(string language); + + /// + /// Returns the number of created and increases the count + /// + /// + int GetNextModelIndex(); + + /// + /// Generates a unique resource URI for + /// + /// + string GetResourceUri(string resourceType = "unknown") { return $"inmemory://{resourceType}-{GetNextModelIndex()}"; } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorMarker.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorMarker.cs new file mode 100644 index 000000000..7a3cab800 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorMarker.cs @@ -0,0 +1,86 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + + +/// +/// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html +/// +public interface IMonacoEditorMarker +{ + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#code + /// + public string? Code { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#endColumn + /// + public int EndColumn { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#endLineNumber + /// + public int EndLineNumber { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#message + /// + public string Message { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#modelVersionId + /// + public string? ModelVersionId { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#owner + /// + public string Owner { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#relatedInformation + /// + public IEnumerable? RelatedInformation { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#resource + /// + public Object Resource { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#severity + /// + public int Severity { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#source + /// + public string? Source { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#startColumn + /// + public int StartColumn { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#startLineNumber + /// + public int StartLineNumber { get; set; } + + /// + /// See https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IMarker.html#tags + /// + public IEnumerable? Tags { get; set; } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditor.razor b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditor.razor new file mode 100644 index 000000000..25a2af9cf --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditor.razor @@ -0,0 +1,87 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@using Synapse.Dashboard.Components.MonacoEditorStateManagement +@inherits StatefulComponent + +
+ +
+ + +@code { + /// + /// A boolean indicating the editor is read-only + /// + [Parameter] public bool IsReadOnly { get; set; } = false; + /// + /// The document to display + /// + [Parameter] public object? Document { get; set; } + /// + /// The JSON representation of the document to display + /// + [Parameter] public string? DocumentJson { get; set; } + /// + /// The YAML representation of the document to display + /// + [Parameter] public string? DocumentYaml { get; set; } + /// + /// The see called when the text changes + /// + [Parameter] public EventCallback OnTextChanged { get; set; } + /// + /// The document's model name, if any + /// + [Parameter] public string ModelName { get; set; } = string.Empty; + + /// + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + Store.SetIsReadOnly(IsReadOnly); + if (!string.IsNullOrWhiteSpace(ModelName)) + { + Store.SetTexModelName(ModelName); + } + if (Document != null) + { + Store.SetDocument(Document); + } + if (!string.IsNullOrWhiteSpace(DocumentJson)) + { + Store.SetDocumentJson(DocumentJson); + } + if (!string.IsNullOrWhiteSpace(DocumentYaml)) + { + Store.SetDocumentYaml(DocumentYaml); + } + } + + async Task OnDidChangeModelContent(ModelContentChangedEvent e) + { + if (OnTextChanged.HasDelegate) + { + await OnTextChanged.InvokeAsync(await Store.TextEditor!.GetValue()); + } + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorHelper.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorHelper.cs new file mode 100644 index 000000000..129d49946 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorHelper.cs @@ -0,0 +1,83 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +public class MonacoEditorHelper + : IMonacoEditorHelper +{ + private int _modelCount = 0; + + /// + public string PreferredLanguage { get; protected set; } = "yaml"; + + /// + public event PreferredLanguageChangedEventHandler? PreferredLanguageChanged; + + /// + public Func GetStandaloneEditorConstructionOptions(string value = "", bool readOnly = false, string language = "yaml") { + return (StandaloneCodeEditor editor) => new StandaloneEditorConstructionOptions + { + Theme = "vs-dark", + AutomaticLayout = true, + Minimap = new EditorMinimapOptions { Enabled = false }, + Language = language, + ReadOnly = readOnly, + Value = value, + TabSize = 2, + QuickSuggestions = new QuickSuggestionsOptions + { + Other = "true", + Strings = "true" + } + }; + } + + /// + public Func GetDiffEditorConstructionOptions(bool readOnly = true) + { + return (StandaloneDiffEditor editor) => new DiffEditorConstructionOptions + { + AutomaticLayout = true, + Minimap = new EditorMinimapOptions { Enabled = false }, + ReadOnly = readOnly + }; + } + + /// + public async Task ChangePreferredLanguageAsync(string language) + { + if (!string.IsNullOrEmpty(language) && language != this.PreferredLanguage) + { + this.PreferredLanguage = language; + await this.OnPreferredLanguageChangeAsync(language); + } + } + + /// + protected async Task OnPreferredLanguageChangeAsync(string language) + { + if (this.PreferredLanguageChanged != null) + { + await this.PreferredLanguageChanged.Invoke(language); + } + await Task.CompletedTask; + } + + /// + public int GetNextModelIndex() { + this._modelCount++; + return this._modelCount; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorMarker.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorMarker.cs new file mode 100644 index 000000000..de2f05a50 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorMarker.cs @@ -0,0 +1,46 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +public class MonacoEditorMarker + : IMonacoEditorMarker +{ + /// + public string? Code { get; set; } + /// + public int EndColumn { get; set; } + /// + public int EndLineNumber { get; set; } + /// + public string Message { get; set; } = ""; + /// + public string Owner { get; set; } = ""; + /// + public IEnumerable? RelatedInformation { get; set; } + /// + public Object Resource { get; set; } = ""; + /// + public int Severity { get; set; } + /// + public int StartColumn { get; set; } + /// + public int StartLineNumber { get; set; } + /// + public IEnumerable? Tags { get; set; } + /// + public string? ModelVersionId { get; set; } + /// + public string? Source { get; set; } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorMarkerSeverity.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorMarkerSeverity.cs new file mode 100644 index 000000000..2a146d3b8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorMarkerSeverity.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// See https://microsoft.github.io/monaco-editor/docs.html#enums/MarkerSeverity.html +/// +public enum MonacoEditorMarkerSeverity +{ + /// + /// Hint + /// + Hint = 1, + /// + /// Info + /// + Info = 2, + /// + /// Warning + /// + Warning = 4, + /// + /// Error + /// + Error = 8, +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/PreferredLanguage.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/PreferredLanguage.cs new file mode 100644 index 000000000..b7f0d8e54 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/PreferredLanguage.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Defines the possible preferred language of a Monaco editor +/// +public static class PreferredLanguage +{ + /// + /// JSON + /// + public const string JSON = "json"; + /// + /// YAML + /// + public const string YAML = "yaml"; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/PreferredLanguageSelector.razor b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/PreferredLanguageSelector.razor new file mode 100644 index 000000000..33494a3fd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/PreferredLanguageSelector.razor @@ -0,0 +1,73 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@inject IMonacoEditorHelper MonacoEditorHelper +@implements IDisposable + + +
+ + +
+ + +@code { + + protected string? ClassNames => Class; + [Parameter] public string? Class { get; set; } + [Parameter] public EventCallback PreferedLanguageChange { get; set; } + + protected bool isJsonSelected { get; set; } + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + this.isJsonSelected = this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON; + } + + protected virtual async Task ToggleLanguage() + { + if (this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON) { + await this.MonacoEditorHelper.ChangePreferredLanguageAsync(PreferredLanguage.YAML); + } + else + { + await this.MonacoEditorHelper.ChangePreferredLanguageAsync(PreferredLanguage.JSON); + } + } + + protected override void OnInitialized() + { + base.OnInitialized(); + this.MonacoEditorHelper.PreferredLanguageChanged += this.HandlePreferedLanguageChangeAsync; + } + + protected async Task HandlePreferedLanguageChangeAsync(string language) + { + this.isJsonSelected = this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON; + await this.PreferedLanguageChange.InvokeAsync(language); + this.StateHasChanged(); + } + + public void Dispose() + { + if (this.MonacoEditorHelper != null) { + this.MonacoEditorHelper.PreferredLanguageChanged -= this.HandlePreferedLanguageChangeAsync; + } + GC.SuppressFinalize(this); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/State.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/State.cs new file mode 100644 index 000000000..2bd1a8075 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/State.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components.MonacoEditorStateManagement; + +/// +/// Represents the state of a +/// +public record MonacoEditorState +{ + /// + /// Gets/sets the text representation of the referenced document + /// + public string DocumentText { get; set; } = string.Empty; + + /// + /// Gets/sets a boolean indicating the editor is read-only + /// + public bool IsReadOnly { get; set; } = false; + + /// + /// Gets/sets the document's model name, if any + /// + public string ModelName { get; set; } = string.Empty; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/Store.cs b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/Store.cs new file mode 100644 index 000000000..92d159d80 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/MonacoEditor/Store.cs @@ -0,0 +1,314 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Services; + +namespace Synapse.Dashboard.Components.MonacoEditorStateManagement; + +/// +/// Represents the of a +/// +/// The service used interact with Synapse API +/// The service used from JS interop +/// The service used ease Monaco Editor interactions +/// The service used to serialize and deserialize JSON +/// The service used to serialize and deserialize YAML +public class MonacoEditorStore( + ISynapseApiClient apiClient, + IJSRuntime jsRuntime, + IMonacoEditorHelper monacoEditorHelper, + IJsonSerializer jsonSerializer, + IYamlSerializer yamlSerializer +) + : ComponentStore(new()) +{ + + private TextModel? _textModel = null; + private string _textModelUri = monacoEditorHelper.GetResourceUri(); + + /// + /// Gets the service used to interact with the Synapse API + /// + protected ISynapseApiClient ApiClient { get; } = apiClient; + /// + /// Gets the service used for JS interop + /// + protected IJSRuntime JSRuntime { get; } = jsRuntime; + + /// + /// Gets the service used ease Monaco Editor interactions + /// + protected IMonacoEditorHelper MonacoEditorHelper { get; } = monacoEditorHelper; + + /// + /// Gets the service used to serialize and deserialize JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the service used to serialize and deserialize YAML + /// + protected IYamlSerializer YamlSerializer { get; } = yamlSerializer; + + /// + /// The provider function + /// + public Func StandaloneEditorConstructionOptions = monacoEditorHelper.GetStandaloneEditorConstructionOptions(string.Empty, false, monacoEditorHelper.PreferredLanguage); + + /// + /// The reference + /// + public StandaloneCodeEditor? TextEditor { get; set; } + + #region Selectors + /// + /// Gets an used to observe changes + /// + public IObservable IsReadOnly => this.Select(state => state.IsReadOnly).DistinctUntilChanged(); + /// + /// Gets an used to observe changes + /// + public IObservable DocumentText => this.Select(state => state.DocumentText).DistinctUntilChanged(); + #endregion + + #region Setters + /// + /// Sets the state's + /// + /// The new value + public void SetIsReadOnly(bool isReadOnly) + { + var current = this.Get(state => state.IsReadOnly); + if (current != isReadOnly) + { + this.Reduce(state => state with { + IsReadOnly = isReadOnly + }); + } + } + + /// + /// Sets the state's based on the provided object document + /// + /// The object to build the new with + public void SetDocument(object? document) + { + string documentText = string.Empty; + if (document != null) + { + try + { + documentText = this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON ? + this.JsonSerializer.SerializeToText(document) : + this.YamlSerializer.SerializeToText(document); + } + catch /*(Exception ex)*/ + { + // todo: handle ex + } + } + this.Reduce(state => state with + { + DocumentText = documentText + }); + } + + /// + /// Sets the state's based on the provided JSON document + /// + /// The JSON document to build the new with + public void SetDocumentJson(string? documentJson) + { + var documentText = documentJson ?? string.Empty; + if (this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.YAML) + { + try + { + documentText = this.YamlSerializer.ConvertFromJson(documentText); + } + catch /*(Exception ex)*/ + { + //todo: handle ex + } + } + this.Reduce(state => state with + { + DocumentText = documentText + }); + } + + /// + /// Sets the state's based on the provided YAML document + /// + /// The YAML document to build the new with + public void SetDocumentYaml(string? documentYaml) + { + var documentText = documentYaml ?? string.Empty; + if (this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON) + { + try + { + documentText = this.YamlSerializer.ConvertToJson(documentText); + } + catch /*(Exception ex)*/ + { + //todo: handle ex + } + } + this.Reduce(state => state with + { + DocumentText = documentText + }); + } + /// + /// Sets the state's + /// + /// The new value + public void SetTexModelName(string modelName) + { + var current = this.Get(state => state.ModelName); + if (current != modelName) + { + this.Reduce(state => state with + { + ModelName = modelName + }); + this._textModelUri = !string.IsNullOrEmpty(modelName) ? monacoEditorHelper.GetResourceUri(modelName) : monacoEditorHelper.GetResourceUri(); + } + } + #endregion + + #region Actions + /// + /// Handles changed of the text editor's language + /// + /// + /// + public async Task ToggleTextBasedEditorLanguageAsync(string _) + { + if (this.TextEditor == null) + { + return; + } + var language = this.MonacoEditorHelper.PreferredLanguage; + try + { + var document = await this.TextEditor.GetValue(); + if (document == null) + { + return; + } + document = language == PreferredLanguage.YAML ? + this.YamlSerializer.ConvertFromJson(document) : + this.YamlSerializer.ConvertToJson(document); + this.Reduce(state => state with + { + DocumentText = document + }); + await this.OnTextBasedEditorInitAsync(); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + await this.MonacoEditorHelper.ChangePreferredLanguageAsync(language == PreferredLanguage.YAML ? PreferredLanguage.JSON : PreferredLanguage.YAML); + } + } + + /// + /// Handles initialization of the text editor + /// + /// + public async Task OnTextBasedEditorInitAsync() + { + await this.SetTextBasedEditorLanguageAsync(); + await this.SetTextEditorValueAsync(); + } + + /// + /// Sets the language of the text editor + /// + /// + public async Task SetTextBasedEditorLanguageAsync() + { + try + { + var language = this.MonacoEditorHelper.PreferredLanguage; + if (this.TextEditor != null) + { + this._textModel = await Global.GetModel(this.JSRuntime, this._textModelUri); + this._textModel ??= await Global.CreateModel(this.JSRuntime, "", language, this._textModelUri); + await Global.SetModelLanguage(this.JSRuntime, this._textModel, language); + await this.TextEditor!.SetModel(this._textModel); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + } + + /// + /// Changes the value of the text editor + /// + /// + async Task SetTextEditorValueAsync() + { + var document = this.Get(state => state.DocumentText); + if (this.TextEditor != null && !string.IsNullOrWhiteSpace(document)) + { + await this.TextEditor.SetValue(document); + } + } + #endregion + + /// + public override Task InitializeAsync() + { + this.DocumentText.SubscribeAsync(async (_) => { + await this.SetTextEditorValueAsync(); + }, cancellationToken: this.CancellationTokenSource.Token); + this.IsReadOnly.Subscribe((isReadOnly) => + { + this.TextEditor?.UpdateOptions(new EditorUpdateOptions() { ReadOnly = isReadOnly }); + }, token: this.CancellationTokenSource.Token); + return base.InitializeAsync(); + } + + private bool disposed; + /// + /// Disposes of the store + /// + /// A boolean indicating whether or not the dispose of the store + protected override void Dispose(bool disposing) + { + if (!this.disposed) + { + if (disposing) + { + if (this._textModel != null) + { + this._textModel.DisposeModel(); + this._textModel = null; + } + if (this.TextEditor != null) + { + this.TextEditor.Dispose(); + this.TextEditor = null; + } + } + this.disposed = true; + } + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceDetails/ResourceDetails.razor b/src/dashboard/Synapse.Dashboard/Components/ResourceDetails/ResourceDetails.razor new file mode 100644 index 000000000..f4378921e --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceDetails/ResourceDetails.razor @@ -0,0 +1,87 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@using System.Text.Json +@typeparam TResource where TResource : Resource, new() + +
+
+ + + + + + + + + + + + + + + @if (resource?.IsNamespaced() == true) + { + + + + + } + + + + + + + + + @if (resource?.Metadata.Labels?.Any() == true) + { + + + + + } + +
API Version@resource?.ApiVersion
Kind@resource?.Kind
Name@resource?.GetName()
Namespace@resource?.GetNamespace()
Creation Time@resource?.Metadata.CreationTimestamp?.ToString("R")
Generation@resource?.Metadata.Generation
Labels + @foreach(var label in resource.Metadata.Labels) + { + @label.Key: @label.Value + } +
+
+
+ +@code { + + TResource? resource; + /// + /// Gets/sets the resource to display details about + /// + [Parameter] public TResource? Resource { get; set; } + + /// + protected override Task OnParametersSetAsync() + { + if(this.resource != this.Resource) + { + this.resource = this.Resource; + } + return base.OnParametersSetAsync(); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor new file mode 100644 index 000000000..330e5215f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor @@ -0,0 +1,268 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@using Synapse.Dashboard.Components.ResourceEditorStateManagement +@typeparam TResource where TResource : Resource, new() +@inherits StatefulComponent, ResourceEditorStore, ResourceEditorState> +@inject IMonacoEditorHelper MonacoEditorHelper +@inject IJSRuntime JSRuntime + +
+
+
+ +
+ +
+ @if (problemDetails != null) + { + + @problemDetails.Detail + + @if (problemDetails.Errors != null && problemDetails.Errors.Any()) + { + foreach (KeyValuePair errorContainer in problemDetails.Errors) + { + +
    + @foreach (string error in errorContainer.Value) + { +
  • @error
  • + } +
+
+ } + } + } +
+ +
+
+ +@code { + + TResource? resource; + /// + /// Gets/sets the resource to display details about + /// + [Parameter] public TResource? Resource { get; set; } + + bool isCluster = false; + /// + /// Gets/sets a boolean indicating the resource is a cluster + /// + [Parameter] public bool IsCluster { get; set; } = false; + + /// + /// The reference of the + /// + private StandaloneCodeEditor? textBasedEditor; + + /// + /// The used to observe and debounce the input of the text editor + /// + private Subject textEditorInput = new Subject(); + + /// + /// The content of the text editor + /// + private string textEditorValue = string.Empty; + + /// + /// The provided to the + /// + private TextModel? textEditorModel = null!; + + /// + /// A boolean indicating if the text editor is being updated + /// + private bool isUpdating = false; + + /// + /// A boolean indicating if the resource is being saved + /// + private bool isSaving = false; + + /// + /// The that occurred when trying to save the resource, if any + /// + private ProblemDetails? problemDetails = null; + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync().ConfigureAwait(false); + this.Store.Resource.Subscribe(resource => this.OnStateChanged(cmp => cmp.resource = resource), token: this.CancellationTokenSource.Token); + this.Store.IsUpdating.Subscribe(updating => this.OnStateChanged(cmp => cmp.isUpdating = updating), token: this.CancellationTokenSource.Token); + this.Store.IsSaving.Subscribe(OnSavingChanged, token: this.CancellationTokenSource.Token); + this.Store.ProblemDetails.Subscribe(problemDetails => this.OnStateChanged(cmp => cmp.problemDetails = problemDetails), token: this.CancellationTokenSource.Token); + this.textEditorInput + .Throttle(TimeSpan.FromMilliseconds(300)) + .DistinctUntilChanged() + .Subscribe(text => this.Store.SetEditorValue(text)); + this.Store.TextEditorValue.SubscribeAsync(async textEditorValue => + { + this.textEditorValue = textEditorValue; + await this.SetTextEditorValueAsync(); + }, cancellationToken: this.CancellationTokenSource.Token); + } + + /// + protected override Task OnParametersSetAsync() + { + if (this.resource == null || this.resource.GetQualifiedName() != this.Resource?.GetQualifiedName()) + { + this.resource = this.Resource; // should happen in this.Store.Resource.Subscribe but prevents possible race when multiple params are set + this.Store.SetResource(this.Resource); + } + if (this.isCluster != this.IsCluster) + { + this.isCluster = this.IsCluster; + this.Store.SetIsCluster(this.IsCluster); + } + return base.OnParametersSetAsync(); + } + + /// + /// Sets the editor as read-only when saving + /// + /// Whenever the resource is in a saving state + private void OnSavingChanged(bool saving) + { + this.isSaving = saving; + if (this.textBasedEditor != null) this.textBasedEditor.UpdateOptions(new EditorUpdateOptions() { ReadOnly = saving }); + this.StateHasChanged(); + } + + /// + /// Handles the event + /// + /// A + private async Task OnTextBasedEditorInit() + { + var resourceUri = $"inmemory://{typeof(TResource).Name.ToLower()}"; + this.textEditorModel = await Global.GetModel(this.JSRuntime, resourceUri); + if (this.textEditorModel == null) + { + this.textEditorModel = await Global.CreateModel(this.JSRuntime, this.textEditorValue, this.MonacoEditorHelper.PreferredLanguage, resourceUri); + await this.textBasedEditor!.SetModel(this.textEditorModel); + } + else + { + await this.SetTextEditorValueAsync(); + await this.SetTextBasedEditorLanguageAsync(); + } + this.StateHasChanged(); + } + + /// + /// Handles the event + /// + /// The source + /// A + private async Task OnTextBasedValueChanged(ModelContentChangedEvent e) + { + if (!this.isUpdating && this.textBasedEditor != null && this.textEditorInput != null) + { + var text = await this.textBasedEditor.GetValue(); + this.textEditorInput.OnNext(text); + } + } + + /// + /// Changes the editor's text + /// + /// A + private async Task SetTextEditorValueAsync() + { + if (this.textBasedEditor != null) + { + var editorText = await this.textBasedEditor.GetValue(); + if (this.textEditorValue != editorText) await this.textBasedEditor.SetValue(this.textEditorValue); + } + } + + /// + /// Changes the editor's language + /// + /// A + private async Task SetTextBasedEditorLanguageAsync() + { + if (this.textBasedEditor != null && this.textEditorModel != null) + { + //TextModel model = await this.textBasedEditor!.GetModel(); + await Global.SetModelLanguage(this.JSRuntime, this.textEditorModel, this.MonacoEditorHelper.PreferredLanguage); + } + } + + /// + /// Changes the editor language + /// + /// The new editor's language + /// A + private async Task ToggleTextBasedEditorLanguageAsync(string language) + { + var model = await this.textBasedEditor!.GetModel(); + var editorLanguage = await model.GetLanguageId(); + if (editorLanguage != language) + { + await this.Store.ChangeTextEditorLanguageAsync(language); + await this.SetTextBasedEditorLanguageAsync(); + } + } + + private bool disposed; + /// + /// Disposes of the component + /// + /// A boolean indicating whether or not the dispose of the component + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!this.disposed) + { + if (disposing) + { + if (this.textEditorInput != null) this.textEditorInput.Dispose(); + if (this.textEditorModel != null) + { + this.textEditorModel.DisposeModel(); + this.textEditorModel = null; + } + if (this.textBasedEditor != null) + { + this.textBasedEditor.Dispose(); + this.textBasedEditor = null; + } + } + this.disposed = true; + } + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.css b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.css new file mode 100644 index 000000000..bb7b50900 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.css @@ -0,0 +1,5 @@ +.problems { + height: 200px; + max-height: 200px; + overflow-y: scroll; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.min.css b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.min.css new file mode 100644 index 000000000..b9d598632 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.min.css @@ -0,0 +1 @@ +.problems{height:200px;max-height:200px;overflow-y:scroll;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.scss b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.scss new file mode 100644 index 000000000..d5215725d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor.scss @@ -0,0 +1,5 @@ +.problems { + height: 200px; + max-height: 200px; + overflow-y: scroll; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/State.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/State.cs new file mode 100644 index 000000000..556b186a2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/State.cs @@ -0,0 +1,78 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components.ResourceEditorStateManagement; + +/// +/// Represents the state of the 's component +/// +public record ResourceEditorState + where TResource : Resource, new() +{ + + /// + /// Gets/sets the resource to display details about + /// + public TResource? Resource { get; set; } = new() { Metadata = new() { Name = "new-" + typeof(TResource).Name.ToLower() } }; + + /// + /// Gets/sets the definition of the displayed resource + /// + public ResourceDefinition? Definition { get; set; } = null; + + /// + /// Gets/sets the content of the text editor + /// + public string TextEditorValue { get; set; } = string.Empty; + + /// + /// Gets/sets a boolean indicating if the resource is a cluster + /// + public bool IsCluster { get; set; } = false; + + /// + /// Gets/sets a boolean indicating if the text editor is being updated + /// + public bool IsUpdating { get; set; } = false; + + /// + /// Gets/sets a boolean indicating if the resource is being saved + /// + public bool IsSaving { get; set; } = false; + + /// + /// Gets/sets the type that occurred when trying to save the resource, if any + /// + public Uri? ProblemType { get; set; } = null; + + /// + /// Gets/sets the title that occurred when trying to save the resource, if any + /// + public string ProblemTitle { get; set; } = string.Empty; + + /// + /// Gets/sets the details that occurred when trying to save the resource, if any + /// + public string ProblemDetail { get; set; } = string.Empty; + + /// + /// Gets/sets the status that occurred when trying to save the resource, if any + /// + public int ProblemStatus { get; set; } = 0; + + /// + /// Gets/sets the list of errors that occurred when trying to save the resource, if any + /// + public IDictionary ProblemErrors { get; set; } = new Dictionary(); + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/Store.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/Store.cs new file mode 100644 index 000000000..df476a188 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceEditor/Store.cs @@ -0,0 +1,336 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using JsonCons.Utilities; +using Neuroglia.Data; +using Neuroglia.Serialization.Yaml; +using Synapse.Api.Client.Services; + +namespace Synapse.Dashboard.Components.ResourceEditorStateManagement; + +/// +/// Represents a 's form +/// +/// +/// Initializes a new +/// +/// The service used to interact with a Synapse API +/// The service used to facilitate the Monaco editor interactions +/// The The service used to serialize/deserialize objects to/from JSON +/// The service used to serialize/deserialize objects to/from YAML +public class ResourceEditorStore(ISynapseApiClient apiClient, IMonacoEditorHelper monacoEditorHelper, IJsonSerializer jsonSerializer, IYamlSerializer yamlSerializer) + : ComponentStore>(new()) + where TResource : Resource, new() +{ + + /// + /// Gets an used to observe changes + /// + public IObservable Resource => this.Select(state => state.Resource).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Definition => this.Select(state => state.Definition).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable TextEditorValue => this.Select(state => state.TextEditorValue).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable IsCluster => this.Select(state => state.IsCluster).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable IsUpdating => this.Select(state => state.IsUpdating).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable IsSaving => this.Select(state => state.IsSaving).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemType => this.Select(state => state.ProblemType).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemTitle => this.Select(state => state.ProblemTitle).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemDetail => this.Select(state => state.ProblemDetail).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemStatus => this.Select(state => state.ProblemStatus).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable> ProblemErrors => this.Select(state => state.ProblemErrors).DistinctUntilChanged(); + + /// + /// Gets an used to observe computed + /// + public IObservable ProblemDetails => Observable.CombineLatest( + this.ProblemType, + this.ProblemTitle, + this.ProblemStatus, + this.ProblemDetail, + this.ProblemErrors, + (type, title, status, details, errors) => + { + if (string.IsNullOrWhiteSpace(title)) + { + return null; + } + return new ProblemDetails(type ?? new Uri("unknown://"), title, status, details, null, errors, null); + } + ); + + /// + public override async Task InitializeAsync() + { + await base.InitializeAsync().ConfigureAwait(false); + this.Resource.Subscribe(resource => + { + if (monacoEditorHelper.PreferredLanguage == PreferredLanguage.YAML) + { + this.SetEditorValue(YamlSerializer.Default.Serialize(resource)); + } + else + { + this.SetEditorValue(YamlSerializer.Default.Serialize(resource)); + } + }, token: this.CancellationTokenSource.Token); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetResource(TResource? resource) + { + if (resource != null) + { + this.Reduce(state => state with + { + Resource = resource + }); + return; + } + this.Reduce(state => state with { + Resource = new() { Metadata = new() { Name = "new-" + typeof(TResource).Name.ToLower() } } + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetDefinition(ResourceDefinition? definition) + { + this.Reduce(state => state with + { + Definition = definition + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetEditorValue(string textEditorValue) + { + this.SetUpdating(true); + this.Reduce(state => state with + { + TextEditorValue = textEditorValue + }); + this.SetUpdating(false); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetUpdating(bool isUpdating) + { + this.Reduce(state => state with + { + IsUpdating = isUpdating + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetIsCluster(bool isCluster) + { + this.Reduce(state => state with + { + IsCluster = isCluster + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetSaving(bool isSaving) + { + this.Reduce(state => state with + { + IsSaving = isSaving + }); + } + + /// + /// Sets the state's 's related data + /// + /// The to populate the data with + public void SetProblemDetails(ProblemDetails? problem) + { + this.Reduce(state => state with + { + ProblemType = problem?.Type, + ProblemTitle = problem?.Title ?? string.Empty, + ProblemStatus = problem?.Status ?? 0, + ProblemDetail = problem?.Detail ?? string.Empty, + ProblemErrors = problem?.Errors?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? [] + }); + } + + /// + /// Changes the editor language + /// + /// The new editor's language + /// + public async Task ChangeTextEditorLanguageAsync(string language) + { + var textEditorValue = this.Get(state => state.TextEditorValue); + try + { + var text = language == PreferredLanguage.YAML ? + yamlSerializer.ConvertFromJson(textEditorValue) : + yamlSerializer.ConvertToJson(textEditorValue); + this.SetEditorValue(text); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + await monacoEditorHelper.ChangePreferredLanguageAsync(language == PreferredLanguage.YAML ? PreferredLanguage.JSON : PreferredLanguage.YAML); + } + } + + /// + /// Creates or updates the current resource + /// + /// + public async Task SubmitResourceAsync() + { + TResource? resource = this.Get(state => state.Resource); + if (resource?.Metadata?.Generation == null || resource?.Metadata?.Generation == 0) + { + await this.CreateResourceAsync(); + } + else + { + await this.UpdateResourceAsync(); + } + } + + /// + /// Creates the current resource using the text editor value + /// + /// + public async Task CreateResourceAsync() + { + this.SetProblemDetails(null); + this.SetSaving(true); + var textEditorValue = this.Get(state => state.TextEditorValue); + if (monacoEditorHelper.PreferredLanguage == PreferredLanguage.YAML) textEditorValue = yamlSerializer.ConvertToJson(textEditorValue); + TResource? resource; + try + { + resource = jsonSerializer.Deserialize(textEditorValue); + var isCluster = this.Get(state => state.IsCluster); + resource = await (!isCluster ? + apiClient.ManageNamespaced().CreateAsync(resource!, this.CancellationTokenSource.Token) : + apiClient.ManageCluster().CreateAsync(resource!, this.CancellationTokenSource.Token) + ); + this.SetResource(resource); + } + catch (ProblemDetailsException ex) + { + this.SetProblemDetails(ex.Problem); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); // todo: improve logging + } + this.SetSaving(false); + } + + /// + /// Updates the current resource using the text editor value + /// + /// + public async Task UpdateResourceAsync() + { + this.SetProblemDetails(null); + this.SetSaving(true); + var resource = this.Get(state => state.Resource); + if (resource == null) + { + return; + } + var textEditorValue = this.Get(state => state.TextEditorValue); + if (monacoEditorHelper.PreferredLanguage == PreferredLanguage.YAML) textEditorValue = yamlSerializer.ConvertToJson(textEditorValue); + var jsonPatch = JsonPatch.FromDiff(jsonSerializer.SerializeToElement(resource)!.Value, jsonSerializer.SerializeToElement(jsonSerializer.Deserialize(textEditorValue))!.Value); + var patch = jsonSerializer.Deserialize(jsonPatch.RootElement); + if (patch != null) + { + var resourcePatch = new Patch(PatchType.JsonPatch, jsonPatch); + try + { + var isCluster = this.Get(state => state.IsCluster); + resource = await (!isCluster ? + apiClient.ManageNamespaced().PatchAsync(resource.GetName(), resource.GetNamespace()!, resourcePatch, null, this.CancellationTokenSource.Token) : + apiClient.ManageCluster().PatchAsync(resource.GetName(), resourcePatch, null, this.CancellationTokenSource.Token) + ); + this.SetResource(resource); + } + catch(ProblemDetailsException ex) + { + this.SetProblemDetails(ex.Problem); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); // todo: improve logging + } + } + this.SetSaving(false); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ClusterResourceManagementComponent.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ClusterResourceManagementComponent.cs new file mode 100644 index 000000000..c0879e8d8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ClusterResourceManagementComponent.cs @@ -0,0 +1,27 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents the base class for all components used to manage s +/// +/// The type of to manage +public abstract class ClusterResourceManagementComponent + : ResourceManagementComponent, ClusterResourceManagementComponentStore, TResource> + where TResource : Resource, new() +{ + + + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ClusterResourceManagementComponentStore.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ClusterResourceManagementComponentStore.cs new file mode 100644 index 000000000..62ac022bb --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ClusterResourceManagementComponentStore.cs @@ -0,0 +1,73 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Services; +using System.Reactive.Linq; + +namespace Synapse.Dashboard.Components.ResourceManagement; + +/// +/// Represents a used to manage Synapse s of the specified type +/// +/// The type of s to manage +/// +/// Initializes a new +/// +/// The service used to interact with the Synapse API +/// The websocket service client +public class ClusterResourceManagementComponentStore(ISynapseApiClient apiClient, ResourceWatchEventHubClient resourceEventHub) + : ResourceManagementComponentStoreBase(apiClient, resourceEventHub) + where TResource : Resource, new() +{ + + /// + public override async Task DeleteResourceAsync(TResource resource) + { + await this.ApiClient.ManageCluster().DeleteAsync(resource.GetName()).ConfigureAwait(false); + } + + /// + public override async Task GetResourceDefinitionAsync() + { + var resourceDefinition = await this.ApiClient.ManageCluster().GetDefinitionAsync().ConfigureAwait(false); + this.Reduce(s => s with + { + Definition = resourceDefinition + }); + } + + /// + public override async Task ListResourcesAsync(ResourcesFilter? filter = null) + { + try + { + this.Reduce(state => state with + { + Loading = true, + }); + var resourceList = new EquatableList(await (await this.ApiClient.ManageCluster().ListAsync(filter?.LabelSelectors).ConfigureAwait(false)).OrderBy(r => r.Metadata.CreationTimestamp).ToListAsync().ConfigureAwait(false)); + this.Reduce(s => s with + { + Resources = resourceList, + Loading = false + }); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: implement proper error handling + } + } + + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponent.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponent.cs new file mode 100644 index 000000000..698d56741 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponent.cs @@ -0,0 +1,122 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents the base class for all components used to manage s +/// +/// The type of component inheriting the +/// The type of the component's +/// The type of the component's state +/// The type of to manage +public abstract class NamespacedResourceManagementComponent + : ResourceManagementComponent + where TComponent : NamespacedResourceManagementComponent + where TStore : NamespacedResourceManagementComponentStore + where TState : NamespacedResourceManagementComponentState, new() + where TResource : Resource, new() +{ + + /// + /// Gets the list of available s + /// + protected EquatableList? Namespaces { get; set; } + + /// + /// Gets/sets current namespace + /// + public string? @namespace; + /// + /// Gets/sets current namespace + /// + [Parameter] public string? Namespace { get; set; } + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + this.Store.Namespace.Subscribe(value => this.OnStateChanged(_ => + { + this.@namespace = value; + if (Namespace != value) this.Namespace = value; + }), token: this.CancellationTokenSource.Token); + this.Store.Namespaces.Subscribe(value => this.OnStateChanged(_ => Namespaces = value), token: this.CancellationTokenSource.Token); + } + + /// + protected override async Task OnParametersSetAsync() + { + if (Namespace != @namespace) + { + Store.SetNamespace(Namespace); + } + await base.OnParametersSetAsync(); + } + + /// + /// Handles changes of namespace + /// + /// + /// + protected void OnNamespaceChanged(ChangeEventArgs e) + { + this.Store.SetNamespace(e.Value?.ToString()); + } + +} + +/// +/// Represents the base class for all components used to manage s +/// +/// The type of the component's +/// The type of the component's state +/// The type of to manage +public abstract class NamespacedResourceManagementComponent + : NamespacedResourceManagementComponent, TStore, TState, TResource> + where TStore : NamespacedResourceManagementComponentStore + where TState : NamespacedResourceManagementComponentState, new() + where TResource : Resource, new() +{ + + + +} + +/// +/// Represents the base class for all components used to manage s +/// +/// The type of the component's state +/// The type of to manage +public abstract class NamespacedResourceManagementComponent + : NamespacedResourceManagementComponent, TState, TResource> + where TState : NamespacedResourceManagementComponentState, new() + where TResource : Resource, new() +{ + + + +} + +/// +/// Represents the base class for all components used to manage s +/// +/// The type of to manage +public abstract class NamespacedResourceManagementComponent + : NamespacedResourceManagementComponent, TResource>, NamespacedResourceManagementComponentState, TResource> + where TResource : Resource, new() +{ + + + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponentState.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponentState.cs new file mode 100644 index 000000000..c03e0f5c1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponentState.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components.ResourceManagement; + +/// +/// Represents the state of a namespaced resource management component +/// +/// The type of managed namespaced s +public record NamespacedResourceManagementComponentState + : ResourceManagementComponentState + where TResource : Resource, new() +{ + + /// + /// Gets a that contains all s + /// + public EquatableList? Namespaces { get; set; } + + /// + /// Gets the the (namespaced) resources to list belong to, if any + /// + public string? Namespace { get; set; } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponentStore.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponentStore.cs new file mode 100644 index 000000000..c66b20be5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/NamespacedResourceManagementComponentStore.cs @@ -0,0 +1,92 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Services; +using System.Reactive.Linq; + +namespace Synapse.Dashboard.Components.ResourceManagement; + +/// +/// Represents a used to manage Synapse s of the specified type +/// +/// The type of the component's state +/// The type of s to manage +/// The service used to interact with the Synapse API +/// The websocket service client +public class NamespacedResourceManagementComponentStore(ISynapseApiClient apiClient, ResourceWatchEventHubClient resourceEventHub) + : ResourceManagementComponentStoreBase(apiClient, resourceEventHub) + where TResource : Resource, new() + where TState : NamespacedResourceManagementComponentState, new() +{ + + /// + /// Gets an used to observe s + /// + public IObservable?> Namespaces => this.Select(s => s.Namespaces).DistinctUntilChanged(); + + /// + /// Gets an used to observe current namespace + /// + public IObservable Namespace => this.Select(s => s.Namespace).DistinctUntilChanged(); + + /// + protected override IObservable Filter => Observable.CombineLatest( + this.Namespace, + this.LabelSelectors, + (@namespace, labelSelectors) => new ResourcesFilter() + { + Namespace = @namespace, + LabelSelectors = labelSelectors + } + ) + .DistinctUntilChanged(); + + /// + /// Sets the + /// + /// The new namespace + public void SetNamespace(string? @namespace) + { + this.Reduce(state => state with + { + Namespace = @namespace + }); + } + + /// + /// Lists all available s + /// + /// A new awaitable + public virtual async Task ListNamespaceAsync() + { + var namespaceList = new EquatableList(await (await this.ApiClient.Namespaces.ListAsync().ConfigureAwait(false)).OrderBy(ns => ns.GetQualifiedName()).ToListAsync().ConfigureAwait(false)); + this.Reduce(s => s with + { + Namespaces = namespaceList + }); + } + + /// + public override async Task DeleteResourceAsync(TResource resource) + { + await this.ApiClient.ManageNamespaced().DeleteAsync(resource.GetName(), resource.GetNamespace()!).ConfigureAwait(false); + } + + /// + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + await this.ListNamespaceAsync().ConfigureAwait(false); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponent.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponent.cs new file mode 100644 index 000000000..4b251444c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponent.cs @@ -0,0 +1,268 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Json.Schema; + +namespace Synapse.Dashboard.Components; + +/// +/// Represents the base class for all components used to manage s +/// +/// The type of component inheriting the +/// The type of to use +/// The type of the component's state +/// The type of to manage +public abstract class ResourceManagementComponent + : StatefulComponent + where TComponent : ResourceManagementComponent + where TStore : ResourceManagementComponentStoreBase + where TState : ResourceManagementComponentState, new() + where TResource : Resource, new() +{ + + /// + /// Gets/sets the service to build a bridge with the monaco interop extension + /// + [Inject] + protected MonacoInterop? MonacoInterop { get; set; } + + /// + /// Gets/sets the service used for JS interop + /// + [Inject] + protected JSInterop jsInterop { get; set; } = default!; + + /// + /// Gets the service used to serialize/deserialize objects to/from JSON + /// + [Inject] + protected IJsonSerializer Serializer { get; set; } = null!; + + /// + /// Gets/sets the list of displayed s + /// + protected EquatableList? Resources { get; set; } + + /// + /// Gets/sets the list of selected s + /// + protected EquatableList SelectedResourceNames { get; set; } = []; + + /// + /// Gets/sets the used to show the 's details + /// + protected Offcanvas? DetailsOffCanvas { get; set; } + + /// + /// Gets/sets the used to edit the + /// + protected Offcanvas? EditorOffCanvas { get; set; } + + /// + /// Gets/sets the used to confirm the 's deletion + /// + protected ConfirmDialog? Dialog { get; set; } + + /// + /// Gets/sets the 's + /// + protected ResourceDefinition? Definition { get; set; } + + /// + /// Gets/sets the search term to filter the resources with + /// + protected string? SearchTerm { get; set; } + + /// + /// Gets/sets a boolean value that indicates whether data is currently being gathered + /// + protected bool Loading { get; set; } = false; + + /// + /// Gets/sets the checkbox used to (un)select all resources + /// + protected ElementReference? CheckboxAll { get; set; } = null; + + string activeResourcesName = null!; + /// + /// Gets/sets the name of the active resource + /// + [Parameter] public string? Name { get; set; } + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync().ConfigureAwait(false); + Observable.CombineLatest( + this.Store.Resources, + this.Store.SelectedResourceNames, + (resources, selectedResourceNames) => (resources, selectedResourceNames) + ).SubscribeAsync(async (values) => { + var (resources, selectedResourceNames) = values; + this.OnStateChanged(_ => + { + this.Resources = resources; + this.SelectedResourceNames = selectedResourceNames; + }); + if (this.CheckboxAll.HasValue) + { + + if (selectedResourceNames.Count == 0) + { + await this.jsInterop.SetCheckboxStateAsync(this.CheckboxAll.Value, CheckboxState.Unchecked); + } + else if (selectedResourceNames.Count == (resources?.Count ?? 0)) + { + await this.jsInterop.SetCheckboxStateAsync(this.CheckboxAll.Value, CheckboxState.Checked); + } + else + { + await this.jsInterop.SetCheckboxStateAsync(this.CheckboxAll.Value, CheckboxState.Indeterminate); + } + } + }, cancellationToken: this.CancellationTokenSource.Token); + this.Store.ActiveResourceName.Subscribe(value => this.OnStateChanged(_ => this.activeResourcesName = value), token: this.CancellationTokenSource.Token); + this.Store.SearchTerm.Subscribe(value => this.OnStateChanged(_ => this.SearchTerm = value), token: this.CancellationTokenSource.Token); + this.Store.Loading.Subscribe(value => this.OnStateChanged(_ => this.Loading = value), token: this.CancellationTokenSource.Token); + this.Store.Definition.SubscribeAsync(async definition => + { + if (this.Definition != definition) + { + this.Definition = definition; + if (this.Definition != null && this.MonacoInterop != null) + { + await this.MonacoInterop.AddValidationSchemaAsync(this.Serializer.SerializeToText(this.Definition.Spec.Versions.First().Schema.OpenAPIV3Schema ?? new JsonSchemaBuilder().Build()), $"https://synapse.io/schemas/{typeof(TResource).Name.ToLower()}.json", $"{typeof(TResource).Name.ToLower()}*").ConfigureAwait(false); + } + } + }, cancellationToken: this.CancellationTokenSource.Token); + this.Store.ActiveResource.SubscribeAsync(async resource => + { + if (resource != null) + { + await this.OnShowResourceDetailsAsync(resource); + } + }, cancellationToken: CancellationTokenSource.Token); + } + + + /// + protected override async Task OnParametersSetAsync() + { + if (!string.IsNullOrEmpty(Name) && Name != activeResourcesName) + { + Store.SetActiveResourceName(Name); + } + await base.OnParametersSetAsync(); + } + + /// + /// Handles search input value changes + /// + /// the to handle + protected void OnSearchInput(ChangeEventArgs e) + { + this.Store.SetSearchTerm(e.Value?.ToString()); + } + + /// + /// Handles the deletion of the targeted + /// + /// The to delete + protected async Task OnDeleteResourceAsync(TResource resource) + { + if (this.Dialog == null) return; + var confirmation = await this.Dialog.ShowAsync( + title: $"Are you sure you want to delete '{resource.Metadata.Name}'?", + message1: $"The {typeof(TResource).Name.ToLower()} will be permanently deleted. Are you sure you want to proceed ?", + confirmDialogOptions: new ConfirmDialogOptions() + { + YesButtonColor = ButtonColor.Danger, + YesButtonText = "Delete", + NoButtonText = "Abort", + IsVerticallyCentered = true + } + ); + if (!confirmation) return; + await this.Store.DeleteResourceAsync(resource); + } + + /// + /// Handles the deletion of the selected s + /// + protected async Task OnDeleteSelectedResourcesAsync() + { + if (this.Dialog == null) return; + if (this.SelectedResourceNames.Count == 0) return; + var confirmation = await this.Dialog.ShowAsync( + title: $"Are you sure you want to delete {SelectedResourceNames.Count} resource{(SelectedResourceNames.Count > 1 ? "s" : "")}?", + message1: $"The resource{(SelectedResourceNames.Count > 1 ? "s" : "")} will be permanently deleted. Are you sure you want to proceed ?", + confirmDialogOptions: new ConfirmDialogOptions() + { + YesButtonColor = ButtonColor.Danger, + YesButtonText = "Delete", + NoButtonText = "Abort", + IsVerticallyCentered = true + } + ); + if (!confirmation) return; + await this.Store.DeleteSelectedResourcesAsync(); + } + + /// + /// Opens the targeted 's details + /// + /// The to show the details for + protected Task OnShowResourceDetailsAsync(TResource resource) + { + if (this.DetailsOffCanvas == null) return Task.CompletedTask; + var parameters = new Dictionary + { + { nameof(ResourceEditor.Resource), resource } + }; + return this.DetailsOffCanvas.ShowAsync>(title: typeof(TResource).Name + " details", parameters: parameters); + } + + /// + /// Opens the targeted 's edition + /// + /// The to edit + protected virtual Task OnShowResourceEditorAsync(TResource? resource = null) + { + if (this.EditorOffCanvas == null) return Task.CompletedTask; + var parameters = new Dictionary + { + { nameof(ResourceEditor.Resource), resource! } + }; + string actionType = resource == null ? "creation" : "edition"; + return this.EditorOffCanvas.ShowAsync>(title: typeof(TResource).Name + " " + actionType, parameters: parameters); + } + +} + + +/// +/// Represents the base class for all components used to manage s +/// +/// The type of component inheriting the +/// The type of to use +/// The type of to manage +public abstract class ResourceManagementComponent + : ResourceManagementComponent, TResource> + where TComponent : ResourceManagementComponent + where TStore : ResourceManagementComponentStoreBase + where TResource : Resource, new() +{ + + + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentState.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentState.cs new file mode 100644 index 000000000..b691d3dc7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentState.cs @@ -0,0 +1,59 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components.ResourceManagement; + +/// +/// Represents the state of a resource management component +/// +/// The type of managed s +public record ResourceManagementComponentState + where TResource : Resource, new() +{ + + /// + /// Gets/sets the definition of the managed resource type + /// + public ResourceDefinition? Definition { get; set; } + + /// + /// Gets/sets a that contains all s + /// + public EquatableList? Resources { get; set; } = []; + + /// + /// Gets/sets a that contains all selected s + /// + public EquatableList SelectedResourceNames { get; set; } = []; + + /// + /// Gets/sets a list that contains the label selectors, if any, used to filter the resources to list + /// + public EquatableList? LabelSelectors { get; set; } = []; + + /// + /// Gets/sets the search term, if any, used to filter the resources to list + /// + public string? SearchTerm { get; set; } + + /// + /// Gets/sets a boolean value that indicates whether data is currently being gathered + /// + public bool Loading { get; set; } = true; + + /// + /// Gets/sets the name of the selected resource + /// + public string ActiveResourceName { get; set; } = string.Empty; + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentStoreBase.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentStoreBase.cs new file mode 100644 index 000000000..20f5fe2b7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourceManagementComponentStoreBase.cs @@ -0,0 +1,434 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Services; + +namespace Synapse.Dashboard.Components.ResourceManagement; + +/// +/// Represents a used to manage Synapse s of the specified type +/// +/// The type of the state managed by the component store +/// The type of s to manage +/// The service used to interact with the Synapse API +/// The websocket service client +public abstract class ResourceManagementComponentStoreBase(ISynapseApiClient apiClient, ResourceWatchEventHubClient resourceEventHub) + : ComponentStore(new()) + where TResource : Resource, new() + where TState : ResourceManagementComponentState, new() +{ + + /// + /// Gets an used to observe s of the specified type + /// + public IObservable Definition => this.Select(s => s.Definition).DistinctUntilChanged(); + + /// + /// Gets an used to observe unfiltered s of the specified type + /// + protected IObservable?> InternalResources => this.Select(s => s.Resources).DistinctUntilChanged(); + + /// + /// Gets an used to observe the changes + /// + public IObservable> SelectedResourceNames => this.Select(s => s.SelectedResourceNames).DistinctUntilChanged(); + + /// + /// Gets an used to observe the changes + /// + public IObservable SearchTerm => this.Select(state => state.SearchTerm).DistinctUntilChanged(); + + /// + /// Gets an used to observe the changes + /// + public IObservable?> LabelSelectors => this.Select(state => state.LabelSelectors).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Loading => this.Select(state => state.Loading).DistinctUntilChanged(); + + /// + /// Gets an used to observe s of the specified type + /// + public IObservable?> Resources => Observable.CombineLatest( + this.InternalResources, + this.SearchTerm.Throttle(TimeSpan.FromMilliseconds(100)).StartWith(""), + (resources, searchTerm) => + { + if (resources == null) + { + return []; + } + if (string.IsNullOrWhiteSpace(searchTerm)) + { + return resources!; + } + return new EquatableList(resources!.Where(r => r.GetName().Contains(searchTerm))); + } + ) + .DistinctUntilChanged(); + + /// + /// Gets an used to observe the + /// + protected virtual IObservable Filter => this.LabelSelectors + .Select(labelSelectors => new ResourcesFilter() { LabelSelectors = labelSelectors }) + .DistinctUntilChanged(); + + /// + /// Gets an used to observe the changes + /// + public virtual IObservable ActiveResourceName => this.Select(state => state.ActiveResourceName).DistinctUntilChanged(); + + /// + /// Gets an used to observe the active resource changes + /// + public virtual IObservable ActiveResource => Observable.CombineLatest( + this.Resources.Where(resources => resources != null), + this.ActiveResourceName.Where(name => !string.IsNullOrWhiteSpace(name)), + (resources, name) => resources!.FirstOrDefault(r => r.GetName() == name) + ) + .DistinctUntilChanged(); + + /// + /// Gets the service used to interact with the Synapse API + /// + protected ISynapseApiClient ApiClient { get; } = apiClient; + + /// + /// Gets the websocket service client + /// + protected ResourceWatchEventHubClient ResourceEventHub { get; } = resourceEventHub; + + /// + /// Gets the service used to monitor resources of the specified type + /// + protected Api.Client.Services.ResourceWatch ResourceWatch { get; private set; } = null!; + + /// + /// Gets an that represents the store's subscription + /// + protected IDisposable ResourceWatchSubscription { get; private set; } = null!; + + /// + public override async Task InitializeAsync() + { + await this.GetResourceDefinitionAsync().ConfigureAwait(false); + await this.ResourceEventHub.StartAsync().ConfigureAwait(false); + this.ResourceWatch = await this.ResourceEventHub.WatchAsync().ConfigureAwait(false); + this.ResourceWatch.SubscribeAsync( + onNextAsync: this.OnResourceWatchEventAsync, + onErrorAsync: ex => Task.Run(() => Console.WriteLine(ex)), + onCompletedAsync: () => Task.CompletedTask, + cancellationToken: this.CancellationTokenSource.Token + ); + this.Filter.Throttle(TimeSpan.FromMilliseconds(10)).SubscribeAsync( + onNextAsync: this.ListResourcesAsync, + onErrorAsync: ex => Task.Run(() => Console.WriteLine(ex)), + onCompletedAsync: () => Task.CompletedTask, + cancellationToken: this.CancellationTokenSource.Token + ); + await base.InitializeAsync(); + } + + /// + /// Sets the + /// + /// The new search term + public virtual void SetSearchTerm(string? searchTerm) + { + this.Reduce(state => state with + { + SearchTerm = searchTerm + }); + } + + /// + /// Sets the + /// + /// The new value + public virtual void SetActiveResourceName(string activeResourceName) + { + this.Reduce(state => state with + { + ActiveResourceName = activeResourceName ?? string.Empty + }); + } + + /// + /// Sets the + /// + /// The new label selectors + public virtual void SetLabelSelectors(EquatableList? labelSelectors) + { + this.Reduce(state => state with + { + LabelSelectors = new EquatableList(labelSelectors ?? []) + }); + } + + /// + /// Adds a single + /// + /// The label selector to add + public virtual void AddLabelSelector(LabelSelector labelSelector) + { + if (labelSelector == null) + { + return; + } + var labelSelectors = new EquatableList(this.Get(state => state.LabelSelectors) ?? []); + var existingSelector = labelSelectors.FirstOrDefault(selector => selector.Key == labelSelector.Key); + if (existingSelector != null) + { + labelSelectors.Remove(existingSelector); + } + labelSelectors.Add(labelSelector); + this.SetLabelSelectors(labelSelectors); + } + + /// + /// Removes a single by key + /// + /// The label selector key to remove + public void RemoveLabelSelector(string labelSelectorKey) + { + if (string.IsNullOrWhiteSpace(labelSelectorKey)) + { + return; + } + var labelSelectors = new EquatableList(this.Get(state => state.LabelSelectors) ?? []); + var existingSelector = labelSelectors.FirstOrDefault(selector => selector.Key == labelSelectorKey); + if (existingSelector != null) + { + labelSelectors.Remove(existingSelector); + } + this.SetLabelSelectors(labelSelectors); + } + + /// + /// Toggles the resources selection + /// + /// The name of the resource to select, or all if none is provided + public virtual void ToggleResourceSelection(string? name = null) + { + this.Reduce(state => + { + if (string.IsNullOrWhiteSpace(name)) + { + if (state.SelectedResourceNames.Any()) + { + return state with + { + SelectedResourceNames = [] + }; + } + return state with + { + SelectedResourceNames = [.. state.Resources?.Select(resource => resource.GetName()) ?? []] + }; + } + if (state.SelectedResourceNames.Contains(name)) + { + return state with + { + SelectedResourceNames = [.. state.SelectedResourceNames.Where(n => n != name)] + }; + } + return state with + { + SelectedResourceNames = [.. state.SelectedResourceNames, name] + }; + }); + } + + /// + /// Deletes the specified + /// + /// The to delete + /// A new awaitable + public abstract Task DeleteResourceAsync(TResource resource); + + /// + /// Deletes the selected s + /// + /// A new awaitable + public async Task DeleteSelectedResourcesAsync() + { + var selectedResourcesNames = this.Get(state => state.SelectedResourceNames); + var resources = (this.Get(state => state.Resources) ?? []).Where(resource => selectedResourcesNames.Contains(resource.GetName())); + foreach(var resource in resources) + { + await this.DeleteResourceAsync(resource); + } + this.Reduce(state => state with + { + SelectedResourceNames = [] + }); + } + + /// + /// Fetches the definition of the managed type + /// + /// A new awaitable + public virtual async Task GetResourceDefinitionAsync() + { + var resourceDefinition = await this.ApiClient.ManageNamespaced().GetDefinitionAsync().ConfigureAwait(false); + this.Reduce(s => s with + { + Definition = resourceDefinition + }); + } + + /// + /// Lists all the resources managed by Synapse + /// + /// The , if any, to list the resources of + /// A new awaitable + public virtual async Task ListResourcesAsync(ResourcesFilter? filter = null) + { + try + { + this.Reduce(state => state with + { + Loading = true, + }); + var resourceList = new EquatableList(await (await this.ApiClient.ManageNamespaced().ListAsync(filter?.Namespace, filter?.LabelSelectors).ConfigureAwait(false)).OrderBy(r => r.Metadata.CreationTimestamp).ToListAsync().ConfigureAwait(false)); + this.Reduce(s => s with + { + Resources = resourceList, + Loading = false + }); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: implement proper error handling + } + } + + /// + /// Handles the specified + /// + /// The to handle + /// A new awaitable + protected virtual Task OnResourceWatchEventAsync(IResourceWatchEvent e) + { + var labelSelectors = this.Get(state => state.LabelSelectors); + if (labelSelectors != null && labelSelectors.Count > 0 && !labelSelectors.All(selector => { + if (e.Resource?.Metadata?.Labels == null || e.Resource.Metadata.Labels.Count == 0 || !e.Resource.Metadata.Labels.TryGetValue(selector.Key, out string? value)) + { + return false; + } + var label = value; + return selector.Operator switch + { + LabelSelectionOperator.Equals => !string.IsNullOrWhiteSpace(selector.Value) && selector.Value.Equals(label), + LabelSelectionOperator.NotEquals => !string.IsNullOrWhiteSpace(selector.Value) && !selector.Value.Equals(label), + LabelSelectionOperator.Contains => selector.Values != null && selector.Values.Contains(label), + LabelSelectionOperator.NotContains => selector.Values != null && !selector.Values.Contains(label), + _ => false, + }; + })) + { + return Task.CompletedTask; + } + switch (e.Type) + { + case ResourceWatchEventType.Created: + this.Reduce(state => + { + var resources = state.Resources == null ? [] : new EquatableList(state.Resources); + resources.Add(e.Resource); + return state with + { + Resources = resources + }; + }); + break; + case ResourceWatchEventType.Updated: + this.Reduce(state => + { + if (state.Resources == null) return state; + var resources = new EquatableList(state.Resources); + var resource = resources.FirstOrDefault(r => r.GetQualifiedName() == e.Resource.GetQualifiedName()); + if (resource == null) return state; + var index = resources.IndexOf(resource); + resources.Remove(resource); + resources.Insert(index, e.Resource); + return state with + { + Resources = resources + }; + }); + break; + case ResourceWatchEventType.Deleted: + this.Reduce(state => + { + if (state.Resources == null) + { + return state; + } + var resources = new EquatableList(state.Resources); + var resource = resources.FirstOrDefault(r => r.GetQualifiedName() == e.Resource.GetQualifiedName()); + if (resource == null) return state; + resources.Remove(resource); + return state with + { + Resources = resources + }; + }); + break; + default: + throw new NotSupportedException($"The specified {nameof(ResourceWatchEventType)} '{e.Type}' is not supported"); + } + return Task.CompletedTask; + } + + /// + protected override void Dispose(bool disposing) + { + if (!disposing) return; + this.ResourceWatchSubscription?.Dispose(); + base.Dispose(disposing); + } + + /// + protected override async ValueTask DisposeAsync(bool disposing) + { + if (!disposing) return; + await this.ResourceWatch.DisposeAsync().ConfigureAwait(false); + this.ResourceWatchSubscription.Dispose(); + base.Dispose(disposing); + } + +} + +/// +/// Represents a used to manage Synapse s of the specified type +/// +/// The type of s to manage +/// +/// Initializes a new +/// +/// The service used to interact with the Synapse API +/// The websocket service client +public abstract class ResourceManagementComponentStoreBase(ISynapseApiClient apiClient, ResourceWatchEventHubClient resourceEventHub) + : ResourceManagementComponentStoreBase, TResource>(apiClient, resourceEventHub) + where TResource : Resource, new() +{ + + + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourcesFilter.cs b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourcesFilter.cs new file mode 100644 index 000000000..cb5adc6e8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/ResourceManagement/ResourcesFilter.cs @@ -0,0 +1,32 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components.ResourceManagement; + +/// +/// Holds filters used to request resources +/// +public record ResourcesFilter +{ + + /// + /// Gets the , if any, the (namespaced) resources to list belong to + /// + public string? Namespace { get; set; } + + /// + /// Gets/sets a list that contains the label selectors, if any, used to filter the resources to list + /// + public EquatableList? LabelSelectors { get; set; } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/TaskInstanceDetails/TaskInstanceDetails.razor b/src/dashboard/Synapse.Dashboard/Components/TaskInstanceDetails/TaskInstanceDetails.razor new file mode 100644 index 000000000..3924c1975 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/TaskInstanceDetails/TaskInstanceDetails.razor @@ -0,0 +1,175 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components + +@if (TaskInstance != null) +{ +
+
Details
+
+
+
Name
+ @TaskInstance.Name +
+
+
Reference
+ @TaskInstance.Reference +
+
+
Status
+ @(TaskInstance.Status ?? TaskInstanceStatus.Pending) + @if (!string.IsNullOrWhiteSpace(TaskInstance.StatusReason)) + { +
@TaskInstance.StatusReason
+ } +
+
+
+
+
Start Time
+ @(TaskInstance.StartedAt?.DateTime.RelativeFormat() ?? "-") +
+
+
End Time
+ @(TaskInstance.EndedAt?.DateTime.RelativeFormat() ?? "-") +
+
+
Duration
+ @if (TaskInstance.StartedAt.HasValue == true && TaskInstance.EndedAt.HasValue == true) + { + @TaskInstance.EndedAt.Value.Subtract(TaskInstance.StartedAt.Value).ToString("hh\\:mm\\:ss\\.fff") + } + else + { + @("-") + } +
+
+
+ + + @if (TaskInstance.Error != null) + { + + } + else if (!string.IsNullOrWhiteSpace(TaskInstance.OutputReference)) + { + + } +
+
Executed Tasks
+ @if (SubTasks == null || SubTasks.Count() == 0) + { + @("-") + } + else + { + + + + + + + + + + + + + @foreach (var task in SubTasks) + { + + } + +
NameStatusStart TimeEnd TimeDuration
+ } +
Runs
+ @if (TaskInstance.Runs == null || TaskInstance.Runs.Count == 0) + { + @("-") + } + else + { + + + + + + + + + + + @foreach (var run in TaskInstance.Runs) + { + + + + + + + } + +
Start TimeEnd TimeDurationOutcome
@run.StartedAt.DateTime.RelativeFormat()@(run.EndedAt?.DateTime.RelativeFormat() ?? "-")@(run.EndedAt.HasValue ? run.EndedAt.Value.Subtract(run.StartedAt).ToString("hh\\:mm\\:ss\\.fff") : "-")@(run.Outcome ?? TaskInstanceStatus.Pending)
+ } +
Retries
+ @if (TaskInstance.Retries == null || TaskInstance.Retries.Count == 0) + { + @("-") + } + else + { + + + + + + + + + + @foreach (var retry in TaskInstance.Retries) + { + + + + + + } + +
Attempted AtCountCause
@retry.Time.DateTime.RelativeFormat()@retry.Number + +
+ } + +
Raw
+ +
+} + +@code { + [Parameter] public TaskInstance? TaskInstance { get; set; } + [Parameter] public IEnumerable? Tasks { get; set; } + + IEnumerable SubTasks + { + get + { + return TaskInstance == null ? [] : (this.Tasks ?? []).Where(t => t.ParentId == TaskInstance.Id); + } + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDetails/WorkflowDetails.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDetails/WorkflowDetails.razor new file mode 100644 index 000000000..f743fee76 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDetails/WorkflowDetails.razor @@ -0,0 +1,25 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components + + + +@code{ + + [Parameter] public Workflow Workflow { get; set; } = null!; + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CallTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CallTaskNodeViewModel.cs new file mode 100644 index 000000000..0965e8d92 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CallTaskNodeViewModel.cs @@ -0,0 +1,36 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a call task node view model +/// +public class CallTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + /// The type of call + public CallTaskNodeViewModel(string taskReference, string name, string content, string callType = "") + : base(taskReference, new() { Label = name, CssClass = "call-task-node" }) + { + Content = content; + Symbol = !string.IsNullOrEmpty(callType) ? $"{callType.ToLower()}-symbol" : "call-symbol"; + Type = "CALL" + (!string.IsNullOrEmpty(callType) ? $" - {callType.ToUpper()}" : ""); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CatchDoNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CatchDoNodeViewModel.cs new file mode 100644 index 000000000..bf065d92d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CatchDoNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a catch node view model +/// +public class CatchDoNodeViewModel + : WorkflowClusterViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public CatchDoNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name + " - Catch", CssClass = "catch-task-node" }) + { + Content = content; + Symbol = "catch-symbol"; + Type = "CATCH"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CatchNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CatchNodeViewModel.cs new file mode 100644 index 000000000..e9e7eba9c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/CatchNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a catch node view model +/// +public class CatchNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public CatchNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name + " - Catch", CssClass = "catch-task-node" }) + { + Content = content; + Symbol = "catch-symbol"; + Type = "CATCH"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/DoTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/DoTaskNodeViewModel.cs new file mode 100644 index 000000000..49bcebcef --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/DoTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class DoTaskNodeViewModel + : WorkflowClusterViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public DoTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "do-task-node" }) + { + Content = content; + Symbol = "do-symbol"; + Type = "DO"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EmitTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EmitTaskNodeViewModel.cs new file mode 100644 index 000000000..a11684014 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EmitTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class EmitTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public EmitTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "emit-task-node" }) + { + Content = content; + Symbol = "emit-symbol"; + Type = "EMIT"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EndNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EndNodeViewModel.cs new file mode 100644 index 000000000..5ff3fe779 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/EndNodeViewModel.cs @@ -0,0 +1,24 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre; + +namespace Synapse.Dashboard.Components; + +/// +/// Represents the object that holds the data required to render the view of a workflow's end node +/// +public class EndNodeViewModel() + : WorkflowNodeViewModel("end-node", new() { CssClass = "end-node", Shape = NodeShape.Circle, Width = WorkflowGraphBuilder.StartEndNodeRadius, Height = WorkflowGraphBuilder.StartEndNodeRadius }) +{ +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ExtensionTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ExtensionTaskNodeViewModel.cs new file mode 100644 index 000000000..228754c0c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ExtensionTaskNodeViewModel.cs @@ -0,0 +1,33 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class ExtensionTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + public ExtensionTaskNodeViewModel(string taskReference, string name) + : base(taskReference, new() { Label = name, CssClass = "extension-task-node" }) + { + Symbol = "extension-symbol"; + Type = "EXTENSION"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForTaskNodeViewModel.cs new file mode 100644 index 000000000..28c0be6b2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a for task node view model +/// +public class ForTaskNodeViewModel + : WorkflowClusterViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public ForTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "for-task-node" }) + { + Content = content; + Symbol = "for-symbol"; + Type = "FOR"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForkTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForkTaskNodeViewModel.cs new file mode 100644 index 000000000..706a13928 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ForkTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class ForkTaskNodeViewModel + : WorkflowClusterViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public ForkTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "fork-task-node" }) + { + Content = content; + Symbol = "fork-symbol"; + Type = "FORK"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/IWorkflowNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/IWorkflowNodeViewModel.cs new file mode 100644 index 000000000..c0e8dac10 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/IWorkflowNodeViewModel.cs @@ -0,0 +1,59 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse.Dashboard.Components; + +/// +/// Defines the fundamentals of a workflow node +/// +public interface IWorkflowNodeViewModel +{ + + /// + /// Get/sets the id of the symbol of the node. + /// + public string? Symbol { get; } + + /// + /// Gets/sets the displayed type of the node + /// + public string? Type { get; } + + /// + /// Gets/sets the displayed content of the node + /// + public string? Content { get; } + + /// + /// Gets/Sets the number of active s for which the task described by the node is operative + /// + int OperativeInstancesCount { get; set; } + + /// + /// Gets/Sets the number of active faulted s for which the task described by the node is faulted + /// + int FaultedInstancesCount { get; set; } + + /// + /// Gets/Sets a boolean indicating the node is a cluster + /// + bool IsCluster { get; } + + /// + /// Resets the operative and faulted instances counts + /// + void ResetInstancesCount(); + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ListenTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ListenTaskNodeViewModel.cs new file mode 100644 index 000000000..ce5f96201 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/ListenTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class ListenTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public ListenTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "listen-task-node" }) + { + Content = content; + Symbol = "listen-symbol"; + Type = "LISTEN"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/NodeViewModelConfig.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/NodeViewModelConfig.cs new file mode 100644 index 000000000..ed95028d6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/NodeViewModelConfig.cs @@ -0,0 +1,88 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre; +using Neuroglia.Blazor.Dagre.Templates; + +namespace Synapse.Dashboard.Components; + +/// +/// Holds the parameters used to instanciate a graph node +/// +public class NodeViewModelConfig +{ + /// + /// Gets/sets the label of the node + /// + public string Label { get; set; } = string.Empty; + + /// + /// Get/sets the label template, if any. If none is provided, the label is used instead. + /// + public RenderFragment? LabelTemplate { get; set; } + + /// + /// Get/sets the id of the symbol of the node. + /// + public string? Symbol { get; set; } + + /// + /// Gets/sets the additional css classes of the node + /// + public string CssClass { get; set; } = string.Empty; + + /// + /// Gets/sets the X coordinate of the node on the graph + /// + public double X { get; set; } = 0; + + /// + /// Gets/sets the Y coordinate of the node on the graph + /// + public double Y { get; set; } = 0; + + /// + /// Gets/sets the width of the node + /// + public double? Width { get; set; } = Constants.NodeWidth * 5; + + /// + /// Gets/sets the height of the node + /// + public double? Height { get; set; } = Constants.NodeHeight * 2.5; + + /// + /// Gets/sets the X radius of the node + /// + public double? RadiusX { get; set; } = Constants.NodeRadius * 2; + + /// + /// Gets/sets the Y radius of the node + /// + public double? RadiusY { get; set; } = Constants.NodeRadius * 2; + + /// + /// Gets/sets parent node id + /// + public string? ParentId { get; set; } + + /// + /// Gets/sets the shape the node + /// + public string Shape { get; set; } = SynapseNodeShape.Cartouche; + + /// + /// Gets/sets the type the component used to render the node + /// + public Type? ComponentType { get; set; } = typeof(NodeTemplate); +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/PortNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/PortNodeViewModel.cs new file mode 100644 index 000000000..25542c016 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/PortNodeViewModel.cs @@ -0,0 +1,24 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre; + +namespace Synapse.Dashboard.Components; + +/// +/// Used as a ghost node to create a top padding in clusters +/// +public class PortNodeViewModel(string id) + : WorkflowNodeViewModel(id, new() { CssClass = "port-node", Shape = NodeShape.Rectangle, Width = 1, Height = 1 }) +{ +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RaiseTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RaiseTaskNodeViewModel.cs new file mode 100644 index 000000000..4640d52f9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RaiseTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class RaiseTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public RaiseTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "raise-task-node" }) + { + Content = content; + Symbol = "raise-symbol"; + Type = "RAISE"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RunTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RunTaskNodeViewModel.cs new file mode 100644 index 000000000..83fe4469d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/RunTaskNodeViewModel.cs @@ -0,0 +1,38 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.VisualBasic; + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class RunTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + /// The type of run + public RunTaskNodeViewModel(string taskReference, string name, string content, string runType = "") + : base(taskReference, new() { Label = name, CssClass = "run-task-node" }) + { + Content = content; + Symbol = !string.IsNullOrEmpty(runType) ? $"{runType.ToLower()}-symbol" : "run-symbol"; + Type = "RUN" + (!string.IsNullOrEmpty(runType) ? $" - {runType.ToUpper()}" : ""); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SetTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SetTaskNodeViewModel.cs new file mode 100644 index 000000000..5a5407eec --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SetTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a set task node view model +/// +public class SetTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public SetTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "set-task-node" }) + { + Content = content; + Symbol = "set-symbol"; + Type = "SET"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/StartNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/StartNodeViewModel.cs new file mode 100644 index 000000000..e04dd9a05 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/StartNodeViewModel.cs @@ -0,0 +1,30 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre; + +namespace Synapse.Dashboard.Components; + +/// +/// Represents the object that holds the data required to render the view of a workflow's start node +/// +public class StartNodeViewModel(bool hasSuccessor = false) + : WorkflowNodeViewModel("start-node", new() { CssClass = "start-node", Shape = NodeShape.Circle, Width = WorkflowGraphBuilder.StartEndNodeRadius, Height = WorkflowGraphBuilder.StartEndNodeRadius }) +{ + + /// + /// Gets a boolean indicating whether or not the node has a successor + /// + public bool HasSuccessor { get; } = hasSuccessor; + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SwitchTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SwitchTaskNodeViewModel.cs new file mode 100644 index 000000000..96beb1e65 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SwitchTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class SwitchTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public SwitchTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "switch-task-node" }) + { + Content = content; + Symbol = "switch-symbol"; + Type = "SWITCH"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SynapseNodeShape.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SynapseNodeShape.cs new file mode 100644 index 000000000..36b0e9e18 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/SynapseNodeShape.cs @@ -0,0 +1,28 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +using Neuroglia.Blazor.Dagre.Models; + +namespace Synapse.Dashboard; + +/// +/// Holds specific Synapse's shapes +/// +public static class SynapseNodeShape +{ + /// + /// A cartouched rectangle + /// + public const string Cartouche = "cartouche"; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeActivityBadge.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeActivityBadge.razor new file mode 100644 index 000000000..fd964e676 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeActivityBadge.razor @@ -0,0 +1,123 @@ +@* + Copyright © 2022-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard +@using System.Collections.ObjectModel + +@if (ActiveInstances > 0) +{ + + + +
+ @ActiveInstances +
+
+
+} +@if (CompensatedInstances > 0) +{ + + > + + +
+ @CompensatedInstances +
+
+
+} +@if (FaultedInstances > 0) +{ + + > + + +
+ @FaultedInstances +
+
+
+} + +@code { + [CascadingParameter(Name = "ActiveInstances")] public int ActiveInstances { get; set; } = 0; + + [CascadingParameter(Name = "FaultedInstances")] public int FaultedInstances { get; set; } = 0; + + [CascadingParameter(Name = "CompensatedInstances")] public int CompensatedInstances { get; set; } = 0; + + [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; + + protected virtual string ActivityRadius { get; set; } = ""; + protected virtual string ActiveX { get; set; } = ""; + protected virtual string ActiveY { get; set; } = ""; + protected virtual string FaultedX { get; set; } = ""; + protected virtual string FaultedY { get; set; } = ""; + protected virtual string CompensatedX { get; set; } = ""; + protected virtual string CompensatedY { get; set; } = ""; + protected virtual string CountSize { get; set; } = ""; + protected virtual string CountXY { get; set; } = ""; + double radius = 15; + + protected override void OnInitialized() + { + base.OnInitialized(); + this.ActivityRadius = radius.ToInvariantString(); + this.CountSize = (radius * 2).ToInvariantString(); + this.CountXY = (0 - radius).ToInvariantString(); + } + + protected override void OnParametersSet() + { + double divider = this.Node.Shape == NodeShape.Circle || this.Node.Shape == NodeShape.Ellipse ? 1 : 2; + double offset = radius * 2; + double activeOffset = 0; + double compensatedOffset = 0; + double faultedOffset = 0; + if (this.ActiveInstances > 0) + { + compensatedOffset += 1; + faultedOffset += 1; + if (this.CompensatedInstances > 0) + { + faultedOffset += 1; + } + } + else if (this.CompensatedInstances > 0) + { + faultedOffset += 1; + } + this.ActiveX = (((this.Node.Bounds?.Width ?? Neuroglia.Blazor.Dagre.Constants.ClusterWidth) - (offset * activeOffset)) / divider).ToInvariantString(); + this.CompensatedX = (((this.Node.Bounds?.Width ?? Neuroglia.Blazor.Dagre.Constants.ClusterWidth) - (offset * compensatedOffset)) / divider).ToInvariantString(); + this.FaultedX = (((this.Node.Bounds?.Width ?? Neuroglia.Blazor.Dagre.Constants.ClusterWidth) - (offset * faultedOffset)) / divider).ToInvariantString(); + this.ActiveY = ((this.Node.Bounds?.Height ?? Neuroglia.Blazor.Dagre.Constants.ClusterHeight) / divider).ToInvariantString(); + this.CompensatedY = ((this.Node.Bounds?.Height ?? Neuroglia.Blazor.Dagre.Constants.ClusterHeight) / divider).ToInvariantString(); + this.FaultedY = ((this.Node.Bounds?.Height ?? Neuroglia.Blazor.Dagre.Constants.ClusterHeight) / divider).ToInvariantString(); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeLabelTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeLabelTemplate.razor new file mode 100644 index 000000000..dd4e1abb6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeLabelTemplate.razor @@ -0,0 +1,30 @@ +@namespace Synapse.Dashboard + +@if (LabelTemplate != null) +{ + @LabelTemplate +} +else if (!string.IsNullOrWhiteSpace(Node.Label) ) +{ + + +
@Node.Label
+
+
+} + +@code { + + BoundingBox bounds => Node.Bounds!; + string x => bounds.X.ToInvariantString() ?? string.Empty; + string y => bounds.Y.ToInvariantString() ?? string.Empty; + string width => bounds.Width.ToInvariantString() ?? string.Empty; + string height => bounds.Height.ToInvariantString() ?? Neuroglia.Blazor.Dagre.Constants.LabelHeight.ToInvariantString(); + + [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; + [CascadingParameter(Name = "LabelTemplate")] public RenderFragment? LabelTemplate { get; set; } = null!; + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeShapeTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeShapeTemplate.razor new file mode 100644 index 000000000..7e5715bfa --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeShapeTemplate.razor @@ -0,0 +1,63 @@ +@namespace Synapse.Dashboard + + + @if (Node.Shape == NodeShape.Circle) + { + + } + else if (Node.Shape == NodeShape.Ellipse) + { + + } + else if (Node.Shape == SynapseNodeShape.Cartouche) + { + if (!WorkflowNode.IsCluster) + { + + + } + else + { + + + } + } + else + { + + } + + +@code { + + BoundingBox bounds => Node.Bounds!; + [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; + protected virtual IWorkflowNodeViewModel WorkflowNode => (IWorkflowNodeViewModel)this.Node; + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeSymbolTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeSymbolTemplate.razor new file mode 100644 index 000000000..bd1cce00f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeSymbolTemplate.razor @@ -0,0 +1,40 @@ +@namespace Synapse.Dashboard + +@if (!string.IsNullOrWhiteSpace(Symbol)) +{ + + + + +
+            @Constants.ClusterHeight
+            @bounds.Height
+            @_symbolSize
+        
+
+} + +@code { + + Double _symbolSize = 30; + BoundingBox bounds => Node.Bounds!; + string? width => Node.Shape != SynapseNodeShape.Cartouche ? + (bounds.Width / 2).ToInvariantString() : + _symbolSize.ToInvariantString(); + string? height => Node.Shape != SynapseNodeShape.Cartouche ? + (bounds.Height / 2).ToInvariantString() : + _symbolSize.ToInvariantString(); + string? x => Node.Shape != SynapseNodeShape.Cartouche ? + (0 - bounds.Width / 4).ToInvariantString() : + (0 - (bounds.Width - _symbolSize) / 2).ToInvariantString(); + string? y => Node.Shape != SynapseNodeShape.Cartouche ? + (0 - bounds.Height / 4).ToInvariantString() : + !WorkflowNode.IsCluster ? + (0 - _symbolSize / 2).ToInvariantString() : + (0 - (bounds.Height + _symbolSize - (Constants.ClusterHeight+10)) / 2).ToInvariantString(); + + [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; + [CascadingParameter(Name = "Symbol")] public string? Symbol { get; set; } + protected virtual IWorkflowNodeViewModel WorkflowNode => (IWorkflowNodeViewModel)this.Node; + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeTemplate.razor new file mode 100644 index 000000000..9775dd14a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/Templates/NodeTemplate.razor @@ -0,0 +1,70 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard +@inherits Neuroglia.Blazor.Dagre.Templates.NodeTemplate + + + + + + + + + + + + + + + + @if (Graph.EnableProfiling) { + + } + + + + +@code { + protected virtual IWorkflowNodeViewModel WorkflowNode => (IWorkflowNodeViewModel)this.Node; + BoundingBox bounds => Node.Bounds!; + + RenderFragment? LabelTemplate => !string.IsNullOrWhiteSpace(WorkflowNode.Type) || !string.IsNullOrWhiteSpace(WorkflowNode.Content) ? (__builder) => + { + + +
+

+ @if (!string.IsNullOrWhiteSpace(Node.Label)) + { + @Node.Label + } +

+ @if (!string.IsNullOrWhiteSpace(WorkflowNode.Type)) { +

@WorkflowNode.Type

+ } + @if (!string.IsNullOrWhiteSpace(WorkflowNode.Content)) + { +
@WorkflowNode.Content
+ } +
+
+
+ } : null; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryNodeViewModel.cs new file mode 100644 index 000000000..3255aae0a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a catch node view model +/// +public class TryNodeViewModel + : WorkflowClusterViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public TryNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name + " - Try", CssClass = "try-task-node" }) + { + Content = content; + Symbol = "try-symbol"; + Type = "TRY"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryTaskNodeViewModel.cs new file mode 100644 index 000000000..ec89e2b0d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/TryTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a try task node view model +/// +public class TryTaskNodeViewModel + : WorkflowClusterViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public TryTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "try-catch-task-node" }) + { + Content = content; + Symbol = "try-symbol"; + Type = "TRY..CATCH"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WaitTaskNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WaitTaskNodeViewModel.cs new file mode 100644 index 000000000..490f73a64 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WaitTaskNodeViewModel.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a composite task node view model +/// +public class WaitTaskNodeViewModel + : WorkflowNodeViewModel +{ + /// + /// Initializes a new + /// + /// The node task reference + /// The node name + /// The node content + public WaitTaskNodeViewModel(string taskReference, string name, string content) + : base(taskReference, new() { Label = name, CssClass = "wait-task-node" }) + { + Content = content; + Symbol = "wait-symbol"; + Type = "WAIT"; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowClusterViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowClusterViewModel.cs new file mode 100644 index 000000000..865e477e5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowClusterViewModel.cs @@ -0,0 +1,87 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre.Models; +using ServerlessWorkflow.Sdk.Models; +using System.Text.Json.Serialization; + +namespace Synapse.Dashboard.Components; + +/// +/// Represents a +/// +public abstract class WorkflowClusterViewModel + : ClusterViewModel, IWorkflowNodeViewModel +{ + int _operativeInstances = 0; + int _faultedInstances = 0; + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string? Symbol { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string? Type { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string? Content { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public int OperativeInstancesCount + { + get => this._operativeInstances; + set + { + this._operativeInstances = value; + this.OnChange(); + } + } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public int FaultedInstancesCount + { + get => this._faultedInstances; + set + { + this._faultedInstances = value; + this.OnChange(); + } + } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public bool IsCluster => true; + + /// + /// Initializes a new + /// + /// The node task reference + /// The for the node + public WorkflowClusterViewModel(string taskReference, NodeViewModelConfig? config = null) + : base(null, config?.Label, config?.CssClass, config?.Shape, config?.Width ?? 0, config?.Height ?? 0, config?.RadiusX ?? 0, config?.RadiusY ?? 0, config?.X ?? 0, config?.Y ?? 0, config?.ComponentType, config?.ParentId) + { + this.Id = taskReference; + } + + /// + public void ResetInstancesCount() + { + this._operativeInstances = 0; + this._faultedInstances = 0; + this.OnChange(); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagram.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagram.razor new file mode 100644 index 000000000..e5598ccee --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagram.razor @@ -0,0 +1,141 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@using ServerlessWorkflow.Sdk.Models +@using Synapse.Dashboard.Components.WorkflowDiagramStateManagement +@namespace Synapse.Dashboard +@inherits StatefulComponent + +@if (graph != null) +{ + + + + + + + + + + + + + + + + @if (isLegendVisible) + { + + } +} + +@code { + [Parameter] public EventCallback> OnMouseUp { get; set; } + WorkflowDefinition? workflowDefinition = null; + [Parameter] public WorkflowDefinition WorkflowDefinition { get; set; } = null!; + WorkflowDiagramOrientation orientation = default!; + [Parameter] public WorkflowDiagramOrientation Orientation { get; set; } = WorkflowDiagramOrientation.TopToBottom; + EquatableList workflowInstances = []; + [Parameter] public EquatableList WorkflowInstances { get; set; } = []; + + IGraphViewModel? graph; + IDagreGraphOptions? options = null; + bool isLegendVisible = false; + bool isDirty = true; // moving isDirty to the State/Store seems to have unwanted behavior, the `ShouldRender` method doesn't seem to behave properly. + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync().ConfigureAwait(false); + Store.WorkflowDefinition.Subscribe(value => OnStateChanged(_ => workflowDefinition = value), token: CancellationTokenSource.Token); + Store.WorkflowInstances.Subscribe(value => OnStateChanged(_ => workflowInstances = value), token: CancellationTokenSource.Token); + Store.Orientation.Subscribe(value => OnStateChanged(_ => orientation = value), token: CancellationTokenSource.Token); + Store.IsLegendVisible.Subscribe(value => OnStateChanged(_ => + { + isLegendVisible = value; + isDirty = true; + }), token: CancellationTokenSource.Token); + Observable.CombineLatest( + Store.Graph.Where(g => g != null), + Store.Options.Where(o => o != null), + (graph, options) => (graph, options) + ).Subscribe((values) => + { + var (newGraph, newOptions) = values; + OnStateChanged(_ => + { + graph = newGraph; + options = newOptions; + isDirty = true; + }); + }, token: CancellationTokenSource.Token); + } + + protected override void OnParametersSet() + { + base.OnParametersSet(); + if (this.Orientation != this.orientation) + { + this.Store.SetOrientation(this.Orientation); + } + if (this.WorkflowDefinition != null && this.WorkflowDefinition != this.workflowDefinition) + { + this.Store.SetWorkflowDefinition(this.WorkflowDefinition); + } + if (this.WorkflowInstances != this.workflowInstances) + { + this.Store.SetWorkflowInstances(this.WorkflowInstances); + } + } + + protected override bool ShouldRender() + { + if (!this.isDirty) return false; + this.isDirty = false; + return true; + } + + protected void HandleOnMouseDown(GraphEventArgs e) + { + if (e.GraphElement == null && graph != null) + { + graph.CssClass = "grabbing"; + isDirty = true; + } + } + + protected async Task HandleOnMouseUp(GraphEventArgs e) + { + if (e.GraphElement == null && graph != null) + { + graph.CssClass = ""; + isDirty = true; + } + if (OnMouseUp.HasDelegate) + { + await OnMouseUp.InvokeAsync(e); + } + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramLegend.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramLegend.razor new file mode 100644 index 000000000..9f47e2075 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramLegend.razor @@ -0,0 +1,55 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard + +
+ + @for(int i = 0, c = nodeTypes.Count(); i + + +
+ @nodeType.ToUpper().Replace("-", "..") +
+
+ + } +
+
+ +@code { + int width = 100; + int height = 25; + IEnumerable nodeTypes = [ + "call", + "do", + "fork", + "for", + "listen", + "run", + "set", + "switch", + "try-catch", + "try", + "emit", + "wait", + "catch", + "raise" + ]; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramOrientation.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramOrientation.cs new file mode 100644 index 000000000..4c721d1ea --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramOrientation.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Components; + +/// +/// Enumerates all supported workflow diagram orientations +/// +public enum WorkflowDiagramOrientation +{ + /// + /// Indicates a top to bottom diagram + /// + TopToBottom, + /// + /// Indicates a left to right diagram + /// + LeftToRight +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramState.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramState.cs new file mode 100644 index 000000000..7dc30ae05 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramState.cs @@ -0,0 +1,43 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using ServerlessWorkflow.Sdk.Models; +using Synapse.Resources; + +namespace Synapse.Dashboard.Components.WorkflowDiagramStateManagement; + +/// +/// Represents the state of a +/// +public record WorkflowDiagramState +{ + /// + /// Gets/sets the to build a diagram for + /// + public WorkflowDefinition? WorkflowDefinition { get; set; } = null; + + /// + /// Gets/sets the of the diagram + /// + public WorkflowDiagramOrientation Orientation { get; set; } = default!; + + /// + /// Gets/sets the s to get the activity counts from + /// + public EquatableList WorkflowInstances { get; set; } = []; + + /// + /// Gets/sets a boolean indicating if the legend is visible + /// + public bool IsLegendVisible { get; set; } = false; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramStore.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramStore.cs new file mode 100644 index 000000000..8baa722ee --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowDiagramStore.cs @@ -0,0 +1,177 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre; +using Neuroglia.Blazor.Dagre.Models; +using ServerlessWorkflow.Sdk.Models; +using Synapse.Resources; +using System.Text.RegularExpressions; + +namespace Synapse.Dashboard.Components.WorkflowDiagramStateManagement; + +/// +/// Represents the of a component +/// +/// The service used build the workflow graph +public class WorkflowDiagramStore( + IWorkflowGraphBuilder workflowGraphBuilder +) + : ComponentStore(new()) +{ + + /// + /// Gets the service used build the workflow graph + /// + protected IWorkflowGraphBuilder WorkflowGraphBuilder { get; } = workflowGraphBuilder; + + /// + /// Gets/sets a reference to the component used to display the legend + /// + public Modal? LegendModal { get; set; } + + /// + /// Gets/set a refrence to the component + /// + public DagreGraph? DagreGraph; + + #region Selectors + /// + /// Gets an used to observe changes + /// + public IObservable WorkflowDefinition => this.Select(state => state.WorkflowDefinition).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Orientation => this.Select(state => state.Orientation).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable> WorkflowInstances => this.Select(state => state.WorkflowInstances).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable IsLegendVisible => this.Select(state => state.IsLegendVisible).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Options => this.Orientation + .Select(orientation => new DagreGraphOptions() + { + Direction = orientation == WorkflowDiagramOrientation.LeftToRight ? DagreGraphDirection.LeftToRight : DagreGraphDirection.TopToBottom + }) + .DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable RawGraph => this.WorkflowDefinition + .Where(workflowDefinition => workflowDefinition != null) + .Select((workflowDefinition) => this.WorkflowGraphBuilder.Build(workflowDefinition!)) + .DistinctUntilChanged() + .Publish() + .RefCount() + ; + + /// + /// Gets an used to observe , with its activities, changes + /// + public IObservable Graph => Observable.CombineLatest( + this.RawGraph, + this.WorkflowInstances, + (graph, instances) => + { + var tasks = instances.SelectMany(instance => instance.Status?.Tasks ?? []); + var allNodes = graph.AllNodes.Values.Concat(graph.AllClusters.Values); + foreach (var node in allNodes) + { + int activeCount, faultedCount; + if (node.Id == "start-node") + { + activeCount = instances.Count(instance => instance.Status == null || instance.Status?.Phase == WorkflowInstanceStatusPhase.Pending || instance.Status?.Phase == WorkflowInstanceStatusPhase.Waiting); + faultedCount = instances.Count(instance => (instance.Status?.Tasks == null || instance.Status.Tasks.Count == 0) && (instance.Status?.Phase == WorkflowInstanceStatusPhase.Cancelled || instance.Status?.Phase == WorkflowInstanceStatusPhase.Faulted)); + } + else if (node.Id == "end-node") + { + activeCount = instances.Count(instance => instance.Status?.Phase == WorkflowInstanceStatusPhase.Completed); + faultedCount = instances.Count(instance => instance.Status?.Tasks?.Count > 0 && (instance.Status?.Phase == WorkflowInstanceStatusPhase.Cancelled || instance.Status?.Phase == WorkflowInstanceStatusPhase.Faulted)); + } + else + { + var nodeTasks = tasks.Where(task => Regex.Replace(task.Reference.ToString(), @"/for/\d*", "") == node.Id); + activeCount = nodeTasks.Count(task => task.Status == TaskInstanceStatus.Running); + faultedCount = nodeTasks.Count(task => task.Status == TaskInstanceStatus.Faulted || task.Status == TaskInstanceStatus.Cancelled); + } + ((IWorkflowNodeViewModel)node).OperativeInstancesCount = activeCount; + ((IWorkflowNodeViewModel)node).FaultedInstancesCount = faultedCount; + } + return graph; + }) + .DistinctUntilChanged(); + #endregion + + #region Setters + /// + /// Sets the state 's value + /// + /// The new value + public void SetWorkflowDefinition(WorkflowDefinition? workflowDefinition) + { + this.Reduce(state => state with + { + WorkflowDefinition = workflowDefinition + }); + } + + /// + /// Sets the state 's value + /// + /// The new value + public void SetOrientation(WorkflowDiagramOrientation orientation) + { + this.Reduce(state => state with + { + Orientation = orientation + }); + } + + /// + /// Sets the state 's value + /// + /// The new value + public void SetWorkflowInstances(EquatableList workflowInstances) + { + this.Reduce(state => state with + { + WorkflowInstances = workflowInstances + }); + } + #endregion + + #region Actions + /// + /// Toggles the legend visibily + /// + public void ToggleLegendAsync() + { + var isLegendVisible = this.Get(state => state.IsLegendVisible); + this.Reduce(state => state with + { + IsLegendVisible = !isLegendVisible + }); + } + #endregion +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowNodeViewModel.cs new file mode 100644 index 000000000..291708ad0 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowDiagram/WorkflowNodeViewModel.cs @@ -0,0 +1,88 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre.Models; +using System.Text.Json.Serialization; + +namespace Synapse.Dashboard.Components; + +/// +/// Represents the base class for all workflow-related node models +/// +public abstract class WorkflowNodeViewModel + : NodeViewModel, IWorkflowNodeViewModel +{ + + int _operativeInstances = 0; + int _faultedInstances = 0; + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string? Symbol { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string? Type { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string? Content { get; init; } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public int OperativeInstancesCount + { + get => _operativeInstances; + set + { + _operativeInstances = value; + OnChange(); + } + } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public int FaultedInstancesCount + { + get => _faultedInstances; + set + { + _faultedInstances = value; + OnChange(); + } + } + + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public bool IsCluster => false; + + /// + /// Initialiazes a new + /// + /// The node task reference + /// The for the node + public WorkflowNodeViewModel(string taskReference, NodeViewModelConfig? config = null) + : base(config?.Label, config?.CssClass, config?.Shape, config?.Width ?? 0, config?.Height ?? 0, config?.RadiusX ?? 0, config?.RadiusY ?? 0, config?.X ?? 0, config?.Y ?? 0, config?.ComponentType, config?.ParentId) + { + this.Id = taskReference; + } + + /// + public void ResetInstancesCount() + { + _operativeInstances = 0; + _faultedInstances = 0; + OnChange(); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceCreation/WorkflowInstanceCreation.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceCreation/WorkflowInstanceCreation.razor new file mode 100644 index 000000000..109f4b97e --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceCreation/WorkflowInstanceCreation.razor @@ -0,0 +1,61 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@using ServerlessWorkflow.Sdk.Models +@inject MonacoInterop MonacoInterop +@inject IJsonSerializer Serializer + + +
+ +
+ +@code { + string payload = string.Empty; + string modelName = string.Empty; + + [Parameter] public WorkflowDefinition? WorkflowDefinition { get; set; } + [Parameter] public EventCallback OnCreate { get; set; } + + void OnTextChanged(string value) + { + payload = value; + } + + async Task OnStart() + { + if (OnCreate.HasDelegate) + { + await OnCreate.InvokeAsync(payload); + } + } + + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + if (this.WorkflowDefinition?.Input?.Schema?.Document != null) + { + modelName = this.WorkflowDefinition.Document.Name + "-" + this.WorkflowDefinition.Document.Version; + await this.MonacoInterop.AddValidationSchemaAsync(this.Serializer.SerializeToText(this.WorkflowDefinition.Input.Schema.Document), $"https://synapse.io/schemas/{modelName}.json", $"{modelName}*").ConfigureAwait(false); + } + + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/CorrelationContextRow.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/CorrelationContextRow.razor new file mode 100644 index 000000000..e7e8a44a9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/CorrelationContextRow.razor @@ -0,0 +1,58 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components + + + @Kvp.Key + @(Kvp.Value?.Status ?? CorrelationContextStatus.Inactive) + + +@if (isOpen) +{ + + + @if (Kvp.Value == null) + { + @("-") + } + else + { + + + + } + + +} + +@code { + [Parameter] public KeyValuePair Kvp { get; set; } + bool isOpen = false; + Collapse? collapse; + + async Task OnToggleRow() + { + isOpen = !isOpen; + StateHasChanged(); + await Task.Delay(1); + if (collapse != null) + { + await collapse.ToggleAsync(); + } + StateHasChanged(); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/TaskInstanceRow.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/TaskInstanceRow.razor new file mode 100644 index 000000000..a644e4bbf --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/TaskInstanceRow.razor @@ -0,0 +1,65 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components + +@if (TaskInstance != null) { + + @TaskInstance.Reference + @(TaskInstance.Status ?? TaskInstanceStatus.Pending) + @(TaskInstance.StartedAt?.DateTime.RelativeFormat() ?? "-") + @(TaskInstance.EndedAt?.DateTime.RelativeFormat() ?? "-") + + @if (TaskInstance.StartedAt.HasValue == true && TaskInstance.EndedAt.HasValue == true) + { + @TaskInstance.EndedAt.Value.Subtract(TaskInstance.StartedAt.Value).ToString("hh\\:mm\\:ss\\.fff") + } + else + { + @("-") + } + + + + @if (isOpen) { + + + + + + + + } +} + +@code { + [Parameter] public TaskInstance? TaskInstance { get; set; } + [Parameter] public IEnumerable? Tasks { get; set; } + bool isOpen = false; + Collapse? collapse; + + async Task OnToggleRow() + { + isOpen = !isOpen; + StateHasChanged(); + await Task.Delay(1); // tick to render the collapse, otherwise it will be null and won't show + if (collapse != null) + { + await collapse.ToggleAsync(); + } + StateHasChanged(); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor new file mode 100644 index 000000000..8bba2e892 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor @@ -0,0 +1,207 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components + +@if (workflowInstance != null) +{ +
+
Details
+
+
+
Name
+ @workflowInstance.GetName() +
+
+
Namespace
+ @workflowInstance.GetNamespace() +
+
+
Status
+ @(workflowInstance.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending) +
+
+
+
+
Start Time
+ @(workflowInstance.Status?.StartedAt?.DateTime.RelativeFormat() ?? "-") +
+
+
End Time
+ @(workflowInstance.Status?.EndedAt?.DateTime.RelativeFormat() ?? "-") +
+
+
Duration
+ @if (workflowInstance.Status?.StartedAt.HasValue == true && workflowInstance.Status?.EndedAt.HasValue == true) + { + @workflowInstance.Status.EndedAt.Value.Subtract(workflowInstance.Status.StartedAt.Value).ToString("hh\\:mm\\:ss\\.fff") + } + else + { + @("-") + } +
+
+
+ + + @if (workflowInstance.Status?.Error != null) + { + + } + else if (!string.IsNullOrWhiteSpace(workflowInstance.Status?.OutputReference)) + { + + } +
+
Executed Tasks
+ @if (workflowInstance.Status?.Tasks == null || workflowInstance.Status.Tasks.Count == 0) + { + @("-") + } + else + { + + + + + + + + + + + + + @foreach (var task in workflowInstance.Status.Tasks.Where(task => string.IsNullOrWhiteSpace(task.ParentId)) ) + { + + } + +
NameStatusStart TimeEnd TimeDuration
+ } +
Runs
+ @if (workflowInstance.Status?.Runs == null || workflowInstance.Status.Runs.Count == 0) + { + @("-") + } + else + { + + + + + + + + + + @foreach (var run in workflowInstance.Status.Runs) + { + + + + + + } + +
Start TimeEnd TimeDuration
@run.StartedAt.DateTime.RelativeFormat()@(run.EndedAt?.DateTime.RelativeFormat() ?? "-")@(run.EndedAt.HasValue ? run.EndedAt.Value.Subtract(run.StartedAt).ToString("hh\\:mm\\:ss\\.fff") : "-")
+ } +
Correlation
+ @if (workflowInstance.Status?.Correlation == null) + { + @("-") + } + else + { +
Keys
+ @if (workflowInstance.Status?.Correlation?.Keys == null || workflowInstance.Status.Correlation.Keys.Count == 0) + { + @("-") + } + else + { + + + + + + + + + @foreach (var keyValuePair in workflowInstance.Status.Correlation.Keys) + { + + + + + } + +
NameValue
@keyValuePair.Key@keyValuePair.Value
+ } +
Contexts
+ @if (workflowInstance.Status?.Correlation?.Contexts == null || workflowInstance.Status.Correlation.Contexts.Count == 0) + { + @("-") + } + else + { + + + + + + + + + + @foreach (var keyValuePair in workflowInstance.Status.Correlation.Contexts) + { + + } + +
NameStatus
+ } + } +
Logs
+ +
Raw
+ +
+} + +@code { + private WorkflowInstance? workflowInstance; + [Parameter] public WorkflowInstance? WorkflowInstance { get; set; } + + protected override void OnParametersSet() + { + base.OnParametersSet(); + if (workflowInstance != WorkflowInstance) + { + workflowInstance = WorkflowInstance; + shouldRender = true; + } + } + + bool shouldRender = true; + protected override bool ShouldRender() + { + if (!shouldRender) return false; + shouldRender = false; + return true; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.css b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.css new file mode 100644 index 000000000..19f8c789b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.css @@ -0,0 +1,3 @@ +.workflow-instance-details .label { + font-weight: 600; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.min.css b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.min.css new file mode 100644 index 000000000..3ca3fce6d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.min.css @@ -0,0 +1 @@ +.workflow-instance-details .label{font-weight:600;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.scss b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.scss new file mode 100644 index 000000000..eca52e91c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.scss @@ -0,0 +1,5 @@ +.workflow-instance-details { + .label { + font-weight: 600; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/State.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/State.cs new file mode 100644 index 000000000..b6311bf8b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/State.cs @@ -0,0 +1,47 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse.Dashboard.Components.WorkflowInstanceLogsStateManagement; + +/// +/// Represents the state of a +/// +public record WorkflowInstanceLogsState +{ + /// + /// Gets/sets the 's name + /// + public string Name { get; set; } = string.Empty; + + /// + /// Gets/sets the 's namespace + /// + public string Namespace { get; set; } = string.Empty; + + /// + /// Gets/sets a boolean indicating if the logs are being loaded + /// + public bool IsLoading { get; set; } = false; + + /// + /// Gets/sets a boolean indicating if the logs panel is expanded + /// + public bool IsExpanded { get; set; } = false; + + /// + /// Gets/sets the 's logs + /// + public IEnumerable Logs { get; set; } = []; +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/Store.cs b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/Store.cs new file mode 100644 index 000000000..ed9454992 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/Store.cs @@ -0,0 +1,251 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using BlazorBootstrap; +using Neuroglia.Data.Infrastructure; +using Synapse.Api.Client.Services; +using System.Text.RegularExpressions; + +namespace Synapse.Dashboard.Components.WorkflowInstanceLogsStateManagement; + +/// +/// Represents the of a component +/// +/// The service used interact with Synapse API +/// The service used to build a bridge with JS +public class WorkflowInstanceLogsStore( + ISynapseApiClient apiClient, + JSInterop jsInterop +) + : ComponentStore(new()) +{ + + CancellationTokenSource _watchCancellationTokenSource = new CancellationTokenSource(); + + /// + /// Gets the service used to interact with the Synapse API + /// + protected ISynapseApiClient ApiClient { get; } = apiClient; + + /// + /// Gets the service used to build a bridge with JS + /// + protected JSInterop JSInterop { get; } = jsInterop; + + /// + /// Gets/sets the logs panel + /// + public Collapse? Collapse { get; set; } + + /// + /// The containing the logs + /// + public ElementReference? LogsContainer { get; set; } = null; + + #region Selectors + /// + /// Gets an used to observe changes + /// + public IObservable Name => this.Select(state => state.Name) + .Where(name => !string.IsNullOrWhiteSpace(name)) + .DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Namespace => this.Select(state => state.Namespace) + .Where(@namespace => !string.IsNullOrWhiteSpace(@namespace)) + .DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable IsLoading => this.Select(state => state.IsLoading).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable IsExpanded => this.Select(state => state.IsExpanded).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable> Logs => this.Select(state => state.Logs).DistinctUntilChanged(); + + #endregion + + #region Setters + /// + /// Sets the state's + /// + /// The new name + public void SetName(string name) + { + this.Reduce(state => state with + { + Name = name + }); + } + + /// + /// Sets the state's + /// + /// The new namespace + public void SetNamespace(string @namespace) + { + this.Reduce(state => state with { + Namespace = @namespace + }); + } + #endregion + + #region Actions + /// + /// Toggles the panel + /// + /// An awaitable task + public async Task ToggleAsync() + { + if (this.Collapse != null) + { + var isExpanded = !this.Get(state => state.IsExpanded); + await (isExpanded ? this.Collapse.ShowAsync() : this.Collapse.HideAsync()); + this.Reduce(state => state with + { + IsExpanded = isExpanded + }); + } + } + + /// + /// Toggles the panel + /// + /// An awaitable task + public async Task HideAsync() + { + if (this.Collapse != null) + { + await this.Collapse.HideAsync(); + this.Reduce(state => state with + { + IsExpanded = false + }); + } + } + + /// + /// Reads and watches the logs + /// + /// An awaitable task + public async Task LoadLogsAsync() + { + this.Reduce(state => state with + { + IsLoading = true + }); + await this.ReadLogsAsync(); + await this.WatchLogsAsync(); //fire and forget, otherwise the subscription is blocked + } + + /// + /// Scrolls down the logs + /// + /// An awaitable task + public async Task ScrollDown() + { + if (this.LogsContainer.HasValue) await this.JSInterop.ScrollDownAsync(this.LogsContainer.Value); + } + + /// + /// Reads the logs form the API + /// + /// An awaitable task + protected async Task ReadLogsAsync() + { + var name = this.Get(state => state.Name); + var @namespace = this.Get(state => state.Namespace); + var logs = await this.ApiClient.WorkflowInstances.ReadLogsAsync(name, @namespace, this.CancellationTokenSource.Token).ConfigureAwait(false); + this.Reduce(state => state with + { + Logs = this.SplitLogs(logs), + IsLoading = false + }); + } + + /// + /// Watches the logs + /// + /// An awaitable task + protected async Task WatchLogsAsync() + { + this._watchCancellationTokenSource.Cancel(); + this._watchCancellationTokenSource.Dispose(); + this._watchCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(this.CancellationTokenSource.Token); + var name = this.Get(state => state.Name); + var @namespace = this.Get(state => state.Namespace); + await foreach (ITextDocumentWatchEvent evt in await this.ApiClient.WorkflowInstances.WatchLogsAsync(name, @namespace, this._watchCancellationTokenSource.Token).ConfigureAwait(false)) + { + if (evt != null && !string.IsNullOrEmpty(evt.Content)) + { + if (evt.Type == TextDocumentWatchEventType.Appended) + { + var logs = this.Get(state => state.Logs) ?? []; + this.Reduce(state => state with + { + Logs = [.. logs, .. this.SplitLogs(evt.Content)] + }); + } + else if (evt.Type == TextDocumentWatchEventType.Replaced) + { + this.Reduce(state => state with + { + Logs = this.SplitLogs(evt.Content) + }); + } + } + } + } + #endregion + + /// + /// Splits the provided logs + /// + /// A blob of text containing the logs + /// A of log lines + protected IEnumerable SplitLogs(string logs) + { + return logs.Replace("\r\n", "\n") + .Split('\n'); + } + + private bool disposed; + /// + /// Disposes of the store + /// + /// A boolean indicating whether or not the dispose of the store + protected override void Dispose(bool disposing) + { + if (!this.disposed) + { + if (disposing) + { + if (this._watchCancellationTokenSource != null) + { + this._watchCancellationTokenSource.Cancel(); + this._watchCancellationTokenSource.Dispose(); + } + } + this.disposed = true; + } + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/WorkflowInstanceLogs.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/WorkflowInstanceLogs.razor new file mode 100644 index 000000000..bed6a85ce --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/WorkflowInstanceLogs.razor @@ -0,0 +1,103 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@using Synapse.Dashboard.Components.WorkflowInstanceLogsStateManagement +@using System.Text.RegularExpressions +@namespace Synapse.Dashboard.Components +@inherits StatefulComponent + +
+ Logs + +
+ + @if (isLoading) + { + + } + else { +
+            @foreach(string line in logs)
+            {
+                var match = Regex.Match(line, timestampLevelPattern);
+                if (string.IsNullOrEmpty(match.Value))
+                {
+

@line

+ } + else + { + string timestamp = match.Groups[1].Value; + string level = match.Groups[2].Value; + string color = level.ToLower() switch + { + "fail" => "text-danger", + "critical" => "text-danger", + "error" => "text-danger", + "warning" => "text-warning", + "info" => "text-primary", + "debug" => "text-cinereous", + "trace" => "text-mute", + _ => "" + }; +

[@timestamp] @level: @line.Replace(match.Value, "")

+ } + } +
+ } +
+ +@code { + private string name = string.Empty; + [Parameter] public string Name { get; set; } = string.Empty; + private string @namespace = string.Empty; + [Parameter] public string Namespace { get; set; } = string.Empty; + bool isLoading = false; + bool isExpanded = false; + IEnumerable logs = []; + string timestampLevelPattern = @"\[(\d{2}:\d{2}:\d{2})\] (\w+):"; + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync().ConfigureAwait(false); + Store.IsLoading.Subscribe(value => OnStateChanged(_ => isLoading = value), token: CancellationTokenSource.Token); + Store.IsExpanded.Subscribe(value => OnStateChanged(_ => isExpanded = value), token: CancellationTokenSource.Token); + Store.Name.Subscribe(value => OnStateChanged(_ => name = value), token: CancellationTokenSource.Token); + Store.Namespace.Subscribe(value => OnStateChanged(_ => @namespace = value), token: CancellationTokenSource.Token); + Store.Logs.Subscribe(value => OnStateChanged(_ => logs = value), token: CancellationTokenSource.Token); + } + + /// + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + await this.Store.HideAsync(); + if (Name != name) + { + Store.SetName(Name); + } + if (Namespace != @namespace) + { + Store.SetNamespace(Namespace); + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + await Store.ScrollDown(); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowInstancesList/WorkflowInstancesList.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstancesList/WorkflowInstancesList.razor new file mode 100644 index 000000000..d79ab101c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowInstancesList/WorkflowInstancesList.razor @@ -0,0 +1,405 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ +@namespace Synapse.Dashboard.Components +@inject JSInterop jsInterop + +
+ @if (Loading) + { + + } +
+ @Title + @(WorkflowInstances?.Count() ?? 0) items +
+ @if (Workflows != null && Workflows.Count() > 0) { + + } + @if (Namespaces != null && Namespaces.Count() > 0) { + + } + @if (Operators != null && Operators.Count() > 0) + { + + } + + +
+
+ + + + @foreach (var column in knownColumns) + { + if ((Columns.Count() == 0 && column != "Delete") || Columns.Contains(column)) + { + + } + } + + + + + @if (WorkflowInstances != null && WorkflowInstances.Any()) + { + + + @foreach (var column in knownColumns) + { + if ((Columns.Count() == 0 && column != "Delete") || Columns.Contains(column)) + { + + } + } + + + + } + +
@(column != "Action" && column != "Delete" ? column : "") + +
+ @switch(column) + { + case "Name": + @instance.Metadata.Name + break; + case "Namespace": + @instance.Metadata.Namespace + + break; + case "Definition": + { + var definitionSegments = instance.Spec.Definition.ToString().Split(':'); + var version = definitionSegments.Last(); + var nameNs = definitionSegments.First(); + var segments = nameNs.Split('.'); + var ns = segments.Last(); + var name = segments.Take(segments.Count() - 1).Join('.'); + @instance.Spec.Definition.ToString() + + break; + } + case "Status": + @(instance.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending) + break; + case "Creation Time": + @instance.Metadata.CreationTimestamp?.DateTime.RelativeFormat() + break; + case "Start Time": + @(instance.Status?.StartedAt?.DateTime.RelativeFormat() ?? "-") + break; + case "End Time": + @(instance.Status?.EndedAt?.DateTime.RelativeFormat() ?? "-") + break; + case "Duration": + @(instance.Status?.StartedAt.HasValue == true && instance.Status?.EndedAt.HasValue == true ? instance.Status.EndedAt.Value.Subtract(instance.Status.StartedAt.Value).ToString("hh\\:mm\\:ss\\.fff") : "-") + break; + case "Operator": + { + @if (instance.Metadata.Labels?.TryGetValue(SynapseDefaults.Resources.Labels.Operator, out var operatorName) == true && !string.IsNullOrWhiteSpace(operatorName)) + { + var operatorSegments = operatorName.Split("."); + var ns = operatorSegments.Last(); + var name = operatorSegments.Take(operatorSegments.Count() - 1).Join('.'); + @operatorName + + } + else + { + - + } + break; + } + case "Actions": + + break; + case "Delete": + + break; + default: + break; + } + + +
+
+ +@code { + + protected string? ClassNames => cssClass; + + string? cssClass; + [Parameter] public string? Class { get; set; } + RenderFragment? title; + [Parameter] public RenderFragment? Title { get; set; } + bool loading = true; + [Parameter] public bool Loading { get; set; } = true; + IEnumerable columns = []; + [Parameter] public IEnumerable Columns { get; set; } = []; + IEnumerable? workflowInstances; + [Parameter] public IEnumerable? WorkflowInstances { get; set; } + IEnumerable selectedInstanceNames = []; + [Parameter] public IEnumerable SelectedInstanceNames { get; set; } = []; + IEnumerable? workflows { get; set; } + [Parameter] public IEnumerable? Workflows { get; set; } + IEnumerable? namespaces { get; set; } + [Parameter] public IEnumerable? Namespaces { get; set; } + string? @namespace { get; set; } + [Parameter] public string? Namespace { get; set; } + IEnumerable? operators { get; set; } + [Parameter] public IEnumerable? Operators { get; set; } + string? operatorName { get; set; } + [Parameter] public string? OperatorName { get; set; } + string? activeRow { get; set; } + [Parameter] public string? ActiveRow { get; set; } + + [Parameter] public EventCallback OnWorkflowChanged { get; set; } + [Parameter] public EventCallback OnNamespaceChanged { get; set; } + [Parameter] public EventCallback OnOperatorChanged { get; set; } + [Parameter] public EventCallback OnSearchInput { get; set; } + [Parameter] public EventCallback OnShowDetails { get; set; } + [Parameter] public EventCallback OnToggleSelected { get; set; } + [Parameter] public EventCallback OnDelete { get; set; } + [Parameter] public EventCallback OnDeleteSelected { get; set; } + + IEnumerable knownColumns = [ + "Name", + "Namespace", + "Definition", + "Status", + "Creation Time", + "Start Time", + "End Time", + "Duration", + "Operator", + "Actions", + "Delete" + ]; + + ElementReference checkboxAll = default!; + + bool shouldRender = true; + + /// + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + if (cssClass != Class) + { + cssClass = Class; + shouldRender = true; + } + if (title != Title) + { + title = Title; + shouldRender = true; + } + if (loading != Loading) + { + loading = Loading; + shouldRender = true; + } + if (columns != Columns) + { + columns = Columns; + shouldRender = true; + } + if (workflowInstances != WorkflowInstances) + { + workflowInstances = WorkflowInstances; + shouldRender = true; + } + if (workflows != Workflows) + { + workflows = Workflows; + shouldRender = true; + } + if (namespaces != Namespaces) + { + namespaces = Namespaces; + shouldRender = true; + } + if (@namespace != Namespace) + { + this.@namespace = Namespace; + shouldRender = true; + } + if (operators != Operators) + { + operators = Operators; + shouldRender = true; + } + if (operatorName != OperatorName) + { + operatorName = OperatorName; + shouldRender = true; + } + if (selectedInstanceNames != SelectedInstanceNames) + { + selectedInstanceNames = SelectedInstanceNames; + + if (selectedInstanceNames.Count() == 0) + { + await this.jsInterop.SetCheckboxStateAsync(checkboxAll, Dashboard.CheckboxState.Unchecked); + } + else if (selectedInstanceNames.Count() == (workflowInstances?.Count() ?? 0)) + { + await this.jsInterop.SetCheckboxStateAsync(checkboxAll, Dashboard.CheckboxState.Checked); + } + else + { + await this.jsInterop.SetCheckboxStateAsync(checkboxAll, Dashboard.CheckboxState.Indeterminate); + } + shouldRender = true; + } + if (activeRow != ActiveRow) + { + activeRow = ActiveRow; + await Task.Delay(1); + shouldRender = true; + } + } + + /// + protected override bool ShouldRender() + { + if (!shouldRender) return false; + shouldRender = false; + return true; + } + + /// + /// Gets the text alignment of the provided column + /// + /// The column to get the alignment for + /// + string GetColumnAlignment(string column) + { + return column == "Name" || column == "Namespace" + ? "start" + : column == "Action" + ? "end" + : "center"; + } + + /// + /// Handles workflow selection changes + /// + /// the to handle + /// + private async Task OnSelectWorkflowChangedAsync(ChangeEventArgs e) + { + if (this.OnWorkflowChanged.HasDelegate) + { + await this.OnWorkflowChanged.InvokeAsync(e.Value?.ToString()); + } + } + + /// + /// Handles namespace selection changes + /// + /// the to handle + /// + private async Task OnSelectNamespaceChangedAsync(ChangeEventArgs e) + { + if (this.OnNamespaceChanged.HasDelegate) + { + await this.OnNamespaceChanged.InvokeAsync(e.Value?.ToString()); + } + } + + /// + /// Handles operator selection changes + /// + /// the to handle + /// + private async Task OnSelectOperatorChangedAsync(ChangeEventArgs e) + { + if (this.OnOperatorChanged.HasDelegate) + { + await this.OnOperatorChanged.InvokeAsync(e.Value?.ToString()); + } + } + + /// + /// Handles search input value changes + /// + /// the to handle + protected async Task OnSearchInputAsync(ChangeEventArgs e) + { + if (this.OnSearchInput.HasDelegate) + { + await this.OnSearchInput.InvokeAsync(e.Value?.ToString()); + } + } + + /// + /// Handles the click on the show button + /// + /// + /// + protected async Task OnShowClickedAsync(WorkflowInstance instance) + { + if (this.OnShowDetails.HasDelegate) + { + await this.OnShowDetails.InvokeAsync(instance); + } + } + + /// + /// Handles the clikc on the delete button + /// + /// + /// + protected async Task OnDeleteClickedAsync(WorkflowInstance instance) + { + if (this.OnDelete.HasDelegate) + { + await this.OnDelete.InvokeAsync(instance); + } + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Components/WorkflowVersionSelector/WorkflowVersionSelector.razor b/src/dashboard/Synapse.Dashboard/Components/WorkflowVersionSelector/WorkflowVersionSelector.razor new file mode 100644 index 000000000..354da5cc9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Components/WorkflowVersionSelector/WorkflowVersionSelector.razor @@ -0,0 +1,46 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components + +@if (Workflow != null) +{ + +} +@code { + + protected string? ClassNames => Class; + [Parameter] public string? Class { get; set; } + [Parameter] public Workflow? Workflow { get; set; } + [Parameter] public string? Version { get; set; } + [Parameter] public EventCallback OnChange { get; set; } + + async Task OnVersionChangedAsync(ChangeEventArgs e) + { + if (OnChange.HasDelegate) + { + await this.OnChange.InvokeAsync(e); + } + } +} diff --git a/src/dashboard/Synapse.Dashboard/Configuration/ApplicationOptions.cs b/src/dashboard/Synapse.Dashboard/Configuration/ApplicationOptions.cs new file mode 100644 index 000000000..52ddd40be --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Configuration/ApplicationOptions.cs @@ -0,0 +1,27 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Configuration; + +/// +/// Represents the object used to configure the application +/// +public class ApplicationOptions +{ + + /// + /// Gets/sets the object used to configure the application's authentication + /// + public virtual AuthenticationOptions Authentication { get; set; } = new(); + +} diff --git a/src/dashboard/Synapse.Dashboard/Configuration/AuthenticationOptions.cs b/src/dashboard/Synapse.Dashboard/Configuration/AuthenticationOptions.cs new file mode 100644 index 000000000..98e7a02bd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Configuration/AuthenticationOptions.cs @@ -0,0 +1,29 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.AspNetCore.Components.WebAssembly.Authentication; + +namespace Synapse.Dashboard.Configuration; + +/// +/// Represents the object used to configure the application's authentication +/// +public class AuthenticationOptions +{ + + /// + /// Gets/sets the object used to configure the application's OIDC authentication, if any + /// + public virtual OidcProviderOptions? Oidc { get; set; } + +} diff --git a/src/dashboard/Synapse.Dashboard/Constants.cs b/src/dashboard/Synapse.Dashboard/Constants.cs deleted file mode 100644 index 2e55c4f2e..000000000 --- a/src/dashboard/Synapse.Dashboard/Constants.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - public static class Constants - { - public const double GraphBagdesRadius = 15; - public const double GraphStartEndNodeRadius = 30; - public const string SpecVersion = "0.8"; - public const string DefinitionVersion = "1.0"; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/ControlFlow.cs b/src/dashboard/Synapse.Dashboard/ControlFlow.cs deleted file mode 100644 index 87e4fb420..000000000 --- a/src/dashboard/Synapse.Dashboard/ControlFlow.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - public enum ControlFlow - { - Default, - Compensation - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/DateTimeExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/DateTimeExtensions.cs new file mode 100644 index 000000000..3ad0de5b1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Extensions/DateTimeExtensions.cs @@ -0,0 +1,57 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using moment.net; +using moment.net.Models; +using System.Globalization; + +namespace Synapse.Dashboard.Extensions; + +/// +/// Provides extension methods for +/// +public static class DateTimeExtensions +{ + /// + /// Formats the provided in a relative fashion (e.g.: 3 minutes ago, Yesterday at 1:00pm...) + /// + /// + /// + public static string RelativeFormat(this DateTime dateTime) + { + var now = DateTime.Now; + var delta = now.Subtract(dateTime); + if (Math.Abs(delta.Days) >= 1) + { + var cultureFormats = CultureInfo.GetCultureInfo("en-US").DateTimeFormat; + var defaults = new CalendarTimeFormats(CultureInfo.CurrentUICulture); + var formats = new CalendarTimeFormats( + defaults.SameDay, + defaults.NextDay, + defaults.NextWeek, + defaults.LastDay, + defaults.LastWeek, + $"{cultureFormats.ShortDatePattern} {cultureFormats.ShortTimePattern}" + ); + return now.CalendarTime(dateTime, formats); + } + else if (delta < TimeSpan.Zero) + { + return dateTime.ToNow(); + } + else + { + return dateTime.FromNow(); + } + } +} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/ExpressionExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/ExpressionExtensions.cs deleted file mode 100644 index bf9d80101..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/ExpressionExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using System.Linq.Expressions; - -namespace Synapse.Dashboard -{ - - /// - /// Defines extensions for s - /// - public static class ExpressionExtensions - { - - /// - /// Combines the with the specified one - /// - /// The - /// The to combine - /// A new - public static Expression CombineWith(this Expression left, Expression right) - { - return Expression.And(left, right); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/GraphExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/GraphExtensions.cs deleted file mode 100644 index 80c9045d4..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/GraphExtensions.cs +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Blazor.Dagre; -using Neuroglia.Blazor.Dagre.Models; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard -{ - public static class GraphExtensions - { - - public static async Task DisplayActivityStatusFor(this IGraphViewModel graph, Dictionary> statesMap, IEnumerable instances, IEnumerable activities, bool highlightPath = false) - { - graph.ClearActivityStatus(); - foreach (var instance in instances) - { - if (instance.Status == V1WorkflowInstanceStatus.Pending - || instance.Status == V1WorkflowInstanceStatus.Starting) - { - var node = graph.Nodes.Values.OfType().FirstOrDefault(); - if (node != null) - node.ActiveInstancesCount += 1; - continue; - } - if (instance.Status == V1WorkflowInstanceStatus.Completed) - { - var node = graph.Nodes.Values.OfType().FirstOrDefault(); - if (node != null) - node.ActiveInstancesCount += 1; - } - await Task.Yield(); // useless? - } - foreach (var activity in activities) - { - var nodes = graph.GetNodesFor(statesMap, activity); - if (nodes != null && nodes.Any()) - { - if (activity.Status == V1WorkflowActivityStatus.Running) - { - nodes.ToList().ForEach(node => { node.ActiveInstancesCount += 1; }); - } - else if (activity.Status == V1WorkflowActivityStatus.Compensating) - { - nodes.ToList().ForEach(node => { node.CompensatedInstancesCount += 1; }); - } - else if (activity.Status == V1WorkflowActivityStatus.Faulted) - { - nodes.ToList().ForEach(node => { node.FaultedInstancesCount += 1; }); - } - if (highlightPath) - { - nodes.ToList().ForEach(node => - { - ((INodeViewModel)node).CssClass = (((INodeViewModel)node).CssClass ?? "") + " active"; - }); - } - } - await Task.Yield(); // useless? - } - } - - public static void ClearActivityStatus(this IGraphViewModel graph) - { - var nodes = graph.AllNodes.Values.ToList(); - nodes.AddRange(graph.AllClusters.Values); - foreach (var node in nodes) - { - if (node is IWorkflowNodeViewModel wfNode) - { - wfNode.ResetInstancesCount(); - } - node.CssClass = node.CssClass?.Replace(" active", ""); - } - } - - public static IEnumerable? GetNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - if (activity == null) - return null; - switch (activity.Type) - { - case V1WorkflowActivityType.Action: - return graph.GetActionNodesFor(statesMap, activity); - case V1WorkflowActivityType.Function: - return graph.GetActionNodesFor(statesMap, activity); - case V1WorkflowActivityType.Transition: - return graph.GetTransitionNodesFor(statesMap, activity); - case V1WorkflowActivityType.Branch: - throw new NotImplementedException(); //todo - case V1WorkflowActivityType.ConsumeEvent: - return graph.GetConsumeEventNodesFor(statesMap, activity); - case V1WorkflowActivityType.End: - return graph.Nodes.Values.OfType(); - case V1WorkflowActivityType.Error: - throw new NotImplementedException(); //todo - case V1WorkflowActivityType.EventTrigger: - return graph.GetEventTriggerNodesFor(statesMap, activity); - case V1WorkflowActivityType.Iteration: - return graph.GetIterationNodesFor(statesMap, activity); - case V1WorkflowActivityType.ProduceEvent: - throw new NotImplementedException(); //todo - case V1WorkflowActivityType.Start: - return graph.Nodes.Values.OfType(); - case V1WorkflowActivityType.State: - return graph.GetInnerStateNodesFor(statesMap, activity); - case V1WorkflowActivityType.SubFlow: - throw new NotImplementedException(); //todo - default: - return null; - } - } - - private static IEnumerable GetStateNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - if (!activity.Metadata.TryGetValue("state", out var stateName)) - throw new InvalidDataException($"The specified activity's metadata does not define a 'state' value"); - if (!statesMap.ContainsKey(stateName)) - return new List().AsEnumerable(); - return statesMap[stateName]; - } - - private static IEnumerable GetInnerStateNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - var stateNodes = graph.GetStateNodesFor(statesMap, activity); - return stateNodes - .Concat(stateNodes.SelectMany(node => node.Children.Values.OfType()) as IEnumerable) - .Concat(stateNodes.SelectMany(node => node.Children.Values.OfType()) as IEnumerable) - ; - } - - private static IEnumerable? GetActionNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - var stateNodes = graph.GetStateNodesFor(statesMap, activity); - if (!activity.Metadata.TryGetValue("action", out var actionName)) - throw new InvalidDataException($"The specified activity's metadata does not define a 'action' value"); - return stateNodes.SelectMany(node => node.Children.Values - .Where(node => typeof(IActionNodeViewModel).IsAssignableFrom(node.GetType())) - .Select(node => node as IActionNodeViewModel) - .Where(node => node != null && node.Action.Name == actionName) - ); - } - - private static IEnumerable? GetTransitionNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - var stateNodes = graph.GetStateNodesFor(statesMap, activity); - if (!activity.Metadata.TryGetValue("case", out var caseName)) - return null; - return stateNodes.SelectMany(node => - node.Children.Values.OfType().Where(node => node != null && node.DataCaseName == caseName) - ); - } - - private static IEnumerable? GetIterationNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - var stateNodes = graph.GetStateNodesFor(statesMap, activity); - return stateNodes.SelectMany(node => node.Children.Values.OfType()); - } - - private static IEnumerable? GetEventTriggerNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - var stateNodes = graph.GetStateNodesFor(statesMap, activity); - if (!activity.Metadata.TryGetValue("trigger", out var eventName)) - throw new InvalidDataException($"The specified activity's metadata does not define a 'trigger' value"); - return stateNodes.SelectMany(node => node.Children.Values.OfType() - .Where(eventNode => eventNode.RefName == eventName /* && eventNode.Kind == ServerlessWorkflow.Sdk.EventKind.Consumed */) - ); - } - - private static IEnumerable? GetConsumeEventNodesFor(this IGraphViewModel graph, Dictionary> statesMap, V1WorkflowActivity activity) - { - var stateNodes = graph.GetStateNodesFor(statesMap, activity); - if (!activity.Metadata.TryGetValue("event", out var eventName)) - throw new InvalidDataException($"The specified activity's metadata does not define a 'event' value"); - return stateNodes.SelectMany(node => node.Children.Values.OfType() - .Where(eventNode => eventNode.RefName == eventName /* && eventNode.Kind == ServerlessWorkflow.Sdk.EventKind.Consumed */) - ); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/IQueryableExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/IQueryableExtensions.cs deleted file mode 100644 index 97d67e229..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/IQueryableExtensions.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia; -using System.Collections; -using System.Linq.Expressions; -using System.Reflection; - -namespace Synapse.Dashboard -{ - - /// - /// Defines extensions for s - /// - public static class IQueryableExtensions - { - - private static readonly MethodInfo WhereMethod = typeof(Queryable).GetMethods().First(m => m.Name == nameof(Queryable.Where)); - private static readonly MethodInfo SkipMethod = typeof(Queryable).GetMethod(nameof(Queryable.Skip))!; - private static readonly MethodInfo TakeMethod = typeof(Queryable).GetMethods().First(m => m.Name == nameof(Queryable.Take)); - private static readonly MethodInfo ToListMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList))!; - private static readonly MethodInfo ToListAsyncMethod = typeof(AsyncEnumerable).GetMethod(nameof(AsyncEnumerable.ToListAsync))!; - - public static IQueryable Where(this IQueryable queryable, LambdaExpression lambda) - { - if (queryable == null) - throw new ArgumentNullException(nameof(queryable)); - MethodInfo method = WhereMethod.MakeGenericMethod(queryable.ElementType); - return (IQueryable)method.Invoke(null, new object[] { queryable, lambda }); - } - - public static IQueryable Skip(this IQueryable queryable, int count) - { - if (queryable == null) - throw new ArgumentNullException(nameof(queryable)); - MethodInfo method = SkipMethod.MakeGenericMethod(queryable.ElementType); - return (IQueryable)method.Invoke(null, new object[] { queryable, count }); - } - - public static IQueryable Take(this IQueryable queryable, int count) - { - if (queryable == null) - throw new ArgumentNullException(nameof(queryable)); - MethodInfo method = TakeMethod.MakeGenericMethod(queryable.ElementType); - return (IQueryable)method.Invoke(null, new object[] { queryable, count }); - } - - /// - /// Converts the into a new - /// - /// The to convert - /// A new - public static IList ToList(this IQueryable queryable) - { - if (queryable == null) - throw new ArgumentNullException(nameof(queryable)); - MethodInfo method = ToListMethod.MakeGenericMethod(queryable.ElementType); - return (IList)method.Invoke(null, new object[] { queryable }); - } - - /// - /// Converts the into a new asynchronously - /// - /// The to convert - /// A - /// A new - public static async Task ToListAsync(this IQueryable queryable, CancellationToken cancellationToken = default) - { - if (queryable == null) - throw new ArgumentNullException(nameof(queryable)); - MethodInfo method = ToListAsyncMethod.MakeGenericMethod(queryable.ElementType); - return (IList)await method.InvokeAsync(null, new object[] { queryable, cancellationToken }); - } - - /// - /// Converts the into a new asynchronously - /// - /// The to convert - /// A - /// A new - public static async Task> ToListAsync(this IQueryable queryable, CancellationToken cancellationToken = default) - { - if (queryable == null) - throw new ArgumentNullException(nameof(queryable)); - MethodInfo method = ToListAsyncMethod.MakeGenericMethod(queryable.ElementType); - return (IList)await method.InvokeAsync(null, new object[] { queryable, cancellationToken }); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/JSchemaExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/JSchemaExtensions.cs deleted file mode 100644 index 141cfb17f..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/JSchemaExtensions.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Schema; -using System.Dynamic; - -namespace Synapse.Dashboard -{ - - /// - /// Defines extensions for s - /// - public static class JSchemaExtensions - { - - /// - /// Generates an example value for the - /// - /// The to generate a new example value for - /// A new example value for the - public static object? GenerateExample(this JSchema schema) - { - if (schema.Default != null) - return schema.Default.ToObject(); - if (schema.ExtensionData.TryGetValue("examples", out var examples) - && examples is JArray examplesArray) - return examplesArray.First?.ToObject(); - switch (schema.Type) - { - case JSchemaType.Array: - var array = new List(); - var itemSchema = schema.Items.First(); - if (itemSchema != null) - array.Add(itemSchema.GenerateExample()!); - return array; - case JSchemaType.Boolean: - return true; - case JSchemaType.Integer: - case JSchemaType.Number: - var min = schema.Minimum; - if (!min.HasValue) - min = 0; - var max = schema.Maximum; - if (!max.HasValue) - max = 0; - return Random.Shared.Next((int)min.Value, (int)max.Value); - case JSchemaType.Object: - IDictionary dyn = new ExpandoObject()!; - foreach(var property in schema.Properties) - { - dyn.Add(property.Key, property.Value.GenerateExample()!); - } - return dyn; - case JSchemaType.String: - return schema.Title; - default: - return null; - } - } - - /// - /// Generates the default value for the - /// - /// The to generate the default value for - /// A new default value for the - public static object? GenerateDefault(this JSchema schema) - { - if (schema.Default != null) - return schema.Default.ToObject(); - if(schema.Type == JSchemaType.Object) - { - IDictionary dyn = new ExpandoObject()!; - foreach (var property in schema.Properties) - { - var value = property.Value.GenerateDefault(); - if(value != null) - dyn.Add(property.Key, value); - } - return dyn; - } - return default; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/ObjectExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/ObjectExtensions.cs deleted file mode 100644 index ba0870a63..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/ObjectExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Newtonsoft.Json; - -namespace Synapse.Dashboard -{ - /// - /// Defines extensions for objects - /// - public static class ObjectExtensions - { - - /// - /// Clones the object - /// - /// The type of object to clone - /// The object to clone - /// A new clone of the object - public static T? Clone(this T source) - { - return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(source)); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/PropertyInfoExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/PropertyInfoExtensions.cs deleted file mode 100644 index 581af29d3..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/PropertyInfoExtensions.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia; -using System.ComponentModel.DataAnnotations; -using System.Reflection; - -namespace Synapse.Dashboard -{ - /// - /// Defines extensions for s - /// - public static class PropertyInfoExtensions - { - - /// - /// Gets the property's display name - /// - /// The to get the display name for - /// The property's display name - public static string GetDisplayName(this PropertyInfo property) - { - var name = null as string; - if (property.TryGetCustomAttribute(out DisplayAttribute displayAttribute)) - name = displayAttribute.Name; - if (string.IsNullOrWhiteSpace(name)) - name = property.Name; - return name; - } - - /// - /// Gets the property's display order - /// - /// The to get the display order for - /// The property's display order - public static int GetDisplayOrder(this PropertyInfo property) - { - int? order = 0; - if (property.TryGetCustomAttribute(out DisplayAttribute displayAttribute)) - order = displayAttribute.GetOrder(); - if (!order.HasValue) - order = 1; - return order.Value; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/StateDefinitionExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/StateDefinitionExtensions.cs deleted file mode 100644 index f8fbc88cd..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/StateDefinitionExtensions.cs +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using ServerlessWorkflow.Sdk; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - - /// - /// Defines extensions for s - /// - public static class StateDefinitionExtensions - { - - /// - /// Gets the 's - /// - /// The to get the of - /// The 's - public static WorkflowOutcome GetOutcome(this StateDefinition state) - { - if (state.IsEnd || state.End != null) - return new(WorkflowOutcomeType.End, state.End); - else - return new(WorkflowOutcomeType.Transition, string.IsNullOrWhiteSpace(state.TransitionToStateName)? state.Transition : state.TransitionToStateName); - } - - /// - /// Sets the 's - /// - /// The to set the of - /// The to set - public static void SetOutcome(this StateDefinition state, WorkflowOutcome outcome) - { - if (outcome == null) throw new ArgumentNullException(nameof(outcome)); - switch (outcome.Type) - { - case WorkflowOutcomeType.Transition: - state.IsEnd = false; - state.End = null; - if(outcome.Definition != null) - { - switch (outcome.Definition) - { - case string transitionToStateName: - state.TransitionToStateName = transitionToStateName; - break; - case TransitionDefinition transition: - state.Transition = transition; - break; - } - } - break; - case WorkflowOutcomeType.End: - state.TransitionToStateName = null; - state.Transition = null; - if (outcome.Definition == null) - { - state.IsEnd = true; - } - else - { - switch (outcome.Definition) - { - case EndDefinition end: - state.End = end; - break; - default: - state.End = null; - state.IsEnd = true; - break; - } - } - break; - default: - throw new NotSupportedException($"The specified {nameof(WorkflowOutcomeType)} '{outcome.Type}' is not supported"); - } - } - - /// - /// Converts the to a new of the specified type - /// - /// The to convert - /// The type to convert the to - /// A new of the specified type - public static StateDefinition OfType(this StateDefinition state, StateType type) - { - var result = type switch - { - StateType.Callback => (StateDefinition)new CallbackStateDefinition(), - StateType.Event => new EventStateDefinition(), - StateType.ForEach => new ForEachStateDefinition(), - StateType.Inject => new InjectStateDefinition(), - StateType.Operation => new OperationStateDefinition(), - StateType.Parallel => new ParallelStateDefinition(), - StateType.Sleep => new SleepStateDefinition(), - StateType.Switch => new SwitchStateDefinition(), - _ => throw new NotSupportedException($"The specified {nameof(StateType)} '{type}' is not supported") - }; - result.CompensatedBy = state.CompensatedBy; - result.DataFilter = state.DataFilter; - result.DataInputSchema = state.DataInputSchema; - if (state.IsEnd) - result.IsEnd = true; - else if(state.End != null) - result.End = state.End; - result.Errors = state.Errors; - result.Id = state.Id; - result.Metadata = state.Metadata; - result.Name = state.Name; - if(string.IsNullOrEmpty(state.TransitionToStateName)) - result.Transition = state.Transition; - else - result.TransitionToStateName = state.TransitionToStateName; - result.UsedForCompensation = state.UsedForCompensation; - if(result is SwitchStateDefinition) - { - result.Transition = null; - result.TransitionToStateName = null; - result.IsEnd = false; - result.End = null; - } - return result; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/StatusExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/StatusExtensions.cs new file mode 100644 index 000000000..5ac8ccb70 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Extensions/StatusExtensions.cs @@ -0,0 +1,54 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse.Dashboard.Extensions; + +/// +/// Provides extension methods for statuses/phases +/// +public static class StatusExtensions +{ + /// + /// Returns the color css class for the provided status or phase + /// + /// The status to return the color class for + /// + public static string GetColorClass(this string? status) => status switch + { + // commented = same as above, which lead to the error "The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match." + WorkflowInstanceStatusPhase.Running => "accent", + //CorrelatorStatusPhase.Running => "accent", + //OperatorStatusPhase.Running => "accent", + CorrelationContextStatus.Active => "accent", + //TaskInstanceStatus.Running => "accent", + WorkflowInstanceStatusPhase.Faulted => "danger", + //TaskInstanceStatus.Faulted => "danger", + WorkflowInstanceStatusPhase.Cancelled => "warning", + //TaskInstanceStatus.Cancelled => "warning", + //CorrelationContextStatus.Cancelled => "warning", + WorkflowInstanceStatusPhase.Completed => "success", + //TaskInstanceStatus.Completed => "success", + //CorrelationContextStatus.Completed => "success", + WorkflowInstanceStatusPhase.Waiting => "cinereous", + TaskInstanceStatus.Suspended => "icterine", + TaskInstanceStatus.Skipped => "cinereous", + WorkflowInstanceStatusPhase.Pending => "mute", + //TaskInstanceStatus.Pending => "mute", + CorrelationContextStatus.Inactive => "mute", + CorrelatorStatusPhase.Stopped => "secondary", + //OperatorStatusPhase.Stopped => "secondary", + _ => "" + }; +} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/SwitchCaseDefinitionExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/SwitchCaseDefinitionExtensions.cs deleted file mode 100644 index 8781e8bc8..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/SwitchCaseDefinitionExtensions.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - /// - /// Defines extensions for s - /// - public static class SwitchCaseDefinitionExtensions - { - - /// - /// Gets the 's - /// - /// The to get the of - /// The 's - public static WorkflowOutcome GetOutcome(this SwitchCaseDefinition switchCase) - { - if (switchCase.IsEnd || switchCase.End != null) - return new(WorkflowOutcomeType.End, switchCase.End); - else - return new(WorkflowOutcomeType.Transition, string.IsNullOrWhiteSpace(switchCase.TransitionToStateName) ? switchCase.Transition : switchCase.TransitionToStateName); - } - - /// - /// Sets the 's - /// - /// The to set the of - /// The to set - public static void SetOutcome(this SwitchCaseDefinition switchCase, WorkflowOutcome outcome) - { - if (outcome == null) throw new ArgumentNullException(nameof(outcome)); - switch (outcome.Type) - { - case WorkflowOutcomeType.Transition: - switchCase.IsEnd = false; - switchCase.End = null; - if (outcome.Definition != null) - { - switch (outcome.Definition) - { - case string transitionToStateName: - switchCase.TransitionToStateName = transitionToStateName; - break; - case TransitionDefinition transition: - switchCase.Transition = transition; - break; - } - } - break; - case WorkflowOutcomeType.End: - switchCase.TransitionToStateName = null; - switchCase.Transition = null; - if (outcome.Definition == null) - { - switchCase.IsEnd = true; - } - else - { - switch (outcome.Definition) - { - case EndDefinition end: - switchCase.End = end; - break; - default: - switchCase.End = null; - switchCase.IsEnd = true; - break; - } - } - break; - default: - throw new NotSupportedException($"The specified {nameof(WorkflowOutcomeType)} '{outcome.Type}' is not supported"); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Extensions/TypeExtensions.cs b/src/dashboard/Synapse.Dashboard/Extensions/TypeExtensions.cs deleted file mode 100644 index 34a2f5b3e..000000000 --- a/src/dashboard/Synapse.Dashboard/Extensions/TypeExtensions.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia; - -namespace Synapse.Dashboard -{ - /// - /// Defines extensions for s - /// - public static class TypeExtensions - { - - /// - /// Determines whether the type implements the interface - /// - /// The type to check - /// A boolean indicating whether the type implements the interface - public static bool IsAsyncEnumerable(this Type type) - { - if (type == null) - throw new ArgumentNullException(nameof(type)); - return type.GetGenericType(typeof(IAsyncEnumerable<>)) != null; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Accordion.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Accordion.razor deleted file mode 100644 index 6ffd4abee..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Accordion.razor +++ /dev/null @@ -1,87 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ -@namespace Synapse.Dashboard -@inject IAccordionManager AccordionManager -@implements IDisposable -@implements IAccordionModel - -

-
-
- -
- @if (IsExpanded) { -
-
- @Body -
-
- } -
-
- -@code { - /// - /// Gets/sets the title of the accordion - /// - [Parameter] public RenderFragment? Header { get; set; } - - /// - /// Gets/sets the content of the accordion - /// - [Parameter] public RenderFragment? Body { get; set; } - - /// - /// Gets/sets if the accordion is opened - /// - [Parameter] public bool IsExpanded { get; set; } = false; - - /// - /// Gets/sets if the accordion can be opened at the same time than others - /// - [Parameter] public bool AllowsMultiple { get; set; } = true; - - /// - /// Register the accordion - /// - /// - protected override async Task OnInitializedAsync() - { - await this.AccordionManager.Register(this); - await base.OnInitializedAsync(); - } - - /// - /// Deregister the accordion when the component is disposed - /// - public void Dispose() - { - this.AccordionManager.Deregister(this); - GC.SuppressFinalize(this); - } - - /// - /// Toggles the accordion - /// - /// - public async Task Toggle() - { - await this.AccordionManager.Toggle(this); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/AccordionManager.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/AccordionManager.cs deleted file mode 100644 index ca06c40bd..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/AccordionManager.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - /// - /// Manage the state of accordions components - /// - public class AccordionManager - : IAccordionManager - { - protected virtual List Items { get; set; } = new List(); - - /// - /// Register an accordion to be interacted with - /// - /// - /// - public virtual async Task Register(IAccordionModel model) - { - this.Items.Add(model); - await Task.CompletedTask; - } - - /// - /// Deregister an accordion for observed accordions - /// - /// - /// - public virtual async Task Deregister(IAccordionModel model) - { - if (this.Items.Contains(model)) - { - this.Items.Remove(model); - } - await Task.CompletedTask; - } - - /// - /// Opens the targeted accordion - /// - /// - /// - public virtual async Task Open(IAccordionModel model) - { - foreach(var accordion in this.Items.Where(accordion => !accordion.AllowsMultiple)) - { - await this.Close(accordion); - } - model.IsExpanded = true; - await Task.CompletedTask; - } - - /// - /// Closes the targeted accordion - /// - /// - /// - public virtual async Task Close(IAccordionModel model) - { - model.IsExpanded = false; - await Task.CompletedTask; - } - - /// - /// Toggles the targeted accordion - /// - /// - /// - public virtual async Task Toggle(IAccordionModel model) - { - if (model.IsExpanded) { - await this.Close(model); - } - else - { - await this.Open(model); - } - await Task.CompletedTask; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Collapse.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Collapse.razor deleted file mode 100644 index 9690cb058..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Collapse.razor +++ /dev/null @@ -1,60 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@implements IAccordionModel - -

-
- @Header -
- @if (IsExpanded) - { -
- @Body -
- } -
- -@code -{ - - [Parameter] public RenderFragment Header { get; set; } - - [Parameter] public RenderFragment Body { get; set; } - - /// - /// Gets a boolean indicating whether or not the is expanded - /// - [Parameter] public bool IsExpanded { get; set; } - - /// - /// Gets the to invoke whenever the is being expanded or collapsed - /// - [Parameter] public EventCallback OnToggleExpand { get; set; } - - public bool AllowsMultiple { get; set; } = true; - - public async Task ToggleExpandAsync() - { - this.IsExpanded = !this.IsExpanded; - //this.StateHasChanged(); - if (this.OnToggleExpand.HasDelegate) { - await this.OnToggleExpand.InvokeAsync(new() { Value = this.IsExpanded }); - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/ExpandableDataRow.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/ExpandableDataRow.razor deleted file mode 100644 index 6c5a7ed24..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/ExpandableDataRow.razor +++ /dev/null @@ -1,30 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inject IAccordionManager AccordionManager -@implements IDisposable -@inherits Accordion - - - @Header - - -@if (IsExpanded) { - - @Body - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Expander.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Expander.razor deleted file mode 100644 index b7929821d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/Expander.razor +++ /dev/null @@ -1,96 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@implements IAccordionModel - -

- - @HeaderTemplate(this) - - - @Body - -
- -@code -{ - - [Parameter] public string HeaderContainerTagName { get; set; } = "div"; - - [Parameter] public string BodyContainerTagName { get; set; } = "div"; - - /// - /// Gets the 's header - /// - [Parameter] public RenderFragment Header { get; set; } - - /// - /// Gets the 's header template - /// - [Parameter] public RenderFragment HeaderTemplate { get; set; } = context => - { - return - @
-
-
- @context.Header -
-
-
- -
-
- ; - }; - - /// - /// Gets the 's body - /// - [Parameter] public RenderFragment Body { get; set; } - - /// - /// Gets a boolean indicating whether or not the is expanded - /// - [Parameter] public bool IsExpanded { get; set; } - - /// - /// Gets the Expander's css classes - /// - [Parameter] public string? Class { get; set; } - - public bool AllowsMultiple { get; set; } = true; - - private string GetBodyCssClass() - { - return $"collapse expander-body {(IsExpanded ? "show" : "")}"; - } - - /// - /// Gets the to invoke whenever the is being expanded or collapsed - /// - [Parameter] public EventCallback OnToggleExpand { get; set; } - - public async Task ToggleExpandAsync() - { - this.IsExpanded = !this.IsExpanded; - //this.StateHasChanged(); - if (this.OnToggleExpand.HasDelegate) { - await this.OnToggleExpand.InvokeAsync(this.IsExpanded); - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/IAccordionManager.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/IAccordionManager.cs deleted file mode 100644 index f4edafbd7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/IAccordionManager.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - /// - /// Manage the state of accordions components - /// - public interface IAccordionManager - { - /// - /// Register an accordion to be interacted with - /// - /// - /// - public Task Register(IAccordionModel model); - /// - /// Deregister an accordion for observed accordions - /// - /// - /// - public Task Deregister(IAccordionModel model); - /// - /// Opens the targeted accordion - /// - /// - /// - public Task Open(IAccordionModel model); - /// - /// Closes the targeted accordion - /// - /// - /// - public Task Close(IAccordionModel model); - /// - /// Toggles the targeted accordion - /// - /// - /// - public Task Toggle(IAccordionModel model); - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/IAccordionModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/IAccordionModel.cs deleted file mode 100644 index 4068b3b8b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Accordion/IAccordionModel.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; - -namespace Synapse.Dashboard -{ - /// - /// Represents the state of an accordion component - /// - public interface IAccordionModel - { - /// - /// Gets/sets the header of the accordion - /// - public RenderFragment Header { get; set; } - /// - /// Gets/sets the content of the accordion - /// - public RenderFragment Body { get; set; } - /// - /// Gets/sets if the accordion is opened - /// - public bool IsExpanded { get; set; } - /// - /// Gets/sets if the accordion can be opened at the same time than others - /// - public bool AllowsMultiple { get; set; } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor deleted file mode 100644 index ae84449db..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor +++ /dev/null @@ -1,54 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inject IBreadcrumbManager BreadcrumbService -@implements IDisposable -

- -@code { - protected override void OnInitialized() - { - base.OnInitialized(); - this.BreadcrumbService.PropertyChanged += this.OnBreadcrumbChanged; - } - - protected void OnBreadcrumbChanged(object? sender, PropertyChangedEventArgs args) - { - this.StateHasChanged(); - } - - public void Dispose() - { - if (this.BreadcrumbService != null) - this.BreadcrumbService.PropertyChanged -= this.OnBreadcrumbChanged; - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.css deleted file mode 100644 index 4e1d54b66..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.css +++ /dev/null @@ -1,3 +0,0 @@ -ul li .icon { - line-height: initial; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.min.css deleted file mode 100644 index 095a77d1b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -ul li .icon{line-height:initial;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.scss deleted file mode 100644 index b9f4311e1..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumb.razor.scss +++ /dev/null @@ -1,7 +0,0 @@ -ul { - li { - .icon { - line-height: initial; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/BreadcrumbItem.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/BreadcrumbItem.cs deleted file mode 100644 index 4a321da21..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/BreadcrumbItem.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - /// - /// Represents a breadcrumb element - /// - public class BreadcrumbItem - : IBreadcrumbItem - { - /// - /// The displayed label - /// - public string Label { get; init; } - - /// - /// The displayed icon, if any - /// - public string? Icon { get; init; } - - /// - /// The navigation link - /// - public string Link { get; init; } - - /// - /// Initializes a new with the provided data - /// - /// - /// - /// - public BreadcrumbItem(string label, string link, string? icon = null) - { - this.Label = label; - this.Link = link; - this.Icon = icon; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/BreadcrumbManager.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/BreadcrumbManager.cs deleted file mode 100644 index 238f578d1..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/BreadcrumbManager.cs +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using System.ComponentModel; -using System.Runtime.CompilerServices; - -namespace Synapse.Dashboard -{ - /// - /// The service used to manage the breadcrumb - /// - public class BreadcrumbManager - : IBreadcrumbManager, INotifyPropertyChanged - { - /// - /// Notifies when the list has changed - /// - public event PropertyChangedEventHandler? PropertyChanged; - - /// - /// The list of displayed - /// - public List Items { get; init; } - - /// - /// The service - /// - protected NavigationManager NavigationManager { get; init; } - - /// - /// Initializes a new - /// - /// - public BreadcrumbManager(NavigationManager navigationManager) - { - this.NavigationManager = navigationManager; - this.Items = new List(Breadcrumbs.Home); - } - - /// - /// Adds the specified to the list - /// - /// - /// - public async Task AddItem(IBreadcrumbItem breadcrumbItem) - { - if (!this.Items.Contains(breadcrumbItem)) - { - this.Items.Add(breadcrumbItem); - } - await this.NotifyChange(); - } - - /// - /// Creates a new with the specified label and icon for the active route and adds it to the list - /// - /// - /// - /// The created - public async Task AddCurrentUri(string label, string? icon = null) - { - IBreadcrumbItem item = new BreadcrumbItem(label, this.NavigationManager.Uri, icon); - this.Items.Add(item); - await this.NotifyChange(); - return await Task.FromResult(item); - } - - /// - /// Adds the specified to the list - /// - /// - /// - public async Task RemoveItem(IBreadcrumbItem breadcrumbItem) - { - if (this.Items.Contains(breadcrumbItem)) - { - this.Items.Remove(breadcrumbItem); - } - await this.NotifyChange(); - } - - /// - /// Clears the 's list - /// - /// - public async Task Clear() - { - this.Items?.Clear(); - await this.NotifyChange(); - } - - /// - /// Replaces the current 's list with the provided one - /// - /// - /// - public async Task Use(IEnumerable list) - { - await this.Clear(); - foreach (var item in list) - { - await this.AddItem(item); - } - } - - /// - public async Task NavigateTo(IBreadcrumbItem breadcrumbItem) - { - var itemIndex = this.Items.IndexOf(breadcrumbItem); - var newState = new List(this.Items.Take(itemIndex+1)); - await this.Use(newState); - this.NavigationManager.NavigateTo(breadcrumbItem.Link); - } - - /// - /// Notifies a change - /// - /// - protected async Task NotifyChange([CallerMemberName] String propertyName = "Items") - { - if (this.PropertyChanged != null) - { - this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - await Task.CompletedTask; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumbs.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumbs.cs deleted file mode 100644 index eec980c5a..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/Breadcrumbs.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - public static class Breadcrumbs - { - public static IEnumerable Home = new List() { new BreadcrumbItem("Home", "/", "bi-house") }; - public static IEnumerable Workflows = new List(Home) { new BreadcrumbItem("Workflows", "/workflows", "bi-gear") }; - public static IEnumerable CreateWorkflow = new List(Workflows) { new BreadcrumbItem("New Workflow", "/workflows/new") }; - public static IEnumerable UploadWorkflow = new List(Breadcrumbs.Workflows) { new BreadcrumbItem("Upload Workflow", "/workflows/upload") }; - public static IEnumerable WorkflowEditor = new List(Workflows) { new BreadcrumbItem("Workflow Editor", "/workflows/editor") }; - public static IEnumerable Schedules = new List(Home) { new BreadcrumbItem("Schedules", "/schedules", "bi-stopwatch") }; - public static IEnumerable CreateSchedule = new List(Schedules) { new BreadcrumbItem("New Schedule", "/schedules/new") }; - public static IEnumerable Correlations = new List(Home) { new BreadcrumbItem("Correlations", "/correlations", "bi-link-45deg") }; - public static IEnumerable CreateCorrelation = new List(Correlations) { new BreadcrumbItem("New Correlation", "/correlations/new") }; - public static IEnumerable Resources = new List(Home) { new BreadcrumbItem("Resources", "/resources", "bi-files") }; - public static IEnumerable FunctionDefinitionCollections = new List(Resources) { new BreadcrumbItem("Functions", "/resources/collections/functions", "bi-files") }; - public static IEnumerable EventDefinitionCollections = new List(Resources) { new BreadcrumbItem("Events", "/resources/collections/events", "bi-files") }; - public static IEnumerable AuthenticationDefinitionCollections = new List(Resources) { new BreadcrumbItem("Authentications", "/resources/collections/authentications", "bi-files") }; - public static IEnumerable CreateFunctionDefinitionCollection = new List(FunctionDefinitionCollections) { new BreadcrumbItem("New Function Definition Collection", "/resources/collections/functions/new") }; - public static IEnumerable CreateEventDefinitionCollection = new List(EventDefinitionCollections) { new BreadcrumbItem("New Event Definition Collection", "/resources/collections/events/new") }; - public static IEnumerable CreateAuthenticationDefinitionCollection = new List(AuthenticationDefinitionCollections) { new BreadcrumbItem("New Authentication Definition Collection", "/resources/collections/authentications/new") }; - public static IEnumerable About = new List(Home) { new BreadcrumbItem("About", "/application/info", "bi-display") }; - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/IBreadcrumbItem.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/IBreadcrumbItem.cs deleted file mode 100644 index 525909b69..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/IBreadcrumbItem.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - /// - /// Represents a breadcrumb element - /// - public interface IBreadcrumbItem - { - /// - /// The displayed label - /// - string Label { get; init; } - - /// - /// The displayed icon, if any - /// - string? Icon { get; init; } - - /// - /// The navigation link - /// - string Link { get; init; } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/IBreadcrumbManager.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/IBreadcrumbManager.cs deleted file mode 100644 index f49806f2e..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Breadcrumb/IBreadcrumbManager.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.ComponentModel; - -namespace Synapse.Dashboard -{ - /// - /// The service used to manage the breadcrumb - /// - public interface IBreadcrumbManager - { - /// - /// Notifies when the list has changed - /// - event PropertyChangedEventHandler? PropertyChanged; - - /// - /// The list of displayed - /// - List Items { get; init; } - - /// - /// Adds the specified to the list - /// - /// - /// - Task AddItem(IBreadcrumbItem breadcrumbItem); - - /// - /// Creates a new with the specified label and icon for the active route and adds it to the list - /// - /// - /// - /// The created - Task AddCurrentUri(string label, string? icon = null); - - /// - /// Adds the specified to the list - /// - /// - /// - Task RemoveItem(IBreadcrumbItem breadcrumbItem); - - /// - /// Clears the 's list - /// - /// - Task Clear(); - - /// - /// Replaces the current 's list with the provided one - /// - /// - /// - Task Use(IEnumerable list); - - /// - /// Navigate to the provided item and set the breadcrumb state accordingly - /// - /// - /// - Task NavigateTo(IBreadcrumbItem breadcrumbItem); - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/Chart.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/Chart.razor deleted file mode 100644 index 3bd266b61..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/Chart.razor +++ /dev/null @@ -1,96 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using System.Collections -@inject IChartService ChartService -@implements IDisposable -@implements IAsyncDisposable - - - -@code { - - [Parameter] public string Id { get; set; } = Guid.NewGuid().ToString("N").ToLowerInvariant(); - - [Parameter] public ChartConfiguration Configuration { get; set; } = null!; - protected ChartConfiguration configuration { get; set; } = null!; - - protected ElementReference? canvasRef { get; set; } - protected IJSObjectReference? chart = null; - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - await base.OnAfterRenderAsync(firstRender); - if(firstRender && this.chart == null && this.canvasRef != null) - { - this.chart = await this.ChartService.CreateChartAsync(this.canvasRef.Value, this.configuration); - } - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.configuration != this.Configuration) - { - this.configuration = this.Configuration; - if (this.chart != null) - { - await this.chart.InvokeVoidAsync("destroy"); - await this.chart.DisposeAsync(); - this.chart = null; - } - if (this.canvasRef.HasValue) { - this.chart = await this.ChartService.CreateChartAsync(this.canvasRef.Value, this.configuration); - } - } - } - - private bool _Disposed; - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - - public async ValueTask DisposeAsync() - { - await this.DisposeAsyncCore().ConfigureAwait(false); - this.Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - (this.chart as IDisposable)?.Dispose(); - this.chart = null; - this._Disposed = true; - } - } - - protected virtual async ValueTask DisposeAsyncCore() - { - if (this.chart is not null) - { - await this.chart.DisposeAsync().ConfigureAwait(false); - } - this.chart = null; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartConfiguration.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartConfiguration.cs deleted file mode 100644 index 8f73f1f8c..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartConfiguration.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - ///

- /// Represents the object used to configure the - /// - public class ChartConfiguration - { - - /// - /// Gets/sets the 's type - /// - public virtual ChartType Type { get; set; } = ChartType.Pie; - - /// - /// Gets/sets the 's options - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual object? Options { get; set; } - - /// - /// Gets/sets the 's data - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual ChartData Data { get; set; } = new(); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartData.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartData.cs deleted file mode 100644 index 82a544005..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartData.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - ///

- /// Represents the object used to configure a 's data - /// - public class ChartData - { - - /// - /// Gets/sets a containing the 's labels - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual List Labels { get; set; } = new(); - - /// - /// Gets/sets a containing the 's s - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual List Datasets { get; set; } = new(); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartDataset.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartDataset.cs deleted file mode 100644 index 68ae4c3dd..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartDataset.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - ///

- /// Represents a 's dataset - /// - public class ChartDataset - { - - /// - /// Gets/sets the 's , in case of mixed charts - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)] - public virtual ChartType? Type { get; set; } - - /// - /// Gets/sets the 's label - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual string? Label { get; set; } - - /// - /// Gets/sets a containing the data the is made out of - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual List Data { get; set; } = new(); - - /// - /// Gets/sets a containing the background colors to use when rendering the - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual List BackgroundColor { get; set; } = new(); - - /// - /// Gets/sets the border color to use when rendering the - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual string? BorderColor { get; set; } - - /// - /// Gets/sets the border width to use when rendering the - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] - public virtual double? BorderWidth { get; set; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartService.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartService.cs deleted file mode 100644 index 799ac10b0..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartService.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.JSInterop; - -namespace Synapse.Dashboard -{ - public class ChartService - : IChartService - { - protected readonly IJSRuntime jsRuntime; - protected IJSInProcessObjectReference? chartModule = null; - - public ChartService(IJSRuntime jSRuntime) - { - this.jsRuntime = jSRuntime; - } - - public async Task CreateChartAsync(ElementReference canvasRef, ChartConfiguration configuration) - { - if (this.chartModule == null) - { - this.chartModule = await this.jsRuntime.InvokeAsync("import", "./js/chart.js"); - } - return await this.chartModule.InvokeAsync("createChart", canvasRef, configuration); - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartType.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartType.cs deleted file mode 100644 index 8bb7c2141..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/ChartType.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Runtime.Serialization; - -namespace Synapse.Dashboard -{ - - /// - /// Enumerates all supported chart types - /// - [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.StringEnumConverterFactory))] - public enum ChartType - { - /// - /// Indicates a pie chart - /// - [EnumMember(Value = "pie")] - Pie, - /// - /// Indicates a doughnut chart - /// - [EnumMember(Value = "doughnut")] - Doughnut, - /// - /// Indicates a bar chart - /// - [EnumMember(Value = "bar")] - Bar, - /// - /// Indicates a line chart - /// - [EnumMember(Value = "line")] - Line, - /// - /// Indicates a polar area chart - /// - [EnumMember(Value = "polarArea")] - PolarArea, - /// - /// Indicates a bubble chart - /// - [EnumMember(Value = "bubble")] - Bubble, - /// - /// Indicates a radar chart - /// - [EnumMember(Value = "radar")] - Radar, - /// - /// Indicates a scatter chart - /// - [EnumMember(Value = "scatter")] - Scatter - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/IChartService.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/IChartService.cs deleted file mode 100644 index 48b0eb914..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Chart/IChartService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.JSInterop; - -namespace Synapse.Dashboard -{ - interface IChartService - { - Task CreateChartAsync(ElementReference canvasRef, ChartConfiguration configuration); - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/DropdownList/DropdownList.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/DropdownList/DropdownList.razor deleted file mode 100644 index 5d489bc87..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/DropdownList/DropdownList.razor +++ /dev/null @@ -1,127 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@typeparam TItem -

- -@code -{ - - [Parameter] public EventCallback OnToggleExpand { get; set; } - - [Parameter] public EventCallback OnSelectItem { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - [Parameter] public Func NameProvider { get; set; } = elem => elem.ToString(); - - [Parameter] public Func ValueProvider { get; set; } = elem => elem; - - /// - /// Gets/sets the 's header template - /// - [Parameter] public RenderFragment> HeaderTemplate { get; set; } = context => - { - return @; - }; - - /// - /// Gets/sets the 's placeholder - /// - [Parameter] public string Placeholder { get; set; } - - [Parameter] public RenderFragment> ItemTemplate { get; set; } = context => - { - return @ - ; - }; - - [Parameter] public IEnumerable Items { get; set; } - - [Parameter] public TItem SelectedItem { get; set; } - - public object SelectedValue { get; private set; } - - /// - /// Gets a boolean indicating whether or not the is expanded - /// - public bool IsExpanded { get; private set; } - - public async Task ToggleExpand() - { - this.IsExpanded = !this.IsExpanded; - //this.StateHasChanged(); - if (this.OnToggleExpand.HasDelegate) { - await this.OnToggleExpand.InvokeAsync(this.IsExpanded); - } - } - - private async Task SelectItem(TItem item) - { - this.SelectedItem = item; - this.SelectedValue = this.ValueProvider(this.SelectedItem); - if (this.IsExpanded) - this.IsExpanded = false; - //this.StateHasChanged(); - if (this.OnSelectItem.HasDelegate) - { - await this.OnSelectItem.InvokeAsync(this.SelectedItem); - } - - if (this.OnChange.HasDelegate) - { - await this.OnChange.InvokeAsync(new() { Value = this.SelectedValue }); - } - } - - public class ItemRenderContext - { - - public ItemRenderContext(DropdownList dropdown, T item) - { - this.Dropdown = dropdown; - this.Item = item; - } - - public DropdownList Dropdown { get; } - - public T Item { get; } - - public string Name => this.Dropdown.NameProvider(this.Item); - - public object Value => this.Dropdown.ValueProvider(this.Item); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Dynamic/Dynamic.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Dynamic/Dynamic.cs deleted file mode 100644 index a519d6992..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Dynamic/Dynamic.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Rendering; - -namespace Synapse.Dashboard -{ - public class Dynamic - : ComponentBase - { - - [Parameter] - public string Tag { get; set; } - - [Parameter] - public Dictionary AdditionalAttributes { get; set; } - - [Parameter] - public RenderFragment ChildContent { get; set; } - - [Parameter] - public string Class { get; set; } - - [Parameter] - public EventCallback OnClick { get; set; } - - protected override void BuildRenderTree(RenderTreeBuilder builder) - { - if (string.IsNullOrWhiteSpace(Tag)) - throw new ArgumentNullException(nameof(Tag)); - builder.OpenElement(0, Tag); - if (this.AdditionalAttributes == null) - this.AdditionalAttributes = new(); - this.AdditionalAttributes["class"] = this.Class; - this.AdditionalAttributes["onclick"] = this.OnClick; - builder.AddMultipleAttributes(2, AdditionalAttributes); - if (ChildContent != null) - builder.AddContent(3, ChildContent); - builder.CloseElement(); - } - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Jq/InputJqExpression.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Jq/InputJqExpression.razor deleted file mode 100644 index 39871eabd..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Jq/InputJqExpression.razor +++ /dev/null @@ -1,120 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ -@namespace Synapse.Dashboard -@using System.Linq.Expressions -@using System.Diagnostics.CodeAnalysis -@inherits InputBase -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IJSRuntime JsRuntime -@inject IYamlConverter YamlConverter - -@if (!string.IsNullOrWhiteSpace(Label)) -{ - -} - - - -
- Test bench -
- -
- - -
- @output -
-
-
- -
- -
- -@code { - - [Parameter, EditorRequired] public Expression> ValidationFor { get; set; } = default!; - [Parameter] public string? Id { get; set; } - [Parameter] public string? Label { get; set; } - - protected string uid = Guid.NewGuid().ToString(); - protected string editorId => this.Id + "-json-" + this.uid; - protected string editorValueBuffer = ""; - protected MonacoEditor editor = null!; - protected string output = ""; - - protected virtual async Task ToggleLanguage(string language) - { - string text = await this.editor!.GetValue(); - try - { - if (language == PreferedLanguage.YAML) { - this.editorValueBuffer = await this.YamlConverter.JsonToYaml(text); - } - else - { - this.editorValueBuffer = await this.YamlConverter.YamlToJson(text); - } - } - catch(Exception ex) - { - Console.WriteLine(ex.ToString()); - await this.MonacoEditorHelper.ChangePreferedLanguage(language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - this.editorValueBuffer = text; - } - await this.OnMonacoEditorDidInit(this.editor); - this.StateHasChanged(); - } - - - protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out string? result, [NotNullWhen(false)] out string? validationErrorMessage) - { - result = value; - validationErrorMessage = null; - return true; - } - - protected async Task OnMonacoEditorDidInit(MonacoEditorBase editorBase) - { - var model = await (editor as MonacoEditor)!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - await (editor as MonacoEditor)!.SetValue(this.editorValueBuffer); - } - - protected virtual async Task ProcessExpression() - { - if (string.IsNullOrWhiteSpace(this.CurrentValue)) - return; - var trimmedExpression = this.CurrentValue.TrimStart('$').TrimStart('{').TrimEnd('}').Trim(); - if (string.IsNullOrWhiteSpace(trimmedExpression)) - return; - var json = await this.editor.GetValue(); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - json = await this.YamlConverter.YamlToJson(json); - } - this.output = await this.JsRuntime.InvokeAsync("jq.raw", json, trimmedExpression); - // todo: try/catch, display error ? - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/JsonForm/JsonForm.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/JsonForm/JsonForm.razor deleted file mode 100644 index 793195d24..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/JsonForm/JsonForm.razor +++ /dev/null @@ -1,85 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using Newtonsoft.Json.Linq -@using Newtonsoft.Json.Schema -@namespace Synapse.Dashboard - - - @if(Schema != null) - { -

- @foreach(var property in Schema.Properties) - { - - } -
- } - - -@code -{ - - [Parameter] public JSchema Schema { get; set; } = null!; - - [Parameter] public object? Value { get; set; } - - [Parameter] public EventCallback OnValueChanged { get; set; } - - private IDictionary modelProperties = null!; - private List formProperties = new(); - - internal void AddProperty(JsonFormProperty property) - { - this.formProperties.Add(property); - - } - - private object? Get(string propertyName) - { - if (this.modelProperties.TryGetValue(propertyName, out var propertyValue)) - return propertyValue; - else - return null; - } - - public async Task ValidateAsync(IList errors) - { - var value = JObject.FromObject(this.modelProperties); - var result = await Task.FromResult(value.IsValid(this.Schema, out errors)); - return result; - } - - public async Task GetValueAsync() - { - return (dynamic)this.modelProperties; - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.Value == null) - this.Value = this.Schema.GenerateDefault(); - this.modelProperties = this.Value.ToExpandoObject()!; - } - - private async Task OnPropertyValueChanged(string property, object? value) - { - this.modelProperties[property] = value!; - await this.OnValueChanged.InvokeAsync(new() { Value = await this.GetValueAsync() }); - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/JsonForm/JsonFormProperty.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/JsonForm/JsonFormProperty.razor deleted file mode 100644 index 43729371e..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/JsonForm/JsonFormProperty.razor +++ /dev/null @@ -1,104 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using Newtonsoft.Json.Schema -@namespace Synapse.Dashboard - -@if(!string.IsNullOrWhiteSpace(Name) - && Schema != null) -{ -

- @switch (Schema.Type) - { - case JSchemaType.Array: - //todo - break; - case JSchemaType.Boolean: -
- - -
- break; - case JSchemaType.Integer: - - - break; - case JSchemaType.Number: - - - break; - case JSchemaType.Object: -
-
@displayName
-
- -
-
- break; - case JSchemaType.String: - - @if(Schema.Enum != null && Schema.Enum.Any()) - { - - } - else - { - - } - break; - } -
-} - -@code -{ - - [CascadingParameter] private JsonForm Form { get; set; } - - [Parameter] public string Name { get; set; } - - [Parameter] public JSchema Schema { get; set; } - - [Parameter] public object Value { get; set; } - - [Parameter] public bool IsRequired { get; set; } - - [Parameter] public EventCallback OnValueChanged { get; set; } - - string RequiredPropertySuffix => this.IsRequired ? " *" : ""; - - string Format => string.IsNullOrWhiteSpace(this.Schema.Format) ? "text" : this.Schema.Format; - - private string displayName => string.IsNullOrWhiteSpace(this.Schema.Title) ? this.Name : this.Schema.Title; - - protected override void OnInitialized() - { - base.OnInitialized(); - this.Form.AddProperty(this); - } - - private async Task OnInputValueChanged(ChangeEventArgs e) - { - this.Value = e.Value; - await this.OnValueChanged.InvokeAsync(new() { Value = this.Value }); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/KeyValuePairEditor/KeyValuePairEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/KeyValuePairEditor/KeyValuePairEditor.razor deleted file mode 100644 index dd682a427..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/KeyValuePairEditor/KeyValuePairEditor.razor +++ /dev/null @@ -1,55 +0,0 @@ -@namespace Synapse.Dashboard - -@if(Kvp == null) -{ - - - - - - - - -
- - - - - -
-} -else -{ - -} - - -@code { - private string key = ""; - private string value = ""; - [Parameter] public KeyValuePair? Kvp { get; set; } - [Parameter] public EventCallback> OnAdd { get; set; } - [Parameter] public EventCallback> OnChange { get; set; } - - protected override void OnParametersSet() - { - if (this.Kvp.HasValue && (this.Kvp.Value.Key != this.key || this.Kvp.Value.Value != this.value)) - { - this.key = this.Kvp.Value.Key; - this.value = this.Kvp.Value.Value; - } - } - - private void OnPropertyChanged(Action patch) - { - patch(this); - this.OnChange.InvokeAsync(new KeyValuePair(this.key, this.value)); - } - - private void OnAddClicked() - { - this.OnAdd.InvokeAsync(new KeyValuePair(this.key, this.value)); - this.key = ""; - this.value = ""; - } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppHeader.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppHeader.razor deleted file mode 100644 index e7541d6e8..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppHeader.razor +++ /dev/null @@ -1,48 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@implements IDisposable -@inject ILayoutService Layout -@code -{ - - [Parameter] - public RenderFragment? ChildContent { get; set; } - - protected override void OnInitialized() - { - base.OnInitialized(); - if (this.Layout != null) - this.Layout.Header = this; - } - - protected override bool ShouldRender() - { - var shouldRender = base.ShouldRender(); - if (shouldRender) - this.Layout.UpdateHeader(); - return shouldRender; - } - - public void Dispose() - { - if (this.Layout != null) - this.Layout.Header = null; - GC.SuppressFinalize(this); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppLeftSidebar.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppLeftSidebar.razor deleted file mode 100644 index 7592e93e4..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppLeftSidebar.razor +++ /dev/null @@ -1,48 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@implements IDisposable -@inject ILayoutService Layout -@code -{ - - [Parameter] - public RenderFragment? ChildContent { get; set; } - - protected override void OnInitialized() - { - base.OnInitialized(); - if (this.Layout != null) - this.Layout.LeftSidebar = this; - } - - protected override bool ShouldRender() - { - var shouldRender = base.ShouldRender(); - if (shouldRender) - this.Layout.UpdateLeftSidebar(); - return shouldRender; - } - - public void Dispose() - { - if (this.Layout != null) - this.Layout.LeftSidebar = null; - GC.SuppressFinalize(this); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppRightSidebar.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppRightSidebar.razor deleted file mode 100644 index 766f02df3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/AppRightSidebar.razor +++ /dev/null @@ -1,48 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@implements IDisposable -@inject ILayoutService Layout -@code -{ - - [Parameter] - public RenderFragment? ChildContent { get; set; } - - protected override void OnInitialized() - { - base.OnInitialized(); - if (this.Layout != null) - this.Layout.RightSidebar = this; - } - - protected override bool ShouldRender() - { - var shouldRender = base.ShouldRender(); - if (shouldRender) - this.Layout.UpdateRightSidebar(); - return shouldRender; - } - - public void Dispose() - { - if (this.Layout != null) - this.Layout.RightSidebar = null; - GC.SuppressFinalize(this); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor deleted file mode 100644 index e03195dc3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor +++ /dev/null @@ -1,51 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using System.ComponentModel -@namespace Synapse.Dashboard -@inject ILayoutService Layout; -@implements IDisposable - -

-
- @Layout.Header?.ChildContent -
-
- -@code -{ - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.Layout.PropertyChanged += OnLayoutHeaderChanged; - } - - private void OnLayoutHeaderChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(ILayoutService.Header)) - { - this.StateHasChanged(); - } - } - - public void Dispose() - { - if (this.Layout != null) - this.Layout.PropertyChanged -= OnLayoutHeaderChanged; - GC.SuppressFinalize(this); - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.css deleted file mode 100644 index 6e263a18d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.css +++ /dev/null @@ -1,25 +0,0 @@ -.header { - background-color: var(--bs-gray-100); - border-bottom: 1px solid var(--bs-gray-200); - justify-content: center; - height: 3.5rem; - display: flex; - align-items: center; -} -.header ::deep a, .header .top-row .btn-link { - white-space: nowrap; - margin-left: 1.5rem; -} -.header a:first-child { - overflow: hidden; - text-overflow: ellipsis; -} - -@media (max-width: 640.98px) { - .header.auth { - justify-content: space-between; - } - .header a, .header .btn-link { - margin-left: 0; - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.min.css deleted file mode 100644 index fc1343c70..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.header{background-color:var(--bs-gray-100);border-bottom:1px solid var(--bs-gray-200);justify-content:center;height:3.5rem;display:flex;align-items:center;}.header ::deep a,.header .top-row .btn-link{white-space:nowrap;margin-left:1.5rem;}.header a:first-child{overflow:hidden;text-overflow:ellipsis;}@media(max-width:640.98px){.header.auth{justify-content:space-between;}.header a,.header .btn-link{margin-left:0;}} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.scss deleted file mode 100644 index f53fdeab2..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/Header.razor.scss +++ /dev/null @@ -1,30 +0,0 @@ -.header { - background-color: var(--bs-gray-100); - border-bottom: 1px solid var(--bs-gray-200); - justify-content: center; - height: 3.5rem; - display: flex; - align-items: center; - - ::deep a, .top-row .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - } - - a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } -} - - -@media (max-width: 640.98px) { - - .header.auth { - justify-content: space-between; - } - - .header a, .header .btn-link { - margin-left: 0; - } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/ILayoutService.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/ILayoutService.cs deleted file mode 100644 index d4f4c76c6..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/ILayoutService.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.ComponentModel; - -namespace Synapse.Dashboard -{ - - public interface ILayoutService - : INotifyPropertyChanged - { - - AppHeader? Header { get; set; } - AppRightSidebar? RightSidebar { get; set; } - AppLeftSidebar? LeftSidebar { get; set; } - - void UpdateHeader(); - - void UpdateRightSidebar(); - - void UpdateLeftSidebar(); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/LayoutService.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/LayoutService.cs deleted file mode 100644 index da171227a..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/LayoutService.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using System.ComponentModel; - -namespace Synapse.Dashboard -{ - public class LayoutService - : ILayoutService - { - - public event PropertyChangedEventHandler? PropertyChanged; - - public RenderFragment? HeaderFragment => this.Header?.ChildContent; - public RenderFragment? RightSidebarFragment => this.RightSidebar?.ChildContent; - public RenderFragment? LeftSidebarFragment => this.LeftSidebar?.ChildContent; - - private AppHeader? _Header; - public AppHeader? Header - { - get => this._Header; - set - { - if (this._Header == value) return; - this._Header = value; - this.UpdateHeader(); - } - } - - private AppRightSidebar? _RightSidebar; - public AppRightSidebar? RightSidebar - { - get => this._RightSidebar; - set - { - if (this._RightSidebar == value) return; - this._RightSidebar = value; - this.UpdateRightSidebar(); - } - } - - private AppLeftSidebar? _LeftSidebar; - public AppLeftSidebar? LeftSidebar - { - get => this._LeftSidebar; - set - { - if (this._LeftSidebar == value) return; - this._LeftSidebar = value; - this.UpdateLeftSidebar(); - } - } - - public void UpdateHeader() - { - if (this.PropertyChanged != null) - { - this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Header))); - } - } - - public void UpdateRightSidebar() - { - if (this.PropertyChanged != null) - { - this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.RightSidebar))); - } - } - - public void UpdateLeftSidebar() - { - if (this.PropertyChanged != null) - { - this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.LeftSidebar))); - } - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/LeftSidebar.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/LeftSidebar.razor deleted file mode 100644 index 5aa133dc3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/LeftSidebar.razor +++ /dev/null @@ -1,51 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using System.ComponentModel -@namespace Synapse.Dashboard -@inject ILayoutService Layout; -@implements IDisposable - -@if (Layout.LeftSidebar != null) -{ -

-} - -@code -{ - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.Layout.PropertyChanged += OnLayoutChanged; - } - - private void OnLayoutChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(ILayoutService.LeftSidebar)) - { - this.StateHasChanged(); - } - } - - public void Dispose() - { - if (this.Layout != null) - this.Layout.PropertyChanged -= this.OnLayoutChanged; - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor deleted file mode 100644 index 8ca957f21..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor +++ /dev/null @@ -1,34 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits LayoutComponentBase - -

- -
- - -
- -
- @Body -
- -
-
-
- \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.css deleted file mode 100644 index 475d46553..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.css +++ /dev/null @@ -1,9 +0,0 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -.main { - flex: 1; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.min.css deleted file mode 100644 index 63044e758..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.page{position:relative;display:flex;flex-direction:column;}.main{flex:1;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.scss deleted file mode 100644 index e182330eb..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/MainLayout.razor.scss +++ /dev/null @@ -1,9 +0,0 @@ -.page { - position: relative; - display: flex; - flex-direction: column; -} - -.main { - flex: 1; -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor deleted file mode 100644 index ecf26d7d4..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor +++ /dev/null @@ -1,82 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -

- -@code -{ - - private bool collapseNavMenu = true; - - private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; - - private void ToggleNavMenu() - { - collapseNavMenu = !collapseNavMenu; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.css deleted file mode 100644 index 0341b3d5b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.css +++ /dev/null @@ -1,52 +0,0 @@ -.header { - padding: 0 !important; - flex-direction: column; -} -.header .navbar-brand { - background-color: rgba(var(--bs-black-rgb), 0.4); - height: 3rem; - margin-right: 0 !important; - padding-left: 1.5rem; - padding-right: 1.5rem; -} -.header .navbar-brand .logo-container { - margin: auto; - width: 200px; -} -.header .white { - fill: var(--bs-white); - stroke: var(--bs-white); -} -.header .logo { - height: 100%; -} -.header .logotype { - height: 66.6%; -} -.header .nav-item ::deep a { - color: var(--bs-gray-100); - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - padding-left: 1.5rem; - padding-right: 1.5rem; - font-weight: 600; -} -.header .nav-item ::deep a.active { - background-color: var(--bs-white); - color: var(--bs-black); -} -.header .nav-item ::deep a:hover { - background-color: rgba(var(--bs-white-rgb), 0.1); - color: white; -} -.header .bi { - margin-right: 0.5rem; -} - -@media (min-width: 992px) { - .header { - flex-direction: row; - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.min.css deleted file mode 100644 index a3142a677..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.header{padding:0!important;flex-direction:column;}.header .navbar-brand{background-color:rgba(var(--bs-black-rgb),.4);height:3rem;margin-right:0!important;padding-left:1.5rem;padding-right:1.5rem;}.header .navbar-brand .logo-container{margin:auto;width:200px;}.header .white{fill:var(--bs-white);stroke:var(--bs-white);}.header .logo{height:100%;}.header .logotype{height:66.6%;}.header .nav-item ::deep a{color:var(--bs-gray-100);height:3rem;display:flex;align-items:center;line-height:3rem;padding-left:1.5rem;padding-right:1.5rem;font-weight:600;}.header .nav-item ::deep a.active{background-color:var(--bs-white);color:var(--bs-black);}.header .nav-item ::deep a:hover{background-color:rgba(var(--bs-white-rgb),.1);color:#fff;}.header .bi{margin-right:.5rem;}@media(min-width:992px){.header{flex-direction:row;}} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.scss deleted file mode 100644 index ab35c8956..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/NavMenu.razor.scss +++ /dev/null @@ -1,62 +0,0 @@ -.header { - padding: 0 !important; - flex-direction: column; - - .navbar-brand { - background-color: rgba(var(--bs-black-rgb),0.4); - height: 3rem; - margin-right: 0 !important; - padding-left: 1.5rem; - padding-right: 1.5rem; - .logo-container { - margin: auto; - width: 200px; - } - } - - .white { - fill: var(--bs-white); - stroke: var(--bs-white); - } - - .logo { - height: 100%; - } - - .logotype { - height: 66.6%; - } - - - .nav-item ::deep a { - color: var(--bs-gray-100); - height: 3rem; - display: flex; - align-items: center; - line-height: 3rem; - padding-left: 1.5rem; - padding-right: 1.5rem; - font-weight: 600; - - &.active { - background-color: var(--bs-white); - color: var(--bs-black); - } - - &:hover { - background-color: rgba(var(--bs-white-rgb),0.1); - color: white; - } - } - - .bi { - margin-right: 0.5rem; - } -} - - -@media (min-width: 992px) { - .header { - flex-direction: row; - } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/RightSidebar.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/RightSidebar.razor deleted file mode 100644 index c87f6bec3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Layout/RightSidebar.razor +++ /dev/null @@ -1,51 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using System.ComponentModel -@namespace Synapse.Dashboard -@inject ILayoutService Layout; -@implements IDisposable - -@if (Layout.RightSidebar != null) -{ -

-} - -@code -{ - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.Layout.PropertyChanged += OnLayoutChanged; - } - - private void OnLayoutChanged(object? sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == nameof(ILayoutService.RightSidebar)) - { - this.StateHasChanged(); - } - } - - public void Dispose() - { - if (this.Layout != null) - this.Layout.PropertyChanged -= this.OnLayoutChanged; - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor deleted file mode 100644 index 7f5136bb7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor +++ /dev/null @@ -1,30 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -

- - - - - - - - - - -
\ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.css deleted file mode 100644 index 7ffc87c2b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.css +++ /dev/null @@ -1,22 +0,0 @@ -.loader-container { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; -} -.loader-container .logo-viewbox { - width: 75px; - fill: var(--bs-primary); - stroke: var(--bs-primary); -} - -@keyframes fadeIn { - from { - opacity: 0; - } -} -.animation-in-out { - animation: fadeIn 0.666s infinite alternate; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.min.css deleted file mode 100644 index f92b158c8..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.loader-container{width:100%;height:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;}.loader-container .logo-viewbox{width:75px;fill:var(--bs-primary);stroke:var(--bs-primary);}@keyframes fadeIn{from{opacity:0;}}.animation-in-out{animation:fadeIn .666s infinite alternate;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.scss deleted file mode 100644 index 3b90d1152..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Loader/Loader.razor.scss +++ /dev/null @@ -1,24 +0,0 @@ -.loader-container { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - - .logo-viewbox { - width: 75px; - fill: var(--bs-primary); - stroke: var(--bs-primary); - } -} - -@keyframes fadeIn { - from { - opacity: 0; - } -} - -.animation-in-out { - animation: fadeIn 0.666s infinite alternate; -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/Modal.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/Modal.razor deleted file mode 100644 index 0d27d138f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/Modal.razor +++ /dev/null @@ -1,57 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits ModalBase - -

- -@if (Active) -{ - -} - -@code -{ - - [Parameter] public RenderFragment Title { get; set; } - - [Parameter] public RenderFragment Body { get; set; } - - [Parameter] public RenderFragment Footer { get; set; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/ModalBase.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/ModalBase.cs deleted file mode 100644 index ba3e99c20..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/ModalBase.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using Microsoft.AspNetCore.Components; - -namespace Synapse.Dashboard -{ - public abstract class ModalBase - : ComponentBase - { - - [Parameter] - public virtual ModalSize Size { get; set; } = ModalSize.Large; - - [Parameter] - public virtual bool ShowCloseIcon { get; set; } = true; - - [Parameter] - public virtual EventCallback OnActiveChange { get; set; } - - [Parameter] - public virtual EventCallback OnShow { get; set; } - - [Parameter] - public virtual EventCallback OnHide { get; set; } - - public virtual bool Active { get; set; } = false; - - protected virtual string modalSizeClass => this.Size switch - { - ModalSize.Small => "modal-sm", - ModalSize.Default => "", - ModalSize.Large => "modal-lg", - ModalSize.ExtraLarge => "modal-xl", - _ => throw new NotSupportedException($"The specified {nameof(ModalSize)} '{this.Size}' is not supported") - }; - - public virtual async Task ToggleAsync() - { - this.Active = !this.Active; - await this.OnActiveChange.InvokeAsync(this.Active); - if (!this.Active) - await this.OnHide.InvokeAsync(); - } - - public virtual async Task ShowAsync() - { - this.Active = true; - await this.OnActiveChange.InvokeAsync(this.Active); - await this.OnShow.InvokeAsync(); - } - - public virtual async Task HideAsync() - { - this.Active = false; - await this.OnActiveChange.InvokeAsync(this.Active); - await this.OnHide.InvokeAsync(); - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/ModalSize.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/ModalSize.cs deleted file mode 100644 index f8e041e74..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Modal/ModalSize.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - /// - /// Enumerates all supported modal sizes - /// - public enum ModalSize - { - /// - /// Indicates a small modal - /// - Small, - /// - /// Indicates a normal modal - /// - Default, - /// - /// Indicates a large modal - /// - Large, - /// - /// Indicates an extra large modal - /// - ExtraLarge - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/IMonacoEditorHelper.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/IMonacoEditorHelper.cs deleted file mode 100644 index 59ba710e3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/IMonacoEditorHelper.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using BlazorMonaco; -using System.ComponentModel; - -namespace Synapse.Dashboard -{ - public delegate Task PreferedLanguageChangedEventHandler(string newLanguage); - - public interface IMonacoEditorHelper - { - string PreferedLanguage { get; } - - event PreferedLanguageChangedEventHandler? PreferedLanguageChanged; - - Func GetStandaloneEditorConstructionOptions(string value = "", bool readOnly = false, string language = "json"); - - Func GetDiffEditorConstructionOptions(bool readOnly = true); - - Task ChangePreferedLanguage(string language); - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/IMonacoEditorMarker.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/IMonacoEditorMarker.cs deleted file mode 100644 index 384ad5129..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/IMonacoEditorMarker.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - /// - /// See https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IMarker.html - /// - public interface IMonacoEditorMarker - { - public string? Code { get; set; } - public int EndColumn { get; set; } - public int EndLineNumber { get; set; } - public string Message { get; set; } - public string Owner { get; set; } - public IEnumerable? RelatedInformation { get; set; } - public Object Resource { get; set; } - public int Severity { get; set; } - public int StartColumn { get; set; } - public int StartLineNumber { get; set; } - public IEnumerable? Tags { get; set; } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/InputDynamicObject.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/InputDynamicObject.razor deleted file mode 100644 index 70c44cbbf..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/InputDynamicObject.razor +++ /dev/null @@ -1,127 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Neuroglia.Serialization -@using System.Linq.Expressions -@using System.Diagnostics.CodeAnalysis -@inherits InputBase -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IJsonSerializer Serializer -@inject IYamlConverter YamlConverter - -@if (!string.IsNullOrWhiteSpace(Label)) -{ - -} - - - - -@code { - - [Parameter, EditorRequired] public Expression> ValidationFor { get; set; } = default!; - [Parameter] public string? Id { get; set; } - [Parameter] public string? Label { get; set; } - - protected string ComputedCssClass => "monaco-small " + this.CssClass; - protected DynamicObject lastParsed = null!; - protected string editorId => this.Id + "-json"; - protected MonacoEditor editor = null!; - - protected virtual async Task ToggleLanguage(string language) - { - string text = await this.editor!.GetValue(); - try - { - if (language == PreferedLanguage.YAML) { - await this.YamlConverter.JsonToYaml(text); - } - else - { - await this.YamlConverter.YamlToJson(text); - } - } - catch(Exception ex) - { - Console.WriteLine(ex.ToString()); - await this.MonacoEditorHelper.ChangePreferedLanguage(language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - } - await this.OnEditorInit(this.editor); - this.StateHasChanged(); - } - - protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out DynamicObject result, [NotNullWhen(false)] out string? validationErrorMessage) - { - if (this.editor == null) - { - result = this.Serializer.Deserialize("{}"); - this.lastParsed = result; - validationErrorMessage = "Editor isn't initialized"; - } - try - { - result = this.Serializer.Deserialize(string.IsNullOrWhiteSpace(value) ? "{}" : value); - this.lastParsed = result; - validationErrorMessage = ""; - } - catch(Exception ex) - { - result = this.lastParsed; - validationErrorMessage = "Enabled to parse JSON"; - } - return true; - } - - protected override string? FormatValueAsString(DynamicObject? value) => value != null ? this.Serializer.Serialize(value) : ""; - - protected async Task OnEditorInit(MonacoEditorBase editorBase) - { - var model = await (editor as MonacoEditor)!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - string value = "{}"; - if (!string.IsNullOrWhiteSpace(this.CurrentValueAsString)) - { - value = this.CurrentValueAsString; - } - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - value = await this.YamlConverter.JsonToYaml(value); - } - await this.editor.SetValue(value); - } - - protected async Task OnEditorChange(ModelContentChangedEvent e) - { - string value = await this.editor.GetValue(); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - value = await this.YamlConverter.YamlToJson(value); - } - if (this.CurrentValueAsString != value) - { - this.CurrentValueAsString = value; - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorHelper.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorHelper.cs deleted file mode 100644 index 5699e0a47..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorHelper.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using BlazorMonaco; - -namespace Synapse.Dashboard -{ - public class MonacoEditorHelper - : IMonacoEditorHelper - { - public string PreferedLanguage { get; protected set; } = "json"; - - public event PreferedLanguageChangedEventHandler? PreferedLanguageChanged; - - public Func GetStandaloneEditorConstructionOptions(string value = "", bool readOnly = false, string language = "json") { - return (MonacoEditor editor) => new StandaloneEditorConstructionOptions - { - AutomaticLayout = true, - Minimap = new EditorMinimapOptions { Enabled = false }, - Language = language, - ReadOnly = readOnly, - Value = value - }; - } - - public Func GetDiffEditorConstructionOptions(bool readOnly = true) - { - return (MonacoDiffEditor editor) => new DiffEditorConstructionOptions - { - AutomaticLayout = true, - Minimap = new EditorMinimapOptions { Enabled = false }, - ReadOnly = readOnly - }; - } - - public async Task ChangePreferedLanguage(string language) - { - if (!string.IsNullOrEmpty(language) && language != this.PreferedLanguage) - { - this.PreferedLanguage = language; - await this.OnPreferedLanguageChange(language); - } - } - - protected async Task OnPreferedLanguageChange(string language) - { - if (this.PreferedLanguageChanged != null) - { - await this.PreferedLanguageChanged.Invoke(language); - } - await Task.CompletedTask; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorMarker.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorMarker.cs deleted file mode 100644 index 3b31f9b36..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorMarker.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - public class MonacoEditorMarker : IMonacoEditorMarker - { - public string? Code { get; set; } - public int EndColumn { get; set; } - public int EndLineNumber { get; set; } - public string Message { get; set; } = ""; - public string Owner { get; set; } = ""; - public IEnumerable? RelatedInformation { get; set; } - public Object Resource { get; set; } = ""; - public int Severity { get; set; } - public int StartColumn { get; set; } - public int StartLineNumber { get; set; } - public IEnumerable? Tags { get; set; } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorMarkerSeverity.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorMarkerSeverity.cs deleted file mode 100644 index a8a7d4df4..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/MonacoEditorMarkerSeverity.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - /// - /// See https://microsoft.github.io/monaco-editor/api/enums/monaco.MarkerSeverity.html - /// - public enum MonacoEditorMarkerSeverity - { - Hint = 1, - Info = 2, - Warning = 4, - Error = 8, - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/PreferedLanguage.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/PreferedLanguage.cs deleted file mode 100644 index ddb8581da..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/PreferedLanguage.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Synapse.Dashboard -{ - public static class PreferedLanguage - { - public const string JSON = "json"; - public const string YAML = "yaml"; - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/PreferedLanguageSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/PreferedLanguageSelector.razor deleted file mode 100644 index 1435642f9..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/MonacoEditor/PreferedLanguageSelector.razor +++ /dev/null @@ -1,67 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ -@namespace Synapse.Dashboard -@inject IMonacoEditorHelper MonacoEditorHelper -@implements IDisposable - -
- - -
- -@code { - - [Parameter] public EventCallback PreferedLanguageChange { get; set; } - - protected bool isJsonSelected { get; set; } - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.isJsonSelected = this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.JSON; - } - - protected virtual async Task ToggleLanguage(ChangeEventArgs e) - { - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.JSON) { - await this.MonacoEditorHelper.ChangePreferedLanguage(PreferedLanguage.YAML); - } - else - { - await this.MonacoEditorHelper.ChangePreferedLanguage(PreferedLanguage.JSON); - } - } - - protected override void OnInitialized() - { - base.OnInitialized(); - this.MonacoEditorHelper.PreferedLanguageChanged += this.HandlePreferedLanguageChange; - } - - protected async Task HandlePreferedLanguageChange(string language) - { - this.isJsonSelected = this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.JSON; - await this.PreferedLanguageChange.InvokeAsync(language); - this.StateHasChanged(); - } - - public void Dispose() - { - if (this.MonacoEditorHelper != null) - this.MonacoEditorHelper.PreferedLanguageChanged -= this.HandlePreferedLanguageChange; - GC.SuppressFinalize(this); - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/PublishEventModal/PublishEventModal.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/PublishEventModal/PublishEventModal.razor deleted file mode 100644 index 1aef20418..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/PublishEventModal/PublishEventModal.razor +++ /dev/null @@ -1,215 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using CloudNative.CloudEvents -@using CloudNative.CloudEvents.Http -@using Neuroglia.Serialization -@inherits ModalBase -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IYamlConverter YamlConverter -@inject IJsonSerializer JsonSerializer -@inject HttpClient HttpClient; -@inject CloudEventFormatter CloudEventFormatter - - - -@if (Active) -{ - -} - -@code{ - - private MonacoEditor? payloadEditor; - private ExtensionAttribute extensionAttribute = new(); - - private V1Event? cloudEvent; - [Parameter] public V1Event? CloudEvent { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if(this.cloudEvent != this.CloudEvent) - { - this.cloudEvent = this.CloudEvent; - this.StateHasChanged(); - } - } - - private async Task OnTogglePayloadEditorEditorLanguage(string language) - { - await this.SetPayloadEditorLanguageAsync(); - } - - private async Task SetPayloadEditorLanguageAsync() - { - var model = await this.payloadEditor!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - } - - private async Task OnPayloadEditorInit(MonacoEditorBase editor) - { - await this.SetPayloadEditorLanguageAsync(); - var text = "{}"; - if (this.cloudEvent?.Data != null) - text = await this.JsonSerializer.SerializeAsync(this.cloudEvent.Data); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - text = await this.YamlConverter.JsonToYaml(text); - await this.payloadEditor!.SetValue(text); - } - - private async Task OnPayloadChanged(ModelContentChangedEvent e) - { - var rawPayload = await this.payloadEditor!.GetValue(); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - rawPayload = await this.YamlConverter.YamlToJson(rawPayload); - var payload = await this.JsonSerializer.DeserializeAsync(rawPayload); - if (payload == null) payload = Neuroglia.Serialization.Dynamic.FromObject(new()); - this.OnEventChanged(evt => evt.Data = payload); - } - - private void OnEventChanged(Action patch) - { - if (this.cloudEvent == null) this.cloudEvent = V1Event.Create(); - patch(this.cloudEvent); - } - - private void OnRemoveExtensionAttribute(string name) - { - if (this.cloudEvent == null) return; - this.cloudEvent.ExtensionAttributes.Remove(name); - this.StateHasChanged(); - } - - private void OnAddExtensionAttribute() - { - if (this.cloudEvent == null) this.cloudEvent = V1Event.Create(); - Neuroglia.Serialization.Dynamic dynamic = (string.IsNullOrWhiteSpace(this.extensionAttribute.Value) ? null : Neuroglia.Serialization.Dynamic.FromObject(this.extensionAttribute.Value))!; - if (this.cloudEvent.ExtensionAttributes == null) this.cloudEvent.ExtensionAttributes = new(); - this.cloudEvent.ExtensionAttributes[this.extensionAttribute.Name] = dynamic; - this.extensionAttribute = new(); - this.StateHasChanged(); - } - - private void OnNewExtensionAttributeChanged(Action patch) - { - patch(this.extensionAttribute); - this.StateHasChanged(); - } - - private async Task OnPublishEvent() - { - if (this.cloudEvent == null) return; - var cloudEvent = this.cloudEvent.ToCloudEvent(); - using var content = cloudEvent.ToHttpContent(ContentMode.Structured, this.CloudEventFormatter); - using var request = new HttpRequestMessage(HttpMethod.Post, "/") { Content = content }; - using var response = await this.HttpClient.SendAsync(request); - response.EnsureSuccessStatusCode(); - await this.HideAsync(); - } - - public record ExtensionAttribute - { - - public string Name { get; set; } - - public string Value { get; set; } - - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Routing/RoutingActions.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Routing/RoutingActions.cs deleted file mode 100644 index 38da5a65d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Routing/RoutingActions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Microsoft.AspNetCore.Components; - -namespace Synapse.Dashboard.Features.Shared.Routing.Actions -{ - public class NavigateTo - { - public string Uri { get; } - public bool ForceLoad { get; } - public bool Replace { get; } - - public NavigateTo(string uri, bool forceLoad = false, bool replace = false) - { - this.Uri = uri; - this.ForceLoad = forceLoad; - this.Replace = replace; - } - - public NavigateTo(string uri, NavigationOptions options) - : this(uri, options.ForceLoad, options.ReplaceHistoryEntry) - { - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Routing/RoutingEffects.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Routing/RoutingEffects.cs deleted file mode 100644 index 6ec83e69b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Routing/RoutingEffects.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Neuroglia.Data.Flux; -using Synapse.Dashboard.Features.Shared.Routing.Actions; - -namespace Synapse.Dashboard.Features.Shared.Routing.Effects -{ - [Effect] - public static class RoutingEffects - { - public static async Task OnNavigateTo(NavigateTo action, IEffectContext context) - { - var navigationManager = context.Services.GetRequiredService(); - if (navigationManager == null) - throw new NullReferenceException("Unable to resolved service 'NavigationManager'."); - navigationManager.NavigateTo(action.Uri, action.ForceLoad, action.Replace); - await Task.CompletedTask; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor deleted file mode 100644 index ffc986c32..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor +++ /dev/null @@ -1,47 +0,0 @@ -@namespace Synapse.Dashboard - -
- -
-@code { - - [Parameter] public string? PlaceHolder { get; set; } - - [Parameter] public EventCallback OnTermChange { get; set; } - - [Parameter] public EventCallback OnSearch { get; set; } - - [Parameter] public EventCallback OnClear { get; set; } - - public string? Term { get; private set; } - - private async Task OnTermInputChange(ChangeEventArgs e) - { - this.Term = e.Value as string; - await this.OnSearch.InvokeAsync(this.Term); - } - - private async Task OnSearchButtonClick(MouseEventArgs e) - { - await this.OnSearch.InvokeAsync(this.Term); - } - - private async Task OnClearButtonClick(MouseEventArgs e) - { - this.Term = string.Empty; - await this.OnClear.InvokeAsync(this.Term); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.css deleted file mode 100644 index b68d537a0..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.css +++ /dev/null @@ -1,35 +0,0 @@ -.search-box .input-left { - position: relative; - z-index: 3; -} -.search-box .input-left input { - padding-right: 3rem; - border-top-right-radius: 0 !important; - border-bottom-right-radius: 0 !important; -} -.search-box .input-group-append { - margin-left: 0; - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; -} -.search-box .input-group-append .btn { - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; -} -.search-box .btn { - display: flex; - align-items: center; - justify-content: center; - height: 100%; -} -.search-box .btn i { - color: var(--bs-primary); - margin: 0; -} -.search-box .reset { - width: 2.5rem; - position: absolute; - right: 0; - top: 0; - padding: 0; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.min.css deleted file mode 100644 index 5fa9a2a03..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.search-box .input-left{position:relative;z-index:3;}.search-box .input-left input{padding-right:3rem;border-top-right-radius:0!important;border-bottom-right-radius:0!important;}.search-box .input-group-append{margin-left:0;border-top-left-radius:0!important;border-bottom-left-radius:0!important;}.search-box .input-group-append .btn{border-top-left-radius:0!important;border-bottom-left-radius:0!important;}.search-box .btn{display:flex;align-items:center;justify-content:center;height:100%;}.search-box .btn i{color:var(--bs-primary);margin:0;}.search-box .reset{width:2.5rem;position:absolute;right:0;top:0;padding:0;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.scss deleted file mode 100644 index 63dbedd76..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/SearchBox/SearchBox.razor.scss +++ /dev/null @@ -1,44 +0,0 @@ -.search-box { - - .input-left { - position: relative; - z-index: 3; - - input { - padding-right: 3rem; - border-top-right-radius: 0 !important; - border-bottom-right-radius: 0 !important; - } - } - - .input-group-append { - margin-left: 0; - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; - - .btn { - border-top-left-radius: 0 !important; - border-bottom-left-radius: 0 !important; - } - } - - .btn { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - - i { - color: var(--bs-primary); - margin: 0; - } - } - - .reset { - width: 2.5rem; - position: absolute; - right: 0; - top: 0; - padding: 0; - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/TabControl/TabControl.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/TabControl/TabControl.razor deleted file mode 100644 index 629360077..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/TabControl/TabControl.razor +++ /dev/null @@ -1,72 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -

-
- -
-
-
- - @ChildContent - -
-
-
- -@code -{ - - [Parameter] public RenderFragment? ChildContent { get; set; } - [Parameter] public string? ContainerCssClass { get; set; } - [Parameter] public string? NavCssClass { get; set; } - [Parameter] public string? PageCssClass { get; set; } - [Parameter] public EventCallback OnTabPageChange { get; set; } - - public TabPage? ActivePage { get; set; } - - List Pages = new List(); - - internal void AddPage(TabPage tabPage) - { - this.Pages.Add(tabPage); - if (this.Pages.Count == 1) - this.ActivePage = tabPage; - this.StateHasChanged(); - } - - string GetButtonClass(TabPage page) - { - return page == this.ActivePage ? "active" : ""; - } - - public async Task ActivatePageAsync(TabPage page) - { - this.ActivePage = page; - await this.OnTabPageChange.InvokeAsync(page); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/TabControl/TabPage.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/TabControl/TabPage.razor deleted file mode 100644 index 536c7cacd..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/TabControl/TabPage.razor +++ /dev/null @@ -1,56 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if (Parent.ActivePage == this) -{ - @ChildContent -} - -@code -{ - - [CascadingParameter] private TabControl Parent { get; set; } = null!; - - [Parameter] public string? Header { get; set; } - - [Parameter] public RenderFragment? ChildContent { get; set; } - - private bool isActive; - [Parameter] public bool IsActive { get; set; } - - protected override void OnInitialized() - { - if (this.Parent == null) - throw new ArgumentNullException(nameof(Parent), "The TabPage component must exist within a TabControl"); - this.Parent.AddPage(this); - base.OnInitialized(); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.isActive != this.IsActive) - { - this.isActive = this.IsActive; - if (this.isActive && this.Parent.ActivePage != this) - await this.Parent.ActivatePageAsync(this); - this.StateHasChanged(); - } - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Cell.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Cell.razor deleted file mode 100644 index d871bea44..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Cell.razor +++ /dev/null @@ -1,27 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@typeparam T - -@RenderContext.Value - -@code { - - [Parameter] - public CellRenderingContext RenderContext { get; set; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/CellCollectionPresenter.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/CellCollectionPresenter.razor deleted file mode 100644 index f9e0e4870..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/CellCollectionPresenter.razor +++ /dev/null @@ -1,36 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@typeparam T - - @foreach(var column in Table.columns.Where(c => c.IsVisible)) -{ - - @if(column.CellTemplate == null) - @Table.DefaultCellTemplate(new(Table, column, Item)) - else - @column.CellTemplate(new(Table, column, Item)) - -} - -@code { - - [CascadingParameter] public Table Table { get; set; } - - [Parameter] public T Item { get; set; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/CellRenderingContext.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/CellRenderingContext.cs deleted file mode 100644 index 5a61849c7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/CellRenderingContext.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - public class CellRenderingContext - { - - public CellRenderingContext(Table table, Column column, T item) - { - this.Table = table; - this.Column = column; - this.Item = item; - } - - public Table Table { get; } - - public Column Column { get; } - - public T Item { get; } - - private object? _Value; - - public object? Value - { - get - { - if (this._Value == null) - this._Value = this.Column.GetValueFor(this.Item); - return this._Value; - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Column.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Column.razor deleted file mode 100644 index 824d128b3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Column.razor +++ /dev/null @@ -1,147 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using System.Linq.Expressions -@using System.Reflection -@using System.ComponentModel.DataAnnotations -@namespace Synapse.Dashboard -@typeparam T - -@code { - - [CascadingParameter] public Table Table { get; set; } = null!; - - [Parameter] public string? Name { get; set; } - - [Parameter] public string? CssClass { get; set; } - - [Parameter] public string? Description { get; set; } - - [Parameter] public Type? Type { get; set; } - - [Parameter] public string? Format { get; set; } - - [Parameter] public IFormatProvider? FormatProvider { get; set; } - - [Parameter] public PropertyPath? PropertyPath { get; set; } - - [Parameter] public bool IsDraggable { get; set; } = true; - - [Parameter] public bool IsFilterable { get; set; } = true; - - [Parameter] public bool IsSorteable { get; set; } = true; - - [Parameter] public bool IsVisible { get; set; } = true; - - [Parameter] public int? DisplayOrder { get; set; } - - public SortMode SortMode { get; private set; } - - public bool AutoGenerated { get; private set; } - - [Parameter] public Expression>? ValueProvider { get; set; } - - [Parameter] public RenderFragment>? HeaderTemplate { get;set; } - - [Parameter] public RenderFragment>? CellTemplate { get;set; } - - [Parameter] public EventCallback> OnDragStart { get; set; } - - [Parameter] public EventCallback> OnDragEnd { get; set; } - - [Parameter] public EventCallback> OnDrop { get; set; } - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.Table.AddColumn(this); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.ValueProvider == null) - this.BuildValueProvider(); - } - - private void BuildValueProvider() - { - if(this.PropertyPath == null) - { - this.ValueProvider = _ => null; - } - else - { - var parameterExpression = Expression.Parameter(typeof(T)); - var memberExpression = this.PropertyPath.ToExpression(parameterExpression); - var bodyExpression = Expression.Convert(memberExpression, typeof(object)); - this.Type = memberExpression.Type; - this.ValueProvider = Expression.Lambda>(bodyExpression, parameterExpression); - } - } - - public object? GetValueFor(T item) - { - if (this.ValueProvider == null) - this.BuildValueProvider(); - var valueProvider = this.ValueProvider!.Compile(); - object? value = null; - try - { - value = valueProvider.Invoke(item); - } - catch { } - if (value == null) - return null; - if (string.IsNullOrWhiteSpace(this.Format) - || value is not IFormattable formattable) - return value; - return formattable.ToString(this.Format, this.FormatProvider); - } - - public void ToggleSortMode() - { - if (!this.IsSorteable) - return; - int sortModeValue = (int)this.SortMode; - sortModeValue++; - if (sortModeValue > 2) - sortModeValue = 0; - this.SortMode = (SortMode)sortModeValue; - } - - public static Column CreateFor(PropertyInfo property) - { - var name = property.Name; - var description = string.Empty; - var displayOrder = null as int?; - if (property.TryGetCustomAttribute(out var displayAttribute)) - { - name = displayAttribute.Name; - description = displayAttribute.Description; - displayOrder = displayAttribute.Order; - } - return new() - { - Name = name, - Description = description, - DisplayOrder = displayOrder, - PropertyPath = PropertyPath.Parse(property.Name), - AutoGenerated = true, - }; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeader.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeader.razor deleted file mode 100644 index 11fb8175d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeader.razor +++ /dev/null @@ -1,27 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@typeparam T - -@RenderContext.Column.Name - -@code { - - [Parameter] - public ColumnHeaderRenderingContext RenderContext { get; set; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeaderCollectionPresenter.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeaderCollectionPresenter.razor deleted file mode 100644 index cd70764d2..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeaderCollectionPresenter.razor +++ /dev/null @@ -1,41 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@typeparam T - -@foreach(var column in Table.columns.Where(c => c.IsVisible)) -{ - - @if(column.HeaderTemplate == null) - @Table.DefaultHeaderTemplate(new(Table, column)) - else - @column.HeaderTemplate(new(Table, column)) - -} - -@code { - - [CascadingParameter] - public Table Table { get; set; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeaderRenderingContext.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeaderRenderingContext.cs deleted file mode 100644 index 3f5acdd31..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ColumnHeaderRenderingContext.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - public class ColumnHeaderRenderingContext - { - - public ColumnHeaderRenderingContext(Table table, Column column) - { - this.Table = table; - this.Column = column; - } - - public Table Table { get; } - - public Column Column { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Row.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Row.razor deleted file mode 100644 index d5b203e0f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Row.razor +++ /dev/null @@ -1,28 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@typeparam T - - - - -@code { - - [Parameter] - public RowRenderingContext RenderContext { get; set; } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/RowRenderingContext.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/RowRenderingContext.cs deleted file mode 100644 index 8f753dc4e..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/RowRenderingContext.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - public class RowRenderingContext - { - - public RowRenderingContext(Table table, T item, int index) - { - this.Table = table; - this.Item = item; - this.Index = index; - } - - public Table Table { get; } - - public T Item { get; } - - public int Index { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/SortMode.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/SortMode.cs deleted file mode 100644 index 40520266f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/SortMode.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - ///

- /// Enumerates all supported sort modes - /// - public enum SortMode - { - /// - /// Indicates no sorting - /// - None, - /// - /// Indicates an ascending sorting - /// - Ascending, - /// - /// Indicates a descending sorting - /// - Descending - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Table.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Table.razor deleted file mode 100644 index 992783d94..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/Table.razor +++ /dev/null @@ -1,167 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using Neuroglia.Data.Flux; -@using System.Reactive.Linq; -@namespace Synapse.Dashboard -@typeparam T -@inject IStore Store; -@inject IDispatcher Dispatcher; - - - @Columns@*needed to initialize columns declared in markup*@ - - - - - - - - @if(items != null) - { - var index = 0; - @foreach (var item in items) - { - @RowTemplate(new(this, item, index)) - index++; - } - } - -
-
- -@code -{ - - internal List> columns = new(); - private IEnumerable? items; - private Column? draggedColumn; - - [Parameter] public string? TableClass { get; set; } = "table table-stripped table-hover"; - - [Parameter] public string? HeaderRowClass { get; set; } - - [Parameter] public string? HeaderClass { get; set; } - - [Parameter] public string? RowClass { get; set; } - - [Parameter] public string? CellClass { get; set; } - - [Parameter] public RenderFragment Columns { get; set; } = null!; - - [Parameter] public bool AutoGenerateColumns { get; set; } = true; - - [Parameter] public RenderFragment> DefaultHeaderTemplate { get; set; } = context => __builder => - { - __builder.OpenComponent(0, typeof(ColumnHeader)); - __builder.AddAttribute(0, nameof(ColumnHeader.RenderContext), context); - __builder.CloseComponent(); - }; - - [Parameter] public RenderFragment> RowTemplate { get; set; } = context => __builder => - { - __builder.OpenComponent(0, typeof(Row)); - __builder.AddAttribute(0, nameof(Row.RenderContext), context); - __builder.CloseComponent(); - }; - - [Parameter] public RenderFragment> DefaultCellTemplate { get; set; } = context => __builder => - { - __builder.OpenComponent(0, typeof(Cell)); - __builder.AddAttribute(0, nameof(Cell.RenderContext), context); - __builder.CloseComponent(); - }; - - [Parameter] public IEnumerable? Items { get; set; } - - [Parameter] public EventCallback> OnOrderBy { get; set; } - - public void AddColumn(Column column) - { - this.columns.Add(column); - this.StateHasChanged(); - } - - public bool RemoveColumn(Column column) - { - var removed = this.columns.Remove(column); - this.StateHasChanged(); - return removed; - } - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - if (this.AutoGenerateColumns) - { - foreach(var property in typeof(T) - .GetProperties() - .Where(p => p.PropertyType.IsPrimitiveType() || p.PropertyType == typeof(string) || p.PropertyType == typeof(DateTimeOffset))) - { - this.AddColumn(Column.CreateFor(property)); - } - } - } - - protected override async Task OnParametersSetAsync() - { - if(this.Items is IAsyncEnumerable asyncEnumerable) - this.items = await asyncEnumerable.ToListAsync(); - else - this.items = this.Items; - } - - internal async Task OnClickColumn(Column column) - { - if (!column.IsSorteable - || column.PropertyPath == null) - return; - column.ToggleSortMode(); - if (this.OnOrderBy.HasDelegate) { - await this.OnOrderBy.InvokeAsync(column); - } - } - - internal void OnStartDragColumn(Column column) - { - this.draggedColumn = column; - } - - internal void OnEndDragColumn(Column column) - { - this.draggedColumn = null; - } - - internal void OnDropColumnOn(Column column) - { - if (column == null - || this.draggedColumn == null) - return; - int draggedIndex = this.columns.IndexOf(this.draggedColumn); - int droppedIndex = this.columns.IndexOf(column); - this.columns.Remove(this.draggedColumn); - int insertAt; - if (draggedIndex < droppedIndex) - insertAt = droppedIndex++; - else - insertAt = droppedIndex; - if (droppedIndex >= this.columns.Count) - droppedIndex = this.columns.Count - 1; - this.columns.Insert(insertAt, this.draggedColumn); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ViewDetailsColumn.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ViewDetailsColumn.razor deleted file mode 100644 index c4e1843b3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Table/ViewDetailsColumn.razor +++ /dev/null @@ -1,31 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@typeparam T - - - - - - - - -@code { - - [Parameter] public EventCallback OnViewDetails { get; set; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/IToastBuilder.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/IToastBuilder.cs deleted file mode 100644 index f94462a5b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/IToastBuilder.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - public interface IToastBuilder - { - - IToastBuilder WithLevel(LogLevel level); - - IToastBuilder WithHeader(string header); - - IToastBuilder WithoutHeader(); - - IToastBuilder WithBody(string body); - - IToastBuilder WithLifetime(TimeSpan duration); - - IToastBuilder OnHide(Action callback); - - Toast Build(); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/IToastManager.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/IToastManager.cs deleted file mode 100644 index 64662e4d1..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/IToastManager.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - ///

- /// Defines the fundamentals of a service used to manage toasts - /// - public interface IToastManager - : IDisposable - { - - /// - /// Represents the event fired whenever a toast has been shown - /// - public event Action OnShowToast; - /// - /// Represents the event fired whenever a toast has been hidden - /// - public event Action OnHideToast; - - /// - /// Gets an containing - /// - IEnumerable Toasts { get; } - - void ShowToast(Action setup); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toast.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toast.cs deleted file mode 100644 index 268064179..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toast.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; - -namespace Synapse.Dashboard -{ - - public class Toast - : IDisposable - { - - public event Action OnHide; - - public Guid Id { get; } = Guid.NewGuid(); - - public DateTime CreatedAt { get; } = DateTime.Now; - - public LogLevel Level { get; set; } = LogLevel.Information; - - public bool HasHeader { get; set; } - - public MarkupString Header { get; set; } - - public MarkupString Body { get; set; } - - public TimeSpan Duration { get; set; } = TimeSpan.FromSeconds(30); - - public Action OnHideCallback { get; set; } - - protected System.Timers.Timer Timer { get; private set; } - - public void Show() - { - this.Timer = new(this.Duration.TotalMilliseconds) { AutoReset = false }; - this.Timer.Elapsed += (sender, e) => this.OnTimerElapsed(); - this.Timer.Start(); - } - - public void Hide() - { - this.OnTimerElapsed(); - } - - private void OnTimerElapsed() - { - this.Timer.Stop(); - this.Timer.Dispose(); - this.Timer = null; - this.OnHide?.Invoke(this); - } - - private bool _Disposed; - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - this.Timer?.Dispose(); - this._Disposed = true; - } - } - - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/ToastBuilder.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/ToastBuilder.cs deleted file mode 100644 index b83df7bc3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/ToastBuilder.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; - -namespace Synapse.Dashboard -{ - public class ToastBuilder - : IToastBuilder - { - - protected Toast Toast { get; } = new(); - - public virtual IToastBuilder WithLevel(LogLevel level) - { - this.Toast.Level = level; - return this; - } - - public virtual IToastBuilder WithLifetime(TimeSpan duration) - { - this.Toast.Duration = duration; - return this; - } - - public virtual IToastBuilder WithHeader(string header) - { - this.Toast.Header = (MarkupString)header; - return this; - } - - public virtual IToastBuilder WithoutHeader() - { - this.Toast.HasHeader = false; - return this; - } - - public virtual IToastBuilder WithBody(string body) - { - this.Toast.Body = (MarkupString)body; - return this; - } - - public virtual IToastBuilder OnHide(Action callback) - { - this.Toast.OnHideCallback = callback; - return this; - } - - public virtual Toast Build() - { - return this.Toast; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/ToastManager.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/ToastManager.cs deleted file mode 100644 index 3b10a6bfa..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/ToastManager.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Collections.Concurrent; - -namespace Synapse.Dashboard -{ - - public class ToastManager - : IToastManager - { - - /// - public event Action OnShowToast; - /// - public event Action OnHideToast; - - protected ConcurrentDictionary ToastMap { get; } = new(); - - /// - public IEnumerable Toasts => this.ToastMap.Values; - - /// - public virtual void ShowToast(Action setup) - { - ToastBuilder builder = new(); - setup?.Invoke(builder); - Toast toast = builder.Build(); - toast.OnHide += this.OnToastHidden; - this.ToastMap.TryAdd(toast.Id, toast); - toast.Show(); - this.OnShowToast?.Invoke(); - } - - /// - public virtual void OnToastHidden(Toast toast) - { - toast.OnHide -= this.OnToastHidden; - this.ToastMap.TryRemove(toast.Id, out _); - toast.Dispose(); - toast.OnHideCallback?.Invoke(); - this.OnHideToast?.Invoke(); - } - - private bool _Disposed; - /// - /// Disposes of the - /// - /// A boolean indicating whether or not the is being disposed of - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.ToastMap.Values.ToList().ForEach(t => t.Dispose()); - this.ToastMap.Clear(); - } - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor deleted file mode 100644 index 2bf21f99b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor +++ /dev/null @@ -1,83 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using Microsoft.Extensions.Logging -@namespace Synapse.Dashboard - -

- @foreach (var toast in ToastManager.Toasts.OrderByDescending(t => t.CreatedAt)) - { - var displaySettings = BuildDisplaySettingsFor(toast); - - } -
- -@code -{ - - [Inject] - protected IToastManager ToastManager { get; private set; } - - ToastDisplaySettings BuildDisplaySettingsFor(Toast toast) - { - ToastDisplaySettings settings = new(); - switch (toast.Level) - { - case LogLevel.Error: - settings.BackgroundCssClass = "danger"; - settings.IconCssClass = "bug"; - break; - case LogLevel.Warning: - settings.BackgroundCssClass = "warning"; - settings.IconCssClass = "warning"; - break; - case LogLevel.Information: - settings.BackgroundCssClass = "info"; - settings.IconCssClass = "info"; - break; - default: - throw new NotSupportedException($"The specified toast type '{toast.Level}' is not supported"); - } - return settings; - } - - protected override void OnInitialized() - { - base.OnInitialized(); - this.ToastManager.OnShowToast += () => this.StateHasChanged(); - this.ToastManager.OnHideToast += () => this.StateHasChanged(); - } - - class ToastDisplaySettings - { - - public string BackgroundCssClass { get; set; } - - public string IconCssClass { get; set; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.css deleted file mode 100644 index 5f282702b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.css +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.min.css deleted file mode 100644 index 5f282702b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.min.css +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.scss deleted file mode 100644 index 5f282702b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toast/Toaster.razor.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor deleted file mode 100644 index cb7a3ac63..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor +++ /dev/null @@ -1,25 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -

-
- @ChildContent -
-
-@code { - [Parameter] public RenderFragment ChildContent { get; set; } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.css deleted file mode 100644 index 0a731fd36..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.css +++ /dev/null @@ -1,3 +0,0 @@ -.card-body { - gap: 1rem; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.min.css deleted file mode 100644 index 5b1ebc098..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.card-body{gap:1rem;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.scss deleted file mode 100644 index 6268b2c78..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/Toolbar/Toolbar.razor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.card-body { - gap: 1rem; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ActionNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ActionNodeViewModel.cs deleted file mode 100644 index 3982e1b50..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ActionNodeViewModel.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - - /// - /// Represents a - /// - public class ActionNodeViewModel - : LabeledNodeViewModel, IActionNodeViewModel - { - - /// - /// Initializes a new - /// - /// The the represents - public ActionNodeViewModel(ActionDefinition action, string? label = null, string? cssClass = "action-node") - : base(label ?? action.Name, cssClass) - { - this.Action = action; - } - - /// - /// Gets the the represents - /// - public ActionDefinition Action { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ActivityBadges.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ActivityBadges.razor deleted file mode 100644 index 08f50e02b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ActivityBadges.razor +++ /dev/null @@ -1,124 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using System.Collections.ObjectModel - -@if (ActiveInstances > 0) -{ - - - -
- @ActiveInstances -
-
-
-} -@if (CompensatedInstances > 0) -{ - > - - -
- @CompensatedInstances -
-
-
-} -@if (FaultedInstances > 0) -{ - > - - -
- @FaultedInstances -
-
-
-} - -@code { - [CascadingParameter(Name = "ActiveInstances")] public int ActiveInstances { get; set; } = new(); - - [CascadingParameter(Name = "FaultedInstances")] public int FaultedInstances { get; set; } = new(); - - [CascadingParameter(Name = "CompensatedInstances")] public int CompensatedInstances { get; set; } = new(); - - [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; - - protected virtual string ActivityRadius { get; set; } = ""; - protected virtual string ActiveX { get; set; } = ""; - protected virtual string ActiveY { get; set; } = ""; - protected virtual string FaultedX { get; set; } = ""; - protected virtual string FaultedY { get; set; } = ""; - protected virtual string CompensatedX { get; set; } = ""; - protected virtual string CompensatedY { get; set; } = ""; - protected virtual string CountSize { get; set; } = ""; - protected virtual string CountXY { get; set; } = ""; - - protected override void OnInitialized() - { - base.OnInitialized(); - double radius = Constants.GraphBagdesRadius; - this.ActivityRadius = radius.ToInvariantString(); - this.CountSize = (radius * 2).ToInvariantString(); - this.CountXY = (0 - radius).ToInvariantString(); - } - - protected override void OnParametersSet() - { - double divider = this.Node.Shape == NodeShape.Circle || this.Node.Shape == NodeShape.Ellipse ? 1 : 2; - double offset = Constants.GraphBagdesRadius * 4; - double activeOffset = 0; - double compensatedOffset = 0; - double faultedOffset = 0; - if (this.ActiveInstances > 0) - { - compensatedOffset += 1; - faultedOffset += 1; - if (this.CompensatedInstances > 0) - { - faultedOffset += 1; - } - } - else if (this.CompensatedInstances > 0) - { - faultedOffset += 1; - } - this.ActiveX = (((this.Node.BBox?.Width ?? Neuroglia.Blazor.Dagre.Constants.ClusterWidth) - (offset * activeOffset))/ divider).ToInvariantString(); - this.CompensatedX = (((this.Node.BBox?.Width ?? Neuroglia.Blazor.Dagre.Constants.ClusterWidth) - (offset * compensatedOffset)) / divider).ToInvariantString(); - this.FaultedX = (((this.Node.BBox?.Width ?? Neuroglia.Blazor.Dagre.Constants.ClusterWidth) - (offset * faultedOffset)) / divider).ToInvariantString(); - this.ActiveY = ((this.Node.BBox?.Height ?? Neuroglia.Blazor.Dagre.Constants.ClusterHeight) / divider).ToInvariantString(); - this.CompensatedY = ((this.Node.BBox?.Height ?? Neuroglia.Blazor.Dagre.Constants.ClusterHeight) / divider).ToInvariantString(); - this.FaultedY = ((this.Node.BBox?.Height ?? Neuroglia.Blazor.Dagre.Constants.ClusterHeight) / divider).ToInvariantString(); - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/DataCaseNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/DataCaseNodeViewModel.cs deleted file mode 100644 index 62895e45b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/DataCaseNodeViewModel.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - /// - /// Represents a data case - /// - public class DataCaseNodeViewModel - : LabeledNodeViewModel - { - - /// - /// Initializes a new - /// - /// The name of the the represents - public DataCaseNodeViewModel(string dataCaseName) - : base(dataCaseName, "datacase-node") - { - this.DataCaseName = dataCaseName; - } - - /// - /// Gets the name of the the represents - /// - public string DataCaseName { get; } - - } - - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/DragAndDropNodeBehavior.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/DragAndDropNodeBehavior.cs deleted file mode 100644 index aa3a57a54..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/DragAndDropNodeBehavior.cs +++ /dev/null @@ -1,120 +0,0 @@ -using Microsoft.AspNetCore.Components; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.JSInterop; -using Neuroglia.Blazor.Dagre; -using Neuroglia.Blazor.Dagre.Models; - -namespace Synapse.Dashboard -{ - public class DragAndDropNodeBehavior - : GraphBehavior - { - protected double _previousX = 0; - protected double _previousY = 0; - protected double _movementX = 0; - protected double _movementY = 0; - protected readonly IJSRuntime _jSRuntime = null!; - protected IJSInProcessObjectReference? _nodeGhostModule = null; - protected IJSObjectReference? _nodeGhost = null; - protected INodeViewModel? _source = null; - protected INodeViewModel? _destination = null; - - - public DragAndDropNodeBehavior(IGraphViewModel graph, IJSRuntime jSRuntime) - : base(graph) - { - this._jSRuntime = jSRuntime; - this.Graph.MouseEnter += this.OnMouseEnterAsync; - this.Graph.MouseLeave += this.OnMouseLeaveAsync; - this.Graph.MouseMove += this.OnMouseMoveAsync; - this.Graph.MouseDown += this.OnMouseDownAsync; - this.Graph.MouseUp += this.OnMouseUpAsync; - } - - protected virtual async Task OnMouseEnterAsync(GraphEventArgs e) - { - if (e.GraphElement == null || e.GraphElement is not INodeViewModel || e.GraphElement is StartNodeViewModel || e.GraphElement is EndNodeViewModel || this._source == null || this._source == e.GraphElement) - return; - this._destination = e.GraphElement as INodeViewModel; - this._destination.CssClass = (this._destination.CssClass ?? "") + " drop-destination"; - } - - protected virtual async Task OnMouseLeaveAsync(GraphEventArgs e) - { - if (e.GraphElement == null || e.GraphElement is not INodeViewModel || e.GraphElement is StartNodeViewModel || e.GraphElement is EndNodeViewModel || this._destination != e.GraphElement) - return; - this._destination.CssClass = (this._destination.CssClass ?? "").Replace(" drop-destination", ""); - this._destination = null; - } - - protected virtual async Task OnMouseMoveAsync(GraphEventArgs e) - { - if (this._nodeGhost == null) - return; - this._movementX = e.BaseEvent.ClientX - this._previousX; - this._movementY = e.BaseEvent.ClientY - this._previousY; - await this.UpdatedPosition(); - } - - protected virtual async Task OnMouseDownAsync(GraphEventArgs e) - { - if (e.GraphElement == null || e.GraphElement is not INodeViewModel) - return; - if (e.GraphElement is StartNodeViewModel || e.GraphElement is EndNodeViewModel) - return; - if (this._nodeGhostModule == null) - { - this._nodeGhostModule = await _jSRuntime.InvokeAsync("import", "./js/node-ghost.js"); - } - if (this._nodeGhostModule == null) - { - throw new NullReferenceException("Unable to load JS module './js/node-ghost.js'"); - } - this._nodeGhost = await this._nodeGhostModule.InvokeAsync("createNodeGhost", e.Component); - if (this._nodeGhost == null) - { - throw new NullReferenceException("Unable to create new ghost node"); - } - this._source = e.GraphElement as INodeViewModel; - this._previousX = e.BaseEvent.ClientX; - this._previousY = e.BaseEvent.ClientY; - } - - protected virtual async Task OnMouseUpAsync(GraphEventArgs e) - { - if (this._nodeGhost == null) - return; - await this.UpdatedPosition(); - await this._nodeGhost.InvokeVoidAsync("dispose"); - this._nodeGhost = null; - this._source = null; - if (this._destination != null) - { - this._destination.CssClass = (this._destination.CssClass ?? "").Replace(" drop-destination", ""); - this._destination = null; - } - } - - protected virtual async Task UpdatedPosition() - { - if (this._nodeGhost == null) - return; - if (this._movementX == 0 && this._movementY == 0) - return; - await this._nodeGhost.InvokeVoidAsync("move", this._movementX / (double)this.Graph.Scale, this._movementY / (double)this.Graph.Scale); - this._previousX += this._movementX; - this._previousY += this._movementY; - this._movementX = 0; - this._movementY = 0; - } - - public override void Dispose() - { - this.Graph.MouseEnter -= this.OnMouseEnterAsync; - this.Graph.MouseLeave -= this.OnMouseLeaveAsync; - this.Graph.MouseMove -= this.OnMouseMoveAsync; - this.Graph.MouseDown -= this.OnMouseDownAsync; - this.Graph.MouseUp -= this.OnMouseUpAsync; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EndNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EndNodeViewModel.cs deleted file mode 100644 index b2b7051a5..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EndNodeViewModel.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Blazor.Dagre; - -namespace Synapse.Dashboard -{ - public class EndNodeViewModel - : WorkflowNodeViewModel - { - public EndNodeViewModel() - :base("", "end-node", NodeShape.Circle, Constants.GraphStartEndNodeRadius, Constants.GraphStartEndNodeRadius) - { - - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EventNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EventNodeTemplate.razor deleted file mode 100644 index 79427f885..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EventNodeTemplate.razor +++ /dev/null @@ -1,64 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits NodeTemplate - - - @if (Node.Shape == NodeShape.Circle) - { - - } - else if (Node.Shape == NodeShape.Ellipse) - { - - } - else - { - - } - - - - - - - @if (!string.IsNullOrWhiteSpace(Node.Label)) { - - -
@Node.Label
-
-
- } - @if (Graph.EnableProfiling) { - - } -
- -@code { - protected virtual string? HalfWidth => (this.Node.BBox!.Width / 2).ToInvariantString(); - protected virtual string? HalfHeight => (this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string? X => (0 - this.Node.BBox!.Width / 4).ToInvariantString(); - protected virtual string? Y => (0 - this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string? LabelY => (0 - this.Node.BBox!.Height / 8).ToInvariantString(); - - protected virtual EventNodeViewModel EventNode => (EventNodeViewModel)this.Node; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EventNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EventNodeViewModel.cs deleted file mode 100644 index 8b989a9c2..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/EventNodeViewModel.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - /// - /// Represents an reference - /// - public class EventNodeViewModel - : LabeledNodeViewModel - { - - /// - /// Initializes a new - /// - /// The kind of the the represents - /// The name of the the represents - public EventNodeViewModel(EventKind kind, string refName) - :base(refName, "event-node") - { - this.Kind = kind; - this.RefName = refName; - this.ComponentType = typeof(EventNodeTemplate); - } - - /// - /// Gets the kind of the the represents - /// - public EventKind Kind { get; } - - /// - /// Gets the name of the the represents - /// - public string RefName { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ForEachNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ForEachNodeTemplate.razor deleted file mode 100644 index 9c318272d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ForEachNodeTemplate.razor +++ /dev/null @@ -1,32 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits NodeTemplate - - - - - - - - -@code { - protected virtual string? HalfWidth => (this.Node.BBox!.Width / 2).ToInvariantString(); - protected virtual string? HalfHeight => (this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string? X => (0 - this.Node.BBox!.Width / 4).ToInvariantString(); - protected virtual string? Y => (0 - this.Node.BBox!.Height / 4).ToInvariantString(); -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ForEachNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ForEachNodeViewModel.cs deleted file mode 100644 index 22398bbf9..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ForEachNodeViewModel.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - /// - /// Represents a for each - /// - public class ForEachNodeViewModel - : WorkflowNodeViewModel - { - - /// - /// Initializes a new - /// - public ForEachNodeViewModel() - : base("", "foreach-node", null, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5) - { - this.ComponentType = typeof(ForEachNodeTemplate); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/FunctionRefNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/FunctionRefNodeViewModel.cs deleted file mode 100644 index 514728b2b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/FunctionRefNodeViewModel.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - /// - /// Represents a - /// - public class FunctionRefNodeViewModel - : ActionNodeViewModel - { - - /// - /// Initializes a new - /// - /// The the represents - /// The the represents - public FunctionRefNodeViewModel(ActionDefinition action, FunctionReference function) - : base(action, action.Name ?? function.RefName, "function-node") - { - this.Function = function; - } - - /// - /// Gets the the represents - /// - public FunctionReference Function { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeTemplate.razor deleted file mode 100644 index 2f1d63bf3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeTemplate.razor +++ /dev/null @@ -1,59 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits NodeTemplate - - - - - - @Symbol - - - - -@code { - protected virtual string? Width => this.Node.BBox!.Width.ToInvariantString(); - protected virtual string? Height => this.Node.BBox!.Height.ToInvariantString(); - protected virtual string? HalfWidth => (this.Node.BBox!.Width / 2).ToInvariantString(); - protected virtual string? HalfHeight => (this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string Symbol - { - get - { - if (this.Gateway.Type == GatewayNodeType.And) - { - return "+"; - } - else if (this.Gateway.Type == GatewayNodeType.Xor) - { - return "x"; - } - else if (this.Gateway.Type == GatewayNodeType.N) - { - return "n"; - } - return ""; - } - } - protected GatewayNodeViewModel Gateway => (GatewayNodeViewModel)this.Element; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeType.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeType.cs deleted file mode 100644 index 30bfbeb60..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeType.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - /// - /// Enumerates all supported types of s - /// - public enum GatewayNodeType - { - /// - /// Indicates a cumulative gateway - /// - And, - /// - /// Indicates an exclusive gateway - /// - Xor, - /// - /// Indicates a cumulative gateway for N instances - /// - N - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeViewModel.cs deleted file mode 100644 index 8c86de496..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/GatewayNodeViewModel.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - /// - /// Represents a logical gateway - /// - public class GatewayNodeViewModel - : WorkflowNodeViewModel - { - - /// - /// Initializes a new - /// - /// The gateway's type - public GatewayNodeViewModel(GatewayNodeType type) - : base("", "gateway-node", null, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5) - { - this.Type = type; - this.ComponentType = typeof(GatewayNodeTemplate); - } - - /// - /// Gets the gateway's ParallelCompletionType - /// - public GatewayNodeType Type { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/IActionNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/IActionNodeViewModel.cs deleted file mode 100644 index 5c879c503..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/IActionNodeViewModel.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - - /// - /// Represents a - /// - public interface IActionNodeViewModel - : IWorkflowNodeViewModel - { - /// - /// Gets the the represents - /// - ActionDefinition Action { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/IWorkflowNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/IWorkflowNodeViewModel.cs deleted file mode 100644 index eb14f81f0..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/IWorkflowNodeViewModel.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Models; - -namespace Synapse.Dashboard -{ - /// - /// Defines the fundamentals of a workflow node - /// - public interface IWorkflowNodeViewModel - { - - /// - /// Gets/Sets the number of active s for which the activity described by the node is active - /// - int ActiveInstancesCount { get; set; } - - /// - /// Gets/Sets the number of active faulted s for which the activity described by the node is active - /// - int FaultedInstancesCount { get; set; } - - /// - /// Gets/Sets the number of active compensated s for which the activity described by the node is active - /// - int CompensatedInstancesCount { get; set; } - - /// - /// Resets the active, faulted and compensated instances counts - /// - void ResetInstancesCount(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/InjectNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/InjectNodeTemplate.razor deleted file mode 100644 index e0656e398..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/InjectNodeTemplate.razor +++ /dev/null @@ -1,32 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits NodeTemplate - - - - - - - - -@code { - protected virtual string? HalfWidth => (this.Node.BBox!.Width / 2).ToInvariantString(); - protected virtual string? HalfHeight => (this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string? X => (0 - this.Node.BBox!.Width / 4).ToInvariantString(); - protected virtual string? Y => (0 - this.Node.BBox!.Height / 4).ToInvariantString(); -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/InjectNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/InjectNodeViewModel.cs deleted file mode 100644 index cee9ed156..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/InjectNodeViewModel.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - /// - /// Represents a inject state node - /// - public class InjectNodeViewModel - : WorkflowNodeViewModel - { - - /// - /// Initializes a new - /// - /// - public InjectNodeViewModel(string data) - : base("", "inject-node", null, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5) - { - this.Data = data; - this.ComponentType = typeof(InjectNodeTemplate); - } - - public string Data { get; set; } - - } - - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/JunctionNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/JunctionNodeViewModel.cs deleted file mode 100644 index 0f34a69b7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/JunctionNodeViewModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Blazor.Dagre; -using Neuroglia.Blazor.Dagre.Models; - -namespace Synapse.Dashboard -{ - public class JunctionNodeViewModel - : NodeViewModel - { - public JunctionNodeViewModel() - : base("", "junction-node", NodeShape.Circle, 2, 2) - { - - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/LabeledNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/LabeledNodeViewModel.cs deleted file mode 100644 index dc0c9acba..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/LabeledNodeViewModel.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - /// - /// Represents a containing a label - /// - public class LabeledNodeViewModel - : WorkflowNodeViewModel - { - - /// - /// Initializes a new - /// - public LabeledNodeViewModel( - string? label = "", - string? cssClass = null, - string? shape = null, - double? width = Neuroglia.Blazor.Dagre.Constants.NodeWidth * 2, - double? height = Neuroglia.Blazor.Dagre.Constants.NodeHeight, - double? radiusX = Neuroglia.Blazor.Dagre.Constants.NodeRadius, - double? radiusY = Neuroglia.Blazor.Dagre.Constants.NodeRadius, - double? x = 0, - double? y = 0, - Type? componentType = null, - Guid? parentId = null - ) - : base(label, cssClass, shape, width, height, radiusX, radiusY, x, y, componentType, parentId) - {} - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/NodeLabelTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/NodeLabelTemplate.razor deleted file mode 100644 index 62333eef1..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/NodeLabelTemplate.razor +++ /dev/null @@ -1,54 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if (!string.IsNullOrWhiteSpace(Node.Label)) { - - -
@Node.Label
-
-
-} - -@code { - [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; - - protected virtual string LabelX { get; set; } = "0"; - protected virtual string LabelY { get; set; } = "0"; - protected virtual string LabelWidth { get; set; } = ""; - protected virtual string LabelHeight { get; set; } = Neuroglia.Blazor.Dagre.Constants.LabelHeight.ToInvariantString(); - - protected override void OnParametersSet() - { - base.OnParametersSet(); - this.LabelX = (this.Node.BBox?.X ?? 0).ToInvariantString(); - this.LabelWidth = (this.Node.BBox?.Width ?? 0).ToInvariantString(); - if (this.Node is IClusterViewModel) - { - this.LabelY = ((this.Node.BBox?.Height ?? 0) / 2).ToInvariantString(); - } - else - { - this.LabelY = (0 - Neuroglia.Blazor.Dagre.Constants.LabelHeight / 2).ToInvariantString(); - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/NodeShapeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/NodeShapeTemplate.razor deleted file mode 100644 index 0ec482fb7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/NodeShapeTemplate.razor +++ /dev/null @@ -1,37 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - @if (Node.Shape == NodeShape.Circle) - { - - } - else if (Node.Shape == NodeShape.Ellipse) - { - - } - else - { - - } - - -@code { - [CascadingParameter(Name = "Node")] public INodeViewModel Node { get; set; } = null!; - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ParallelNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ParallelNodeViewModel.cs deleted file mode 100644 index 946dcf09b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ParallelNodeViewModel.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard -{ - - /// - /// Represents a parallel - /// - public class ParallelNodeViewModel - : WorkflowNodeViewModel - { - - /// - /// Initializes a new - /// - public ParallelNodeViewModel() - : base("", "parallel-node", null, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5, Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5) - { - this.ComponentType = typeof(ParellelNodeTemplate); - } - - /// - /// Gets the gateway's ParallelCompletionType - /// - public GatewayNodeType Type { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ParellelNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ParellelNodeTemplate.razor deleted file mode 100644 index c9803b200..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/ParellelNodeTemplate.razor +++ /dev/null @@ -1,36 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits NodeTemplate - - - - - - - - -@code { - protected virtual string? Width => this.Node.BBox!.Width.ToInvariantString(); - protected virtual string? Height => this.Node.BBox!.Height.ToInvariantString(); - protected virtual string? HalfWidth => (this.Node.BBox!.Width / 2).ToInvariantString(); - protected virtual string? HalfHeight => (this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string? NegHalfWidth => (0 - this.Node.BBox!.Width / 2).ToInvariantString(); - protected virtual string? NegHalfHeight => (0 - this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string? X => (0 - this.Node.BBox!.Width / 4).ToInvariantString(); - protected virtual string? Y => (0 - this.Node.BBox!.Height / 4).ToInvariantString(); -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SleepNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SleepNodeTemplate.razor deleted file mode 100644 index 2826c5570..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SleepNodeTemplate.razor +++ /dev/null @@ -1,48 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits NodeTemplate - - - - - - - - - @if (!string.IsNullOrWhiteSpace(Node.Label)) { - - -
@Node.Label
-
-
- } - @if (Graph.EnableProfiling) { - - } -
-@code { - protected virtual string? HalfWidth => (this.Node.BBox!.Width / 2).ToInvariantString(); - protected virtual string? HalfHeight => (this.Node.BBox!.Height / 2).ToInvariantString(); - protected virtual string? X => (0 - this.Node.BBox!.Width / 4).ToInvariantString(); - protected virtual string? Y => (0 - this.Node.BBox!.Height / 2).ToInvariantString(); -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SleepNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SleepNodeViewModel.cs deleted file mode 100644 index 8dbdaac0c..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SleepNodeViewModel.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -namespace Synapse.Dashboard -{ - /// - /// Represents a sleep state node - /// - public class SleepNodeViewModel - : WorkflowNodeViewModel - { - - /// - /// Initializes a new - /// - /// - public SleepNodeViewModel(TimeSpan delay) - : base(System.Xml.XmlConvert.ToString(delay), "sleep-node") - { - this.Delay = delay; - this.ComponentType = typeof(SleepNodeTemplate); - } - - public TimeSpan Delay { get; set; } - - } - - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StartNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StartNodeViewModel.cs deleted file mode 100644 index dcd07e58f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StartNodeViewModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Blazor.Dagre; - -namespace Synapse.Dashboard -{ - public class StartNodeViewModel - : WorkflowNodeViewModel - { - public bool HasSuccessor { get; set; } - - public StartNodeViewModel(bool hasSuccessor = false) - :base("", "start-node", NodeShape.Circle, Constants.GraphStartEndNodeRadius, Constants.GraphStartEndNodeRadius) - { - this.HasSuccessor = hasSuccessor; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StateNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StateNodeTemplate.razor deleted file mode 100644 index 76753a733..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StateNodeTemplate.razor +++ /dev/null @@ -1,39 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits ClusterTemplate - - - - - - - - - - - - - @if (Graph.EnableProfiling) { - - } - - - -@code { - protected virtual StateNodeViewModel StateNode => (StateNodeViewModel)this.Cluster; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StateNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StateNodeViewModel.cs deleted file mode 100644 index 0a699a847..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/StateNodeViewModel.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Blazor.Dagre.Models; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - - /// - /// Represents a - /// - public class StateNodeViewModel - : ClusterViewModel, IWorkflowNodeViewModel - { - - /// - /// Initializes a new - /// - /// The the represents - public StateNodeViewModel(StateDefinition state, bool isFirst = false) - : base(null, state.Name!, null, null, Neuroglia.Blazor.Dagre.Constants.ClusterWidth * 1.5, Neuroglia.Blazor.Dagre.Constants.ClusterWidth * 1.5) - { - this.State = state; - this.IsFirst = isFirst; - this.ComponentType = typeof(StateNodeTemplate); - if (this.State.UsedForCompensation) - { - this.CssClass = (this.CssClass ?? "") + " used-for-compensation"; - } - } - - /// - /// Gets the the represents - /// - [System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.Always)] - [Newtonsoft.Json.JsonProperty] - public StateDefinition State { get; } - - /// - /// Gets if the state is the first of the worlflow - /// - public bool IsFirst { get; } - - private int _activeInstances = 0; - /// - public int ActiveInstancesCount - { - get => this._activeInstances; - set - { - this._activeInstances = value; - this.OnChange(); - } - } - - private int _faultedInstances = 0; - /// - public int FaultedInstancesCount - { - get => this._faultedInstances; - set - { - this._faultedInstances = value; - this.OnChange(); - } - } - private int _compensatedInstances = 0; - /// - public int CompensatedInstancesCount - { - get => this._compensatedInstances; - set - { - this._compensatedInstances = value; - this.OnChange(); - } - } - - /// - public void ResetInstancesCount() - { - this._activeInstances = 0; - this._faultedInstances = 0; - this._compensatedInstances = 0; - this.OnChange(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SubflowRefNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SubflowRefNodeViewModel.cs deleted file mode 100644 index 2d4bbecd2..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/SubflowRefNodeViewModel.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - /// - /// Represents a - /// - public class SubflowRefNodeViewModel - : ActionNodeViewModel - { - - /// - /// Initializes a new - /// - /// The the represents - public SubflowRefNodeViewModel(ActionDefinition action, SubflowReference subflow) - : base(action, $"{subflow.WorkflowId}:{subflow.Version ?? "latest"}", "subflow-node") - { - this.Subflow = subflow; - } - - /// - /// Gets the the represents - /// - public SubflowReference Subflow { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowDiagram.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowDiagram.razor deleted file mode 100644 index de4c67622..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowDiagram.razor +++ /dev/null @@ -1,90 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inject WorkflowGraphBuilder WorkflowGraphBuilder - - -@if (Graph != null) { - -} - -@code { - - [Parameter] public WorkflowDefinition WorkflowDefinition { get; set; } = null!; - protected WorkflowDefinition workflowDefinition { get; set; } = null!; - - [Parameter] public WorkflowDiagramOrientation Orientation { get; set; } = WorkflowDiagramOrientation.LeftToRight; - - [Parameter] public EventCallback> OnMouseUp { get; set; } - - public IGraphViewModel? Graph { get; set; } - protected DagreGraph? dagre { get; set; } - protected IDagreGraphOptions? options { get; set; } = null; - protected bool isDirty = true; - // Maps a state name to its cluster(s) in the graph - protected Dictionary>? StatesMap = null; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.WorkflowDefinition != null && this.WorkflowDefinition != this.workflowDefinition) { - this.workflowDefinition = this.WorkflowDefinition; - this.options = new DagreGraphOptions() - { - Direction = this.Orientation == WorkflowDiagramOrientation.LeftToRight ? DagreGraphDirection.LeftToRight : DagreGraphDirection.TopToBottom - }; - var graph = await this.WorkflowGraphBuilder.BuildGraph(this.workflowDefinition); - this.Graph = graph; - this.StatesMap = this.Graph.AllClusters.Values.OfType() - .GroupBy(cluster => cluster.State.Name) - .ToDictionary(group => group.Key, group => group.AsEnumerable()) - ; - this.isDirty = true; - } - } - - protected override bool ShouldRender() - { - if (!this.isDirty) return false; - this.isDirty = false; - return true; - } - - public virtual async Task DisplayActivityStatusFor(IEnumerable instances, IEnumerable activities, bool highlightPath = false) - { - this.isDirty = false; - await Task.Yield(); - if (this.Graph != null) { - await this.Graph.DisplayActivityStatusFor(this.StatesMap, instances, activities, highlightPath); - } - this.isDirty = true; - this.StateHasChanged(); - } - - public virtual async Task DisplayActivityStatusFor(V1WorkflowInstance instance, IEnumerable activities, bool highlightPath = false) - { - var instances = new List(); - instances.Add(instance); - await this.DisplayActivityStatusFor(instances, activities, highlightPath); - } - - public virtual async Task RefreshAsync() - { - if (this.dagre != null) await this.dagre.RefreshAsync(); - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowDiagramOrientation.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowDiagramOrientation.cs deleted file mode 100644 index 3106d5f2d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowDiagramOrientation.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Synapse.Dashboard -{ - public enum WorkflowDiagramOrientation - { - LeftToRight, - TopToBottom - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowGraphBuilder.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowGraphBuilder.cs deleted file mode 100644 index da0bd1cf1..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowGraphBuilder.cs +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.JSInterop; -using Neuroglia.Blazor.Dagre; -using Neuroglia.Blazor.Dagre.Models; -using ServerlessWorkflow.Sdk; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - public class WorkflowGraphBuilder - //: IWorkflowGraphViewModelBuilder - { - - protected readonly IJSRuntime jSRuntime; - - public WorkflowGraphBuilder(IJSRuntime jSRuntime) - { - this.jSRuntime = jSRuntime; - } - - /// - public async Task BuildGraph(WorkflowDefinition definition) - { - var isEmpty = definition.States == null || !definition.States.Any(s => !s.UsedForCompensation); - var graph = new GraphViewModel(/*enableProfiling: true*/); - //graph.RegisterBehavior(new DragAndDropNodeBehavior(graph, this.jSRuntime)); - var startNode = this.BuildStartNode(!isEmpty); - var endNode = this.BuildEndNode(); - await graph.AddElementAsync(startNode); - if (!isEmpty) { - var startState = definition.GetStartState(); - await this.BuildStateNodes(definition, graph, startState, endNode, startNode); - } - else - { - await this.BuildEdgeBetween(graph, startNode, endNode, false); - } - await graph.AddElementAsync(endNode); - return graph; - } - - protected NodeViewModel BuildStartNode(bool hasSuccessor = false) - { - return new StartNodeViewModel(hasSuccessor); - } - - protected async Task BuildStateNodes(WorkflowDefinition definition, GraphViewModel graph, StateDefinition state, NodeViewModel endNode, NodeViewModel previousNode) - { - var stateNodeGroup = graph.AllClusters.Values.OfType().FirstOrDefault(cluster => cluster.State.Name == state.Name); - NodeViewModel? firstNode, lastNode = null; - if (stateNodeGroup != null) - { - return stateNodeGroup; - } - else - { - stateNodeGroup = new StateNodeViewModel(state, state == definition.GetStartState()); - await graph.AddElementAsync(stateNodeGroup); - switch (state) - { - case CallbackStateDefinition callbackState: - { - var actionNodes = await this.BuildActionNodes(graph, callbackState.Action!, state.UsedForCompensation); - lastNode = this.BuildConsumeEventNode(callbackState.Event!); - foreach (var actionNode in actionNodes) - { - await stateNodeGroup.AddChildAsync(actionNode); - } - await this.BuildEdgeBetween(graph, previousNode, actionNodes.First(), state.UsedForCompensation); - await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode, state.UsedForCompensation); - await stateNodeGroup.AddChildAsync(lastNode); - break; - } - case EventStateDefinition eventState: - { - firstNode = this.BuildParellelNode(); - lastNode = this.BuildParellelNode(); - await stateNodeGroup.AddChildAsync(firstNode); - await this.BuildEdgeBetween(graph, previousNode, firstNode, state.UsedForCompensation); - foreach (var trigger in eventState.Triggers) - { - var gatewayIn = this.BuildGatewayNode(eventState.Exclusive ? GatewayNodeType.Xor : GatewayNodeType.And); - var gatewayOut = this.BuildGatewayNode(eventState.Exclusive ? GatewayNodeType.Xor : GatewayNodeType.And); - await stateNodeGroup.AddChildAsync(gatewayIn); - await stateNodeGroup.AddChildAsync(gatewayOut); - await this.BuildEdgeBetween(graph, firstNode, gatewayIn, state.UsedForCompensation); - foreach (var eventName in trigger.Events) - { - var eventNode = this.BuildConsumeEventNode(eventName); - await stateNodeGroup.AddChildAsync(eventNode); - await this.BuildEdgeBetween(graph, gatewayIn, eventNode, state.UsedForCompensation); - await this.BuildEdgeBetween(graph, eventNode, gatewayOut, state.UsedForCompensation); - if (trigger.Actions == null || !trigger.Actions.Any()) - { - await this.BuildEdgeBetween(graph, gatewayOut, lastNode, state.UsedForCompensation); - } - } - foreach (var action in trigger.Actions) - { - var actionsNodes = await this.BuildActionNodes(graph, action, state.UsedForCompensation); - foreach (var actionNode in actionsNodes) - { - await stateNodeGroup.AddChildAsync(actionNode); - } - await this.BuildEdgeBetween(graph, gatewayOut, actionsNodes.First(), state.UsedForCompensation); - await this.BuildEdgeBetween(graph, actionsNodes.Last(), lastNode, state.UsedForCompensation); - } - } - await stateNodeGroup.AddChildAsync(lastNode); - break; - } - case ForEachStateDefinition foreachState: - { - switch(foreachState.Mode) - { - case ActionExecutionMode.Parallel: - firstNode = this.BuildForEachNode(foreachState); - lastNode = this.BuildForEachNode(foreachState); - await stateNodeGroup.AddChildAsync(firstNode); - await this.BuildEdgeBetween(graph, previousNode, firstNode, state.UsedForCompensation); - foreach (var action in foreachState.Actions) - { - var actionNodes = await this.BuildActionNodes(graph, action, state.UsedForCompensation); - foreach (var actionNode in actionNodes) - { - await stateNodeGroup.AddChildAsync(actionNode); - } - await this.BuildEdgeBetween(graph, firstNode, actionNodes.First(), state.UsedForCompensation); - await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode, state.UsedForCompensation); - } - await stateNodeGroup.AddChildAsync(lastNode); - break; - case ActionExecutionMode.Sequential: - lastNode = previousNode; - foreach (var action in foreachState.Actions) - { - var actionNodes = await this.BuildActionNodes(graph, action, state.UsedForCompensation); - foreach (var actionNode in actionNodes) - { - await stateNodeGroup.AddChildAsync(actionNode); - } - await this.BuildEdgeBetween(graph, lastNode, actionNodes.First(), state.UsedForCompensation); - lastNode = actionNodes.Last(); - } - break; - default: - throw new Exception($"The specified action execution mode '{foreachState.Mode}' is not supported for a 'foreach' state"); - } - break; - } - case InjectStateDefinition injectState: - { - lastNode = this.BuildInjectNode(injectState); - await stateNodeGroup.AddChildAsync(lastNode); - await this.BuildEdgeBetween(graph, previousNode, lastNode, state.UsedForCompensation); - break; - } - case OperationStateDefinition operationState: - { - switch (operationState.ActionMode) - { - case ActionExecutionMode.Parallel: - firstNode = this.BuildParellelNode(); - lastNode = this.BuildParellelNode(); - await stateNodeGroup.AddChildAsync(firstNode); - await this.BuildEdgeBetween(graph, previousNode, firstNode, state.UsedForCompensation); - foreach (var action in operationState.Actions) - { - var actionNodes = await this.BuildActionNodes(graph, action, state.UsedForCompensation); - foreach (var actionNode in actionNodes) - { - await stateNodeGroup.AddChildAsync(actionNode); - } - await this.BuildEdgeBetween(graph, firstNode, actionNodes.First(), state.UsedForCompensation); - await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode, state.UsedForCompensation); - } - await stateNodeGroup.AddChildAsync(lastNode); - break; - case ActionExecutionMode.Sequential: - lastNode = previousNode; - foreach (var action in operationState.Actions) - { - var actionNodes = await this.BuildActionNodes(graph, action, state.UsedForCompensation); - foreach (var actionNode in actionNodes) - { - await stateNodeGroup.AddChildAsync(actionNode); - } - await this.BuildEdgeBetween(graph, lastNode, actionNodes.First(), state.UsedForCompensation); - lastNode = actionNodes.Last(); - } - break; - default: - throw new Exception($"The specified action execution mode '{operationState.ActionMode}' is not supported for an 'operation' state"); - } - break; - } - case ParallelStateDefinition parallelState: - { - firstNode = parallelState.CompletionType == ParallelCompletionType.AllOf ? this.BuildParellelNode() : this.BuildGatewayNode(GatewayNodeType.N); - lastNode = parallelState.CompletionType == ParallelCompletionType.AllOf ? this.BuildParellelNode() : this.BuildGatewayNode(GatewayNodeType.N); - await stateNodeGroup.AddChildAsync(firstNode); - await this.BuildEdgeBetween(graph, previousNode, firstNode, state.UsedForCompensation); - foreach (var branch in parallelState.Branches) - { - foreach (var action in branch.Actions) - { - var actionNodes = await this.BuildActionNodes(graph, action, state.UsedForCompensation); - foreach (var actionNode in actionNodes) - { - await stateNodeGroup.AddChildAsync(actionNode); - } - await this.BuildEdgeBetween(graph, firstNode, actionNodes.First(), state.UsedForCompensation); - await this.BuildEdgeBetween(graph, actionNodes.Last(), lastNode, state.UsedForCompensation); - } - } - await stateNodeGroup.AddChildAsync(lastNode); - break; - } - case SleepStateDefinition sleepState: - { - lastNode = this.BuildSleepNode(sleepState); - await stateNodeGroup.AddChildAsync(lastNode); - await this.BuildEdgeBetween(graph, previousNode, lastNode, state.UsedForCompensation); - break; - } - case SwitchStateDefinition switchState: - { - firstNode = this.BuildGatewayNode(GatewayNodeType.Xor); - await stateNodeGroup.AddChildAsync(firstNode); - await this.BuildEdgeBetween(graph, previousNode, firstNode, state.UsedForCompensation); - switch (switchState.SwitchType) - { - case SwitchStateType.Data: - { - foreach (var condition in switchState.DataConditions!) - { - var caseNode = this.BuildDataConditionNode(condition.Name! ?? condition.Condition); // todo: should be a labeled edge, not a node? - await stateNodeGroup.AddChildAsync(caseNode); - await this.BuildEdgeBetween(graph, firstNode, caseNode, state.UsedForCompensation); - switch (condition.OutcomeType) - { - case SwitchCaseOutcomeType.End: - await this.BuildEdgeBetween(graph, caseNode, endNode, state.UsedForCompensation); - break; - case SwitchCaseOutcomeType.Transition: - var nextStateName = condition.Transition == null ? condition.TransitionToStateName : condition.Transition.NextState; - var nextState = definition.GetState(nextStateName!); - if (nextState == null) - throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}"); - var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, caseNode); - lastNode = nextStateNode.Children.Values.OfType().Last(); - break; - default: - throw new Exception($"The specified condition type '${condition.OutcomeType}' is not supported"); - } - } - var defaultCaseNode = this.BuildDataConditionNode("default"); - await stateNodeGroup.AddChildAsync(defaultCaseNode); - await this.BuildEdgeBetween(graph, firstNode, defaultCaseNode, state.UsedForCompensation); - if (switchState.DefaultCondition.IsEnd - || switchState.DefaultCondition.End != null) - { - lastNode = defaultCaseNode; - if (!state.IsEnd && state.End == null) - { - await this.BuildEdgeBetween(graph, lastNode, endNode, state.UsedForCompensation); - } - } - else if (!string.IsNullOrWhiteSpace(switchState.DefaultCondition.TransitionToStateName) - || switchState.DefaultCondition.Transition != null) - { - var nextStateName = switchState.DefaultCondition.Transition == null ? switchState.DefaultCondition.TransitionToStateName : switchState.DefaultCondition.Transition.NextState; - var nextState = definition.GetState(nextStateName!); - if (nextState == null) - throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}"); - var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, defaultCaseNode); - lastNode = nextStateNode.Children.Values.OfType().Last(); - if (string.IsNullOrWhiteSpace(state.TransitionToStateName) && state.Transition == null) - { - await this.BuildEdgeBetween(graph, defaultCaseNode, lastNode, state.UsedForCompensation); - } - } - } - break; - case SwitchStateType.Event: - { - foreach (var condition in switchState.EventConditions!) - { - Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(condition)); - var caseNode = this.BuildDataConditionNode(condition.Name ?? condition.Event); // todo: should be a labeled edge, not a node? - await stateNodeGroup.AddChildAsync(caseNode); - await this.BuildEdgeBetween(graph, firstNode, caseNode, state.UsedForCompensation); - switch (condition.OutcomeType) - { - case SwitchCaseOutcomeType.End: - await this.BuildEdgeBetween(graph, caseNode, endNode, state.UsedForCompensation); - break; - case SwitchCaseOutcomeType.Transition: - var nextStateName = condition.Transition == null ? condition.TransitionToStateName : condition.Transition.NextState; - var nextState = definition.GetState(nextStateName!); - if (nextState == null) - throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}"); - var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, caseNode); - lastNode = nextStateNode.Children.Values.OfType().Last(); - break; - default: - throw new Exception($"The specified condition type '${condition.OutcomeType}' is not supported"); - } - } - var defaultCaseNode = this.BuildDataConditionNode("default"); - await stateNodeGroup.AddChildAsync(defaultCaseNode); - await this.BuildEdgeBetween(graph, firstNode, defaultCaseNode, state.UsedForCompensation); - if (switchState.DefaultCondition.IsEnd - || switchState.DefaultCondition.End != null) - { - lastNode = defaultCaseNode; - if (!state.IsEnd && state.End == null) - { - await this.BuildEdgeBetween(graph, lastNode, endNode, state.UsedForCompensation); - } - } - else if (!string.IsNullOrWhiteSpace(switchState.DefaultCondition.TransitionToStateName) - || switchState.DefaultCondition.Transition != null) - { - var nextStateName = switchState.DefaultCondition.Transition == null ? switchState.DefaultCondition.TransitionToStateName : switchState.DefaultCondition.Transition.NextState; - var nextState = definition.GetState(nextStateName!); - if (nextState == null) - throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}"); - var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, defaultCaseNode); - lastNode = nextStateNode.Children.Values.OfType().Last(); - if (string.IsNullOrWhiteSpace(state.TransitionToStateName) && state.Transition == null) - { - await this.BuildEdgeBetween(graph, defaultCaseNode, lastNode, state.UsedForCompensation); - } - } - } - break; - default: - throw new Exception($"The specified switch state type '{switchState.Type}' is not supported"); - } - break; - } - default: - throw new Exception($"The specified state type '{state.Type}' is not supported"); - } - } - if (lastNode == null) - { - throw new Exception($"Unable to define a last node for state '{state.Name}'. Every switch case should provide a last node."); - } - if (state.IsEnd - || state.End != null) - { - //Console.WriteLine($"State '{state.Name}' ends"); - await this.BuildEdgeBetween(graph, lastNode, endNode, state.UsedForCompensation); - } - else if (!string.IsNullOrWhiteSpace(state.TransitionToStateName) - || state.Transition != null) - { - var nextStateName = state.Transition == null ? state.TransitionToStateName! : state.Transition!.NextState; - //Console.WriteLine($"State '{state.Name}' transitions to '{nextStateName}'"); - var nextState = definition.GetState(nextStateName); - if (nextState == null) - throw new Exception($"Failed to find a state with name '{nextStateName}' in definition '{definition.GetUniqueIdentifier()}'"); - var nextStateNode = await this.BuildStateNodes(definition, graph, nextState, endNode, lastNode); - await this.BuildEdgeBetween(graph, lastNode, nextStateNode.Children.Values.OfType().First(), state.UsedForCompensation); - } - if (!string.IsNullOrWhiteSpace(state.CompensatedBy)) - { - var compensationState = definition.GetState(state.CompensatedBy); - if (compensationState == null) - throw new Exception($"Failed to find a state with name '{state.CompensatedBy}' in definition '{definition.GetUniqueIdentifier()}'"); - var compensationStateNode = await this.BuildStateNodes(definition, graph, compensationState, endNode, lastNode); - await this.BuildEdgeBetween(graph, lastNode, compensationStateNode.Children.Values.OfType().First(), true); - } - //Console.WriteLine($"No transition for state '{state.Name}'"); - //Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(state)); - return stateNodeGroup; - } - - protected async Task> BuildActionNodes(GraphViewModel graph, ActionDefinition action, bool usedForCompensation) - { - switch (action.Type) - { - case ActionType.Function: - return new() { this.BuildFunctionNode(action, action.Function!) }; - case ActionType.Subflow: - return new() { this.BuildSubflowNode(action, action.Subflow!) }; - case ActionType.Trigger: - var triggerEventNode = this.BuildProduceEventNode(action.Event!.ProduceEvent); - var resultEventNode = this.BuildConsumeEventNode(action.Event!.ResultEvent); - await this.BuildEdgeBetween(graph, triggerEventNode, resultEventNode, usedForCompensation); - return new() { triggerEventNode, resultEventNode }; - default: - throw new NotSupportedException($"The specified action type '{action.Type}' is not supported"); - } - } - - protected FunctionRefNodeViewModel BuildFunctionNode(ActionDefinition action, FunctionReference function) - { - return new(action, function); - } - - protected SubflowRefNodeViewModel BuildSubflowNode(ActionDefinition action, SubflowReference subflowRef) - { - return new(action, subflowRef); - } - - protected EventNodeViewModel BuildProduceEventNode(string refName) - { - return new(EventKind.Produced, refName); - } - - protected EventNodeViewModel BuildConsumeEventNode(string refName) - { - return new(EventKind.Consumed, refName); - } - - protected GatewayNodeViewModel BuildGatewayNode(GatewayNodeType gatewayNodeType) - { - return new(gatewayNodeType); - } - - protected JunctionNodeViewModel BuildJunctionNode() - { - return new(); - } - - protected InjectNodeViewModel BuildInjectNode(InjectStateDefinition injectState) - { - return new(Newtonsoft.Json.JsonConvert.SerializeObject(injectState.Data, Newtonsoft.Json.Formatting.Indented)); - } - - protected ForEachNodeViewModel BuildForEachNode(ForEachStateDefinition forEachState) - { - return new(); - } - - protected ParallelNodeViewModel BuildParellelNode() - { - return new(); - } - - protected SleepNodeViewModel BuildSleepNode(SleepStateDefinition sleepState) - { - return new(sleepState.Duration); - } - - protected DataCaseNodeViewModel BuildDataConditionNode(string caseDefinitionName) - { - return new(caseDefinitionName); - } - - protected NodeViewModel BuildEndNode() - { - return new EndNodeViewModel(); - } - - - /// - /// Builds an edge between two nodes - /// - /// The graph instance hosting the edge - /// The edge's source node - /// The edge's target node - /// - protected async Task BuildEdgeBetween(GraphViewModel graph, NodeViewModel source, NodeViewModel target, bool compensationEdge) - { - await graph.AddElementAsync(new EdgeViewModel(source.Id, target.Id, null, compensationEdge ? "used-for-compensation" : "")); - } - - - /// - public void Dispose() - { - GC.SuppressFinalize(this); - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowNodeTemplate.razor b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowNodeTemplate.razor deleted file mode 100644 index 9afc80f8f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowNodeTemplate.razor +++ /dev/null @@ -1,40 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inherits NodeTemplate - - - - - - - - - - - - - @if (Graph.EnableProfiling) { - - } - - - - -@code { - protected virtual WorkflowNodeViewModel WorkflowNode => (WorkflowNodeViewModel)this.Node; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowNodeViewModel.cs b/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowNodeViewModel.cs deleted file mode 100644 index 16093a3de..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Shared/WorkflowDiagram/WorkflowNodeViewModel.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Blazor.Dagre.Models; -using Synapse.Dashboard.Services; - -namespace Synapse.Dashboard -{ - - /// - /// Represents the base class for all workflow-related s - /// - public abstract class WorkflowNodeViewModel - : NodeViewModel, IWorkflowNodeViewModel - { - - private int _activeInstances = 0; - /// - public int ActiveInstancesCount { - get => this._activeInstances; - set - { - this._activeInstances = value; - this.OnChange(); - } - } - - private int _faultedInstances = 0; - /// - public int FaultedInstancesCount - { - get => this._faultedInstances; - set - { - this._faultedInstances = value; - this.OnChange(); - } - } - private int _compensatedInstances = 0; - /// - public int CompensatedInstancesCount - { - get => this._compensatedInstances; - set - { - this._compensatedInstances = value; - this.OnChange(); - } - } - - public WorkflowNodeViewModel( - string? label = "", - string? cssClass = null, - string? shape = null, - double? width = Neuroglia.Blazor.Dagre.Constants.NodeWidth * 1.5, - double? height = Neuroglia.Blazor.Dagre.Constants.NodeHeight * 1.5, - double? radiusX = Neuroglia.Blazor.Dagre.Constants.NodeRadius, - double? radiusY = Neuroglia.Blazor.Dagre.Constants.NodeRadius, - double? x = 0, - double? y = 0, - Type? componentType = null, - Guid? parentId = null - ) - : base(label, cssClass, shape, width, height, radiusX, radiusY, x, y, componentType, parentId) - { - this.ComponentType = typeof(WorkflowNodeTemplate); - } - - /// - public void ResetInstancesCount() - { - this._activeInstances = 0; - this._faultedInstances = 0; - this._compensatedInstances = 0; - this.OnChange(); - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/StatefulComponent.cs b/src/dashboard/Synapse.Dashboard/Features/StatefulComponent.cs deleted file mode 100644 index 43b23afda..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/StatefulComponent.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using Neuroglia.Data.Flux; -using System.Reactive.Linq; - -namespace Synapse.Dashboard -{ - - public abstract class StatefulComponent - : ComponentBase, IDisposable - { - - private bool _Disposed; - private IDisposable? _Subscription; - - [Inject] - public IDispatcher Dispatcher { get; set; } = null!; - - [Inject] - public IStore Store { get; set; } = null!; - - public IFeature Feature { get; private set; } = null!; - - public TState State => this.Feature.State; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.Feature = this.Store.GetFeature(); - this._Subscription = this.Feature.DistinctUntilChanged().Subscribe(_ => this.StateHasChanged()); - } - - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this._Subscription?.Dispose(); - this._Subscription = null; - } - this._Disposed = true; - } - } - - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/V1ApplicationState.cs b/src/dashboard/Synapse.Dashboard/Features/V1ApplicationState.cs deleted file mode 100644 index 6ac71e6bc..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/V1ApplicationState.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Features -{ - - [Feature] - public class V1ApplicationState - { - - public V1ApplicationState() - { - - } - - public V1ApplicationState(V1ApplicationInfo info) - { - this.Info = info; - } - - public V1ApplicationInfo? Info { get; } - - } - - [Reducer] - public static class V1ApplicationInfoReducers - { - - public static V1ApplicationState OnSetV1ApplicationInfo(V1ApplicationState state, SetV1ApplicationInfo action) - { - return new(action.Info); - } - - } - - [Effect] - public static class V1ApplicationInfoEffects - { - - public static async Task OnGetApplicationInfo(GetV1ApplicationInfo action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var info = await api.GetApplicationInfoAsync(); - context.Dispatcher.Dispatch(new SetV1ApplicationInfo(info)); - } - - } - - public class GetV1ApplicationInfo - { - - - - } - - public class SetV1ApplicationInfo - { - - public SetV1ApplicationInfo(V1ApplicationInfo info) - { - this.Info = info; - } - - public V1ApplicationInfo Info { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/V1CorrelationCollectionState.cs b/src/dashboard/Synapse.Dashboard/Features/V1CorrelationCollectionState.cs deleted file mode 100644 index 45ad2b001..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/V1CorrelationCollectionState.cs +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard -{ - - [Feature] - public record V1CorrelationCollectionState - { - - public V1CorrelationCollectionState() - { - - } - - public V1CorrelationCollectionState(IEnumerable correlations) - { - this.Correlations = new(correlations); - } - - public List? Correlations { get; init; } - - } - - [Reducer] - public static class V1CorrelationCollectionReducers - { - - public static V1CorrelationCollectionState OnSetV1CorrelationCollection(V1CorrelationCollectionState state, SetV1CorrelationCollection action) - { - return new(action.Correlations); - } - - public static V1CorrelationCollectionState OnAddV1Correlation(V1CorrelationCollectionState state, AddV1Correlation action) - { - var correlations = state.Correlations; - if (correlations == null) correlations = new(); - correlations.Add(action.Correlation); - return state with { Correlations = correlations }; - } - - public static V1CorrelationCollectionState OnAddContextToV1Correlation(V1CorrelationCollectionState state, AddContextToV1Correlation action) - { - var correlations = state.Correlations == null ? new List() : new List(state.Correlations); - var correl = correlations.FirstOrDefault(c => c.Id == action.CorrelationId); - if (correl == null) - return state; - if (correl.Contexts == null) - correl.Contexts = new List(); - else - correl.Contexts = new List(correl.Contexts); - correl.Contexts.Add(action.Context); - return state with { Correlations = correlations }; - } - - public static V1CorrelationCollectionState OnRemoveContextFromV1Correlation(V1CorrelationCollectionState state, RemoveContextFromV1Correlation action) - { - var correl = state.Correlations?.FirstOrDefault(c => c.Id == action.CorrelationId); - if (correl == null) - return state; - if (correl.Contexts == null) - correl.Contexts = new List(); - var context = correl.Contexts.FirstOrDefault(c => c.Id == action.ContextId); - if (context != null) - correl.Contexts.Remove(context); - return state with { }; - } - - public static V1CorrelationCollectionState OnCorrelateV1Event(V1CorrelationCollectionState state, CorrelateV1Event action) - { - var correl = state.Correlations?.FirstOrDefault(c => c.Id == action.CorrelationId); - if (correl == null) - return state; - if (correl.Contexts == null) - correl.Contexts = new List(); - var context = correl.Contexts.FirstOrDefault(c => c.Id == action.ContextId); - if (context == null) - return state; - if (context.PendingEvents == null) - context.PendingEvents = new List(); - context.PendingEvents.Add(action.Event); - return state with { }; - } - - } - - [Effect] - public static class V1CorrelationCollectionEffects - { - - public static async Task OnListV1Correlations(ListV1Correlations action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var correlations = await api.GetCorrelationsAsync(); - context.Dispatcher.Dispatch(new SetV1CorrelationCollection(correlations)); - } - - public static async Task OnSearchV1Correlations(SearchV1Correlations action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var correlations = await api.GetCorrelationsAsync($"$search={action.Term}"); - context.Dispatcher.Dispatch(new SetV1CorrelationCollection(correlations)); - } - - public static async Task OnGetV1WorkflowById(GetV1CorrelationById action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var correlation = await api.GetCorrelationByIdAsync(action.CorrelationId); - context.Dispatcher.Dispatch(new AddV1Correlation(correlation)); - } - - } - - public class SetV1CorrelationCollection - { - - public SetV1CorrelationCollection(IEnumerable correlations) - { - this.Correlations = correlations; - } - - public IEnumerable Correlations { get; } - - } - - public class ListV1Correlations - { - - - - } - - public class SearchV1Correlations - { - - public SearchV1Correlations(string term) - { - this.Term = term; - } - - public string Term { get; } - - } - - public class GetV1CorrelationById - { - - public GetV1CorrelationById(string correlationId) - { - this.CorrelationId = correlationId; - } - - public string CorrelationId { get; } - - } - - public class AddV1Correlation - { - - public AddV1Correlation(V1Correlation correlation) - { - this.Correlation = correlation; - } - - public V1Correlation Correlation { get; } - - } - - public class AddContextToV1Correlation - { - - public AddContextToV1Correlation(string correlationId, V1CorrelationContext context) - { - this.CorrelationId = correlationId; - this.Context = context; - } - - public string CorrelationId { get; } - - public V1CorrelationContext Context { get; } - - } - - public class RemoveContextFromV1Correlation - { - - public RemoveContextFromV1Correlation(string correlationId, string contextId) - { - this.CorrelationId = correlationId; - this.ContextId = contextId; - } - - public string CorrelationId { get; } - - public string ContextId { get; } - - } - - public class CorrelateV1Event - { - - public CorrelateV1Event(string correlationId, string contextId, V1Event e) - { - this.CorrelationId = correlationId; - this.ContextId = contextId; - this.Event = e; - } - - public string CorrelationId { get; } - - public string ContextId { get; } - - public V1Event Event { get; } - - } - - public static class V1CorrelationCollectionSelectors - { - - public static IObservable?> SelectCurrentCorrelations(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Correlations) - .DistinctUntilChanged(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/V1WorkflowActivityCollectionState.cs b/src/dashboard/Synapse.Dashboard/Features/V1WorkflowActivityCollectionState.cs deleted file mode 100644 index 29771b9af..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/V1WorkflowActivityCollectionState.cs +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Collections.ObjectModel; - -namespace Synapse.Dashboard -{ - /* - [Feature] - public class V1WorkflowActivityCollectionState - : List - { - public V1WorkflowActivityCollectionState() - {} - - public V1WorkflowActivityCollectionState(IEnumerable activities) - : base(activities) - {} - } - */ - public class SetV1WorkflowActivityCollection - { - - public SetV1WorkflowActivityCollection(IEnumerable workflowActivities) - { - this.WorkflowActivities = workflowActivities; - } - - public IEnumerable WorkflowActivities { get; } - - } - - public class AddV1WorkflowActivity - { - - public AddV1WorkflowActivity(V1WorkflowActivity workflowActivity) - { - this.WorkflowActivity = workflowActivity; - } - - public V1WorkflowActivity WorkflowActivity { get; } - - } - - public class MarkV1WorkflowActivityAsStarted - { - - public MarkV1WorkflowActivityAsStarted(string id, DateTime startedAt) - { - this.Id = id; - this.StartedAt = startedAt; - } - - public string Id { get; } - - public DateTime StartedAt { get; } - - } - - public class MarkV1WorkflowActivityAsSuspended - { - - public MarkV1WorkflowActivityAsSuspended(string id, DateTime suspendedAt) - { - this.Id = id; - this.SuspendedAt = suspendedAt; - } - - public string Id { get; } - - public DateTime SuspendedAt { get; } - - } - - public class MarkV1WorkflowActivityAsFaulted - { - - public MarkV1WorkflowActivityAsFaulted(string id, DateTime faultedAt, Error error) - { - this.Id = id; - this.FaultedAt = faultedAt; - this.Error = error; - } - - public string Id { get; } - - public DateTime FaultedAt { get; } - - public Error Error { get; } - - } - - public class MarkV1WorkflowActivityAsCancelled - { - - public MarkV1WorkflowActivityAsCancelled(string id, DateTime cancelledAt) - { - this.Id = id; - this.CancelledAt = cancelledAt; - } - - public string Id { get; } - - public DateTime CancelledAt { get; } - - } - - public class MarkV1WorkflowActivityAsExecuted - { - - public MarkV1WorkflowActivityAsExecuted(string id, DateTime executedAt, V1WorkflowActivityStatus status, Error? error, Neuroglia.Serialization.Dynamic? output) - { - this.Id = id; - this.ExecutedAt = executedAt; - this.Status = status; - this.Error = error; - this.Output = output; - } - - public string Id { get; } - - public DateTime ExecutedAt { get; } - - public V1WorkflowActivityStatus Status { get; } - - public Error? Error { get; } - - public Neuroglia.Serialization.Dynamic? Output { get; } - - } - - public class MarkV1WorkflowActivityAsCompensating - { - - public MarkV1WorkflowActivityAsCompensating(string id, DateTime compensatingAt) - { - this.Id = id; - this.CompensatingAt = compensatingAt; - } - - public string Id { get; } - - public DateTime CompensatingAt { get; } - - } - - public class MarkV1WorkflowActivityAsCompensated - { - - public MarkV1WorkflowActivityAsCompensated(string id, DateTime compensatedAt) - { - this.Id = id; - this.CompensatedAt = compensatedAt; - } - - public string Id { get; } - - public DateTime CompensatedAt { get; } - - } - - public class RemoveV1WorkflowActivity - { - - public RemoveV1WorkflowActivity(string id) - { - this.Id = id; - } - - public string Id { get; } - - } - - public class ListV1WorkflowInstanceActivities - { - - public ListV1WorkflowInstanceActivities(string definitionId) - { - this.DefinitionId = definitionId; - } - - public string DefinitionId { get; } - - } - - /* - [Reducer] - public static class V1WorkflowActivityCollectionReducers - { - - public static V1WorkflowActivityCollectionState OnGetWorkflowsFromApiSucceeded(V1WorkflowActivityCollectionState state, SetV1WorkflowInstanceCollection action) - { - var activities = action.WorkflowInstances.SelectMany(instance => instance.Activities ?? new Collection()); - var activityIds = activities.Select(activity => activity.Id).ToList(); - activities = activities.Concat(state.Where(activity => !activityIds.Contains(activity.Id))); - return new(activities); - } - - public static V1WorkflowActivityCollectionState OnAddV1WorkflowInstance(V1WorkflowActivityCollectionState state, AddV1WorkflowInstance action) - { - var activities = (action.WorkflowInstance.Activities ?? new Collection()).AsEnumerable(); - var activityIds = activities.Select(activity => activity.Id).ToList(); - activities = activities.Concat(state.Where(activity => !activityIds.Contains(activity.Id))); - return new(activities); - } - - public static V1WorkflowActivityCollectionState OnAddV1WorkflowActivity(V1WorkflowActivityCollectionState state, AddV1WorkflowActivity action) - { - state.Add(action.WorkflowActivity); - return state; - } - - public static V1WorkflowActivityCollectionState OnMarkV1WorkflowActivityAsStarted(V1WorkflowActivityCollectionState state, MarkV1WorkflowActivityAsStarted action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - { - activity.LastModified = action.StartedAt; - activity.StartedAt = action.StartedAt; - activity.Status = V1WorkflowActivityStatus.Running; - } - return state; - } - - public static V1WorkflowActivityCollectionState OnMarkV1WorkflowActivityAsSuspended(V1WorkflowActivityCollectionState state, MarkV1WorkflowActivityAsSuspended action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - { - activity.LastModified = action.SuspendedAt; - activity.Status = V1WorkflowActivityStatus.Suspended; - } - return state; - } - - public static V1WorkflowActivityCollectionState OnMarkV1WorkflowActivityAsFaulted(V1WorkflowActivityCollectionState state, MarkV1WorkflowActivityAsFaulted action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - { - activity.LastModified = action.FaultedAt; - activity.Error = action.Error; - activity.Status = V1WorkflowActivityStatus.Faulted; - } - return state; - } - - public static V1WorkflowActivityCollectionState OnMarkV1WorkflowActivityAsCancelled(V1WorkflowActivityCollectionState state, MarkV1WorkflowActivityAsCancelled action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - { - activity.LastModified = action.CancelledAt; - activity.ExecutedAt = action.CancelledAt; - activity.Status = V1WorkflowActivityStatus.Cancelled; - } - return state; - } - - public static V1WorkflowActivityCollectionState OnMarkV1WorkflowActivityAsExecuted(V1WorkflowActivityCollectionState state, MarkV1WorkflowActivityAsExecuted action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - { - activity.LastModified = action.ExecutedAt; - activity.ExecutedAt = action.ExecutedAt; - activity.Error = action.Error; - activity.Output = action.Output; - activity.Status = action.Status; - } - return state; - } - - public static V1WorkflowActivityCollectionState OnMarkV1WorkflowActivityAsCompensating(V1WorkflowActivityCollectionState state, MarkV1WorkflowActivityAsCompensating action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - { - activity.LastModified = action.CompensatingAt; - } - return state; - } - - public static V1WorkflowActivityCollectionState OnMarkV1WorkflowActivityAsCompensated(V1WorkflowActivityCollectionState state, MarkV1WorkflowActivityAsCompensated action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - { - activity.LastModified = action.CompensatedAt; - activity.Status = V1WorkflowActivityStatus.Compensated; - } - return state; - } - - public static V1WorkflowActivityCollectionState OnRemoveV1WorkflowActivity(V1WorkflowActivityCollectionState state, RemoveV1WorkflowActivity action) - { - var activity = state.FirstOrDefault(a => a.Id == action.Id); - if (activity != null) - state.Remove(activity); - return state; - } - } - */ - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/V1WorkflowCollectionState.cs b/src/dashboard/Synapse.Dashboard/Features/V1WorkflowCollectionState.cs deleted file mode 100644 index 02750909b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/V1WorkflowCollectionState.cs +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard -{ - - [Feature] - public class V1WorkflowCollectionState - : List - { - - public V1WorkflowCollectionState() - { - - } - - public V1WorkflowCollectionState(IEnumerable workflows) - : base(workflows) - { - - } - - } - - [Reducer] - public static class V1WorkflowCollectionReducers - { - - public static V1WorkflowCollectionState OnGetWorkflowsFromApiSucceeded(V1WorkflowCollectionState state, SetV1WorkflowCollection action) - { - return new(action.Workflows); - } - - public static V1WorkflowCollectionState OnAddV1Workflow(V1WorkflowCollectionState state, AddV1Workflow action) - { - state.Add(action.Workflow); - return state; - } - - public static V1WorkflowCollectionState OnRemoveV1Workflow(V1WorkflowCollectionState state, RemoveV1Workflow action) - { - var workflow = state.FirstOrDefault(w => w.Id == action.WorkflowId); - if(workflow != null) - state.Remove(workflow); - return state; - } - - } - - [Effect] - public static class V1WorkflowCollectionEffects - { - - public static async Task OnListV1Workflows(ListV1Workflows action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var workflows = await api.GetWorkflowsAsync(); - context.Dispatcher.Dispatch(new SetV1WorkflowCollection(workflows)); - } - - public static async Task OnSearchV1Workflows(SearchV1Workflows action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var workflows = await api.GetWorkflowsAsync($"$search={action.Term}"); - context.Dispatcher.Dispatch(new SetV1WorkflowCollection(workflows)); - } - - public static async Task OnGetV1WorkflowById(GetV1WorkflowById action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var workflow = await api.GetWorkflowByIdAsync(action.WorkflowId); - context.Dispatcher.Dispatch(new AddV1Workflow(workflow)); - } - - } - - public class SetV1WorkflowCollection - { - - public SetV1WorkflowCollection(IEnumerable workflows) - { - this.Workflows = workflows; - } - - public IEnumerable Workflows { get; } - - } - - public class AddV1Workflow - { - - public AddV1Workflow(V1Workflow workflow) - { - this.Workflow = workflow; - } - - public V1Workflow Workflow { get; } - - } - - public class RemoveV1Workflow - { - - public RemoveV1Workflow(string workflowId) - { - this.WorkflowId = workflowId; - } - - public string WorkflowId { get; } - - } - - public class ListV1Workflows - { - - - - } - - public class SearchV1Workflows - { - - public SearchV1Workflows(string term) - { - this.Term = term; - } - - public string Term { get; } - - } - - public class GetV1WorkflowById - { - - public GetV1WorkflowById(string workflowId) - { - this.WorkflowId = workflowId; - } - - public string WorkflowId { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/V1WorkflowInstanceCollectionState.cs b/src/dashboard/Synapse.Dashboard/Features/V1WorkflowInstanceCollectionState.cs deleted file mode 100644 index 7fbddea87..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/V1WorkflowInstanceCollectionState.cs +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard -{ - /* - [Feature] - public class V1WorkflowInstanceCollectionState - : List - { - public V1WorkflowInstanceCollectionState() - {} - public V1WorkflowInstanceCollectionState(IEnumerable workflows) - : base(workflows) - {} - } - - [Reducer] - public static class V1WorkflowInstanceCollectionReducers - { - - public static V1WorkflowInstanceCollectionState OnGetWorkflowsFromApiSucceeded(V1WorkflowInstanceCollectionState state, SetV1WorkflowInstanceCollection action) - { - return new(action.WorkflowInstances); - } - - public static V1WorkflowInstanceCollectionState OnAddV1WorkflowInstance(V1WorkflowInstanceCollectionState state, AddV1WorkflowInstance action) - { - state.Add(action.WorkflowInstance); - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsStarting(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsStarting action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if(instance != null) - { - instance.LastModified = action.StartingAt; - instance.Status = V1WorkflowInstanceStatus.Starting; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsStarted(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsStarted action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.StartedAt; - instance.StartedAt = action.StartedAt; - instance.Status = V1WorkflowInstanceStatus.Running; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsSuspending(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsSuspending action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.SuspendingAt; - instance.Status = V1WorkflowInstanceStatus.Suspending; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsSuspended(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsSuspended action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.SuspendedAt; - instance.Status = V1WorkflowInstanceStatus.Suspended; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsResuming(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsResuming action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.ResumingAt; - instance.Status = V1WorkflowInstanceStatus.Resuming; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsCancelling(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsCancelling action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.CancellingAt; - instance.Status = V1WorkflowInstanceStatus.Cancelling; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsCancelled(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsCancelled action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.CancelledAt; - instance.Status = V1WorkflowInstanceStatus.Cancelled; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsFaulted(V1WorkflowInstanceCollectionState state, MarkV1WorkflowInstanceAsFaulted action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.FaultedAt; - instance.ExecutedAt = action.FaultedAt; - instance.Status = V1WorkflowInstanceStatus.Faulted; - instance.Error = action.Error; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnMarkV1WorkflowInstanceAsCompleted(V1WorkflowInstanceCollectionState state, MarkV1WorkflowAsCompleted action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - { - instance.LastModified = action.CompletedAt; - instance.ExecutedAt = action.CompletedAt; - instance.Status = V1WorkflowInstanceStatus.Completed; - instance.Output = action.Output; - } - return state; - } - - public static V1WorkflowInstanceCollectionState OnRemoveV1WorkflowInstance(V1WorkflowInstanceCollectionState state, RemoveV1WorkflowInstance action) - { - var instance = state.FirstOrDefault(i => i.Id == action.Id); - if (instance != null) - state.Remove(instance); - return state; - } - - } - */ - - [Effect] - public static class V1WorkflowInstanceCollectionEffects - { - - public static async Task OnListV1WorkflowInstancesByDefinition(ListV1WorkflowInstancesByDefinition action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var workflowInstances = await api.GetWorkflowInstancesAsync($"$filter={nameof(V1WorkflowInstance.WorkflowId)} eq '{action.DefinitionId}'"); - context.Dispatcher.Dispatch(new SetV1WorkflowInstanceCollection(workflowInstances)); - } - - public static async Task OnSuspendV1WorkflowInstance(SuspendV1WorkflowInstance action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - await api.SuspendWorkflowInstanceAsync(action.InstanceId); - } - - public static async Task OnResumeV1WorkflowInstance(ResumeV1WorkflowInstance action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - await api.ResumeWorkflowInstanceAsync(action.InstanceId); - } - - public static async Task OnCancelV1WorkflowInstance(CancelV1WorkflowInstance action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - await api.CancelWorkflowInstanceAsync(action.InstanceId); - } - - public static async Task OnDeleteV1WorkflowInstance(DeleteV1WorkflowInstance action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - await api.DeleteWorkflowInstanceAsync(action.InstanceId); - } - - } - public class SetV1WorkflowInstanceCollection - { - - public SetV1WorkflowInstanceCollection(IEnumerable workflowInstances) - { - this.WorkflowInstances = workflowInstances; - } - - public IEnumerable WorkflowInstances { get; } - - } - - public class AddV1WorkflowInstance - { - - public AddV1WorkflowInstance(V1WorkflowInstance workflowInstance) - { - this.WorkflowInstance = workflowInstance; - } - - public V1WorkflowInstance WorkflowInstance { get; } - - } - - public class MarkV1WorkflowInstanceAsStarting - { - - public MarkV1WorkflowInstanceAsStarting(string id, DateTime startingAt) - { - this.Id = id; - this.StartingAt = startingAt; - } - - public string Id { get; } - - public DateTime StartingAt { get; } - - } - - public class MarkV1WorkflowInstanceAsStarted - { - - public MarkV1WorkflowInstanceAsStarted(string id, DateTime startedAt) - { - this.Id = id; - this.StartedAt = startedAt; - } - - public string Id { get; } - - public DateTime StartedAt { get; } - - } - - public class MarkV1WorkflowInstanceAsSuspending - { - - public MarkV1WorkflowInstanceAsSuspending(string id, DateTime suspendingAt) - { - this.Id = id; - this.SuspendingAt = suspendingAt; - } - - public string Id { get; } - - public DateTime SuspendingAt { get; } - - } - - public class MarkV1WorkflowInstanceAsSuspended - { - - public MarkV1WorkflowInstanceAsSuspended(string id, DateTime suspendedAt) - { - this.Id = id; - this.SuspendedAt = suspendedAt; - } - - public string Id { get; } - - public DateTime SuspendedAt { get; } - - } - - public class MarkV1WorkflowInstanceAsResuming - { - - public MarkV1WorkflowInstanceAsResuming(string id, DateTime resumingAt) - { - this.Id = id; - this.ResumingAt = resumingAt; - } - - public string Id { get; } - - public DateTime ResumingAt { get; } - - } - - public class MarkV1WorkflowInstanceAsCancelling - { - - public MarkV1WorkflowInstanceAsCancelling(string id, DateTime cancellingAt) - { - this.Id = id; - this.CancellingAt = cancellingAt; - } - - public string Id { get; } - - public DateTime CancellingAt { get; } - - } - - public class MarkV1WorkflowInstanceAsCancelled - { - - public MarkV1WorkflowInstanceAsCancelled(string id, DateTime cancelledAt) - { - this.Id = id; - this.CancelledAt = cancelledAt; - } - - public string Id { get; } - - public DateTime CancelledAt { get; } - - } - - public class MarkV1WorkflowInstanceAsFaulted - { - - public MarkV1WorkflowInstanceAsFaulted(string id, DateTime faultedAt, Error error) - { - this.Id = id; - this.FaultedAt = faultedAt; - this.Error = error; - } - - public string Id { get; } - - public DateTime FaultedAt { get; } - - public Error Error { get; } - - } - - public class MarkV1WorkflowAsCompleted - { - - public MarkV1WorkflowAsCompleted(string id, DateTime completedAt, Neuroglia.Serialization.Dynamic output) - { - this.Id = id; - this.CompletedAt = completedAt; - this.Output = output; - } - - public string Id { get; } - - public DateTime CompletedAt { get; } - - public Neuroglia.Serialization.Dynamic Output { get; } - - } - - public class RemoveV1WorkflowInstance - { - - public RemoveV1WorkflowInstance(string id) - { - this.Id = id; - } - - public string Id { get; } - - } - - public class ListV1WorkflowInstancesByDefinition - { - - public ListV1WorkflowInstancesByDefinition(string definitionId) - { - this.DefinitionId = definitionId; - } - - public string DefinitionId { get; } - - } - - public class SuspendV1WorkflowInstance - { - - public SuspendV1WorkflowInstance(string instanceId) - { - this.InstanceId = instanceId; - } - - public string InstanceId { get; } - } - - public class ResumeV1WorkflowInstance - { - - public ResumeV1WorkflowInstance(string instanceId) - { - this.InstanceId = instanceId; - } - - public string InstanceId { get; } - } - - public class CancelV1WorkflowInstance - { - - public CancelV1WorkflowInstance(string instanceId) - { - this.InstanceId = instanceId; - } - - public string InstanceId { get; } - } - - public class DeleteV1WorkflowInstance - { - - public DeleteV1WorkflowInstance(string instanceId) - { - this.InstanceId = instanceId; - } - - public string InstanceId { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceActivitiesList/WorkflowInstanceActivitiesList.razor b/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceActivitiesList/WorkflowInstanceActivitiesList.razor deleted file mode 100644 index 0ca4f257f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceActivitiesList/WorkflowInstanceActivitiesList.razor +++ /dev/null @@ -1,71 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(Activities != null) -{ - - - - - - - - - - - - - - @foreach(var activity in Activities) - { - -
-
- - - - - - - - - - - } - -
TypeNameStatusStarted AtExecuted AtDuration
@activity.Type - @switch(activity.Type) - { - case(V1WorkflowActivityType.State): - @activity.Metadata[V1WorkflowActivityMetadata.State] - break; - case(V1WorkflowActivityType.Action): - @activity.Metadata[V1WorkflowActivityMetadata.Action] - break; - case(V1WorkflowActivityType.Branch): - @activity.Metadata[V1WorkflowActivityMetadata.Branch] - break; - } - @activity.Status@activity.StartedAt?.ToString("MM/dd/yyyy, HH:mm")@activity.ExecutedAt?.ToString("MM/dd/yyyy, HH:mm")@(activity.Duration?.ToString() ?? "")
-} - -@code { - [Parameter] public List Activities { get; set; } = new List(); - - protected Dictionary rows = new Dictionary(); -} diff --git a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceActivitiesList/WorkflowInstanceActivitiesRow.razor b/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceActivitiesList/WorkflowInstanceActivitiesRow.razor deleted file mode 100644 index fae564124..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceActivitiesList/WorkflowInstanceActivitiesRow.razor +++ /dev/null @@ -1,161 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Newtonsoft.Json -@using System.Text -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IYamlConverter YamlConverter - - -@if(Activity != null) -{ -

- @if (Activity.Type == V1WorkflowActivityType.State) - { -
- - -
- } - else { -
-

Input

- @if(Activity.Input != null) - { - - - } -
-
-

Output

- @if(Activity.Output != null) - { - - - } - @if(Activity.Error != null) - { - - - } -
- } -
-} - - -@code { - [Parameter] public V1WorkflowActivity Activity { get; set; } = null!; - - protected MonacoDiffEditor diffEditor = null!; - protected MonacoEditor inputEditor = null!; - protected MonacoEditor outputEditor = null!; - protected MonacoEditor errorEditor = null!; - - protected virtual async Task ToggleLanguage(string language) - { - if (Activity.Type == V1WorkflowActivityType.State) - { - await this.OnActivityDiffEditorDidInit(); - } - else { - if(Activity.Input != null) - { - await this.InitEditor(this.inputEditor as MonacoEditorBase, Activity.Input); - } - if(Activity.Output != null) - { - await this.InitEditor(this.outputEditor as MonacoEditorBase, Activity.Output); - } - if(Activity.Error != null) - { - await this.InitEditor(this.errorEditor as MonacoEditorBase, Activity.Error); - } - } - this.StateHasChanged(); - } - - protected async Task OnActivityDiffEditorDidInit() - { - var original = JsonConvert.SerializeObject(this.Activity.Input, Formatting.Indented); - var modified = JsonConvert.SerializeObject(this.Activity.Output != null ? this.Activity.Output : this.Activity.Error, Formatting.Indented); - TextModel inputModel = await MonacoEditorBase.CreateModel("", this.MonacoEditorHelper.PreferedLanguage); - TextModel outputModel = await MonacoEditorBase.CreateModel("", this.MonacoEditorHelper.PreferedLanguage); - if (!string.IsNullOrWhiteSpace(original)) - { - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - original = await this.YamlConverter.JsonToYaml(original); - } - inputModel = await MonacoEditorBase.CreateModel(original, this.MonacoEditorHelper.PreferedLanguage); - } - if (!string.IsNullOrWhiteSpace(modified)) - { - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - modified = await this.YamlConverter.JsonToYaml(modified); - } - outputModel = await MonacoEditorBase.CreateModel(modified, this.MonacoEditorHelper.PreferedLanguage); - } - await this.diffEditor.SetModel(new DiffEditorModel - { - Original = inputModel, - Modified = outputModel - }); - } - - protected Func OnActivityStandaloneEditorDidInit(object data) - { - return async (MonacoEditorBase editor) => - { - await this.InitEditor(editor, data); - }; - } - - protected async Task InitEditor(MonacoEditorBase editor, object data) - { - var model = await (editor as MonacoEditor).GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - var text = JsonConvert.SerializeObject(data, Formatting.Indented); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - text = await this.YamlConverter.JsonToYaml(text); - } - await (editor as MonacoEditor)!.SetValue(text); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceDetails/WorkflowInstanceDetails.razor b/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceDetails/WorkflowInstanceDetails.razor deleted file mode 100644 index 56b076c69..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceDetails/WorkflowInstanceDetails.razor +++ /dev/null @@ -1,94 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Newtonsoft.Json - -@if (Workflow != null && WorkflowInstance != null) -{ -

-
-
-
- @Workflow.Definition.Id -
-
- @Workflow.Definition.Version -
-
-
-
- @WorkflowInstance.Key -
-
-
- -
@WorkflowInstance.ActivationType
-
-
- -
@WorkflowInstance.Status
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-} - -@code { - [Parameter] - public V1Workflow Workflow { get; set; } = null!; - [Parameter] - public V1WorkflowInstance WorkflowInstance { get; set; } = null!; - - protected string GetStatusClass() - { - switch (this.WorkflowInstance.Status) - { - case V1WorkflowInstanceStatus.Pending: - return "bg-secondary"; - case V1WorkflowInstanceStatus.Starting: - case V1WorkflowInstanceStatus.Resuming: - case V1WorkflowInstanceStatus.Running: - return "bg-primary text-white"; - case V1WorkflowInstanceStatus.Suspending: - case V1WorkflowInstanceStatus.Suspended: - return "bg-warning text-white"; - case V1WorkflowInstanceStatus.Faulted: - case V1WorkflowInstanceStatus.Cancelling: - case V1WorkflowInstanceStatus.Cancelled: - return "bg-danger text-white"; - case V1WorkflowInstanceStatus.Completed: - return "bg-success text-white"; - default: - return string.Empty; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceIO/WorkflowInstanceIO.razor b/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceIO/WorkflowInstanceIO.razor deleted file mode 100644 index 75e2fd04d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceIO/WorkflowInstanceIO.razor +++ /dev/null @@ -1,87 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Newtonsoft.Json -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IYamlConverter YamlConverter - -@if (WorkflowInstance != null) -{ - if (Loading) - { - - } - - -} - -@code { - [Parameter] public V1WorkflowInstance WorkflowInstance { get; set; } = null!; - - protected MonacoDiffEditor diffEditor = null!; - protected bool Loading = true; - protected string CssClass => this.Loading ? "d-none" : "monaco-normal"; - - protected virtual async Task ToggleLanguage(string language) - { - await this.OnEditorDidInitAsync(); - this.StateHasChanged(); - } - - protected async Task OnEditorDidInitAsync() - { - var input = string.Empty; - var output = string.Empty; - TextModel inputModel = await MonacoEditorBase.CreateModel("", this.MonacoEditorHelper.PreferedLanguage); - TextModel outputModel = await MonacoEditorBase.CreateModel("", this.MonacoEditorHelper.PreferedLanguage); - if (this.WorkflowInstance.Input != null) { - input = JsonConvert.SerializeObject(this.WorkflowInstance.Input, Formatting.Indented); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - input = await this.YamlConverter.JsonToYaml(input); - } - inputModel = await MonacoEditorBase.CreateModel(input, this.MonacoEditorHelper.PreferedLanguage); - } - if (this.WorkflowInstance.Output != null) { - output = JsonConvert.SerializeObject(this.WorkflowInstance.Output, Formatting.Indented); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - output = await this.YamlConverter.JsonToYaml(output); - } - outputModel = await MonacoEditorBase.CreateModel(output, this.MonacoEditorHelper.PreferedLanguage); - } - if (this.WorkflowInstance.Error != null) { - output = JsonConvert.SerializeObject(this.WorkflowInstance.Error, Formatting.Indented); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - output = await this.YamlConverter.JsonToYaml(output); - } - outputModel = await MonacoEditorBase.CreateModel(output, this.MonacoEditorHelper.PreferedLanguage); - } - await this.diffEditor.SetModel(new DiffEditorModel - { - Original = inputModel, - Modified = outputModel - }); - this.Loading = false; - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceLogs/WorkflowInstanceLogs.razor b/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceLogs/WorkflowInstanceLogs.razor deleted file mode 100644 index 32fe22a03..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceLogs/WorkflowInstanceLogs.razor +++ /dev/null @@ -1,45 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Newtonsoft.Json -@inject ISynapseManagementApi SynapseManagementApi; - -

@logs
- -@code { - - private MarkupString logs; - - [Parameter] public V1WorkflowInstance WorkflowInstance { get; set; } = null!; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.WorkflowInstance == null) - return; - this.logs = new((await this.SynapseManagementApi.GetWorkflowInstanceLogsAsync(this.WorkflowInstance.Id)) - .Replace("\r\n", "
") - .Replace("\n", "
") - .Replace("trace:", @"trace:") - .Replace("debug:", @"debug:") - .Replace("info:", @"info:") - .Replace("warn:", @"warning:") - .Replace("error:", @"error:") - .Replace("crit:", @"critical:")); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceToolbar/WorkflowInstanceToolbar.razor b/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceToolbar/WorkflowInstanceToolbar.razor deleted file mode 100644 index 2304ac79b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/WorkflowInstances/WorkflowInstanceToolbar/WorkflowInstanceToolbar.razor +++ /dev/null @@ -1,49 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inject IDispatcher Dispatcher -@inject IBreadcrumbManager BreadcrumbService - -@if (WorkflowInstance != null) -{ - - - @if(WorkflowInstance.Status == V1WorkflowInstanceStatus.Running) - { - - } - @if(WorkflowInstance.Status == V1WorkflowInstanceStatus.Suspended) - { - - } - @if (WorkflowInstance.Status <= V1WorkflowInstanceStatus.Resuming) - { - - } - -} - -@code { - [Parameter] public V1WorkflowInstance WorkflowInstance { get; set; } = null!; - - protected async Task OnNavigateToWorkflow() - { - var workflowItem = this.BreadcrumbService.Items.Take(this.BreadcrumbService.Items.Count - 1).Last(); - await this.BreadcrumbService.NavigateTo(workflowItem); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowDetails/WorkflowDetails.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowDetails/WorkflowDetails.razor deleted file mode 100644 index 020de8c3d..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowDetails/WorkflowDetails.razor +++ /dev/null @@ -1,41 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@if (Workflow != null) -{ -

-
-
-
- @Workflow.Definition.Id -
-
- @Workflow.Definition.Version -
-
-
- - -
-
-
-} - -@code { - [Parameter] - public V1Workflow Workflow { get; set; } = null!; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionCollectionEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionCollectionEditor.razor deleted file mode 100644 index 1651f85c0..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionCollectionEditor.razor +++ /dev/null @@ -1,87 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if (actions != null) -{ - - - - - - - - - - @foreach (var action in actions) - { - -
-
- - - - - - - - } - -
NameType
@action.Name@action.Type - -
- -} -else -{ - -} - -@code { - - private List? actions { get; set; } - [Parameter] public List? Actions { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.actions != this.Actions) { - this.actions = this.Actions; - } - } - - protected virtual async Task OnCreateActionAsync() - { - if (this.actions == null) - return; - var action = new ActionDefinition() { Name = $"action-{this.actions.Count() + 1}", Function = new() { RefName = "undefined" } }; - this.actions.Add(action); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - - protected virtual async Task OnDeleteActionAsync(ActionDefinition action) - { - this.actions?.Remove(action); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionDataFilterEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionDataFilterEditor.razor deleted file mode 100644 index 73f75a173..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionDataFilterEditor.razor +++ /dev/null @@ -1,114 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - - - - - - - - - - - - - - - - - - -
From state data - -
Use results - -
Results - -
To state data - -
- -@code { - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private ActionDataFilterDefinition? dataFilter; - [Parameter] public ActionDataFilterDefinition? DataFilter { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.DataFilter == this.dataFilter) - return; - this.dataFilter = this.DataFilter; - } - - protected virtual async Task OnUseResultsChangedAsync(bool useResults) - { - if (this.dataFilter == null) - this.dataFilter = new(); - await this.OnChangeAsync(f => - { - f.UseResults = useResults; - if (!useResults) - { - f.Results = null; - f.ToStateData = null; - } - }); - } - - protected virtual async Task OnChangeAsync(Action patch) - { - if (this.dataFilter == null) - this.dataFilter = new(); - patch(this.dataFilter); - if (string.IsNullOrWhiteSpace(this.dataFilter.FromStateData) - && this.dataFilter.UseResults - && string.IsNullOrWhiteSpace(this.dataFilter.Results) - && string.IsNullOrWhiteSpace(this.dataFilter.ToStateData)) - this.dataFilter = null; - await this.OnChange.InvokeAsync(this.dataFilter); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionEditor.razor deleted file mode 100644 index 3475c62be..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ActionEditor.razor +++ /dev/null @@ -1,278 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if (action != null) -{ - - - - - -
-
- - - - - - - - -
-
- - - - - - - - @switch (action.Type) - { - case ActionType.Function: - -
-
- - - - - - break; - case ActionType.Subflow: - -
-
- - - - - - break; - case ActionType.Trigger: - -
-
- - - - - - break; - } - -
General
- - - - - - - - - - - - - - - - - - - - - - - -
Name
Type - -
Retry policy - -
Sleep - -
Condition - -
-
Data filter
- -
Function
- - - - - - - - - - - -
Function - -
Arguments - -
-
Subflow
- - - - - - - - - - - -
Workflow - -
Invocation mode - -
-
Trigger
- - - - - - - - - - - -
Produce event - -
Result event - -
-
-} - -@code { - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private ActionDefinition? action; - [Parameter] public ActionDefinition? Action { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.action != this.Action) { - this.action = this.Action; - } - } - - protected virtual async Task OnActionTypeChangedAsync(ActionType actionType) - { - if (this.action == null) - return; - switch (actionType) - { - case ActionType.Function: - this.action.Event = null; - this.action.Subflow = null; - this.action.Function = new(); - break; - case ActionType.Subflow: - this.action.Event = null; - this.action.Function = null; - this.action.Subflow = new(); - break; - case ActionType.Trigger: - this.action.Function = null; - this.action.Subflow = null; - this.action.Event = new(); - break; - } - await this.OnPropertyChangedAsync(nameof(action.Type), _ => { }); - } - - protected virtual async Task OnSubflowChangedAsync(V1WorkflowReference subflow) - { - await this.OnPropertyChangedAsync(nameof(action.Subflow), a => - { - if (subflow == null) - { - a.Subflow = null; - return; - } - if (a.Subflow == null) - a.Subflow = new(); - a.Subflow.WorkflowId = subflow.Id; - a.Subflow.Version = subflow.Version; - }); - } - - protected virtual async Task OnPropertyChangedAsync(string property, Action patchAction) - { - if (this.action == null) - return; - patchAction(this.action); - await this.OnChange.InvokeAsync(this.action); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/AuthenticationEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/AuthenticationEditor.razor deleted file mode 100644 index 6f9e708e1..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/AuthenticationEditor.razor +++ /dev/null @@ -1,192 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if (authentication != null) -{ - - - - - - - - - - - -
-
- - - - @if (!useSecretBasedProperties) - { - - } - - - -
Name
Scheme - -
Properties -
-
- - -
- @if (useSecretBasedProperties) - { - - } -
-
- @switch (authentication.Scheme) - { - case AuthenticationScheme.Basic: - if (authentication.Properties is not BasicAuthenticationProperties basic) - { - basic = new(); - authentication.Properties = basic; - } - - - - - - - - - - - -
Username
Password
- break; - case AuthenticationScheme.Bearer: - if (authentication.Properties is not BearerAuthenticationProperties bearer) - { - bearer = new(); - authentication.Properties = bearer; - } - - - - - - - -
Token
- break; - case AuthenticationScheme.OAuth2: - if (authentication.Properties is not OAuth2AuthenticationProperties oauth2) - { - oauth2 = new(); - authentication.Properties = oauth2; - } - break; - default: - The specified authentication scheme '@(EnumHelper.Stringify(authentication.Scheme))' is not supported - break; - } -
-} - -@code { - - private bool useSecretBasedProperties = false; - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private AuthenticationDefinition? authentication; - [Parameter] public AuthenticationDefinition? Authentication { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (Authentication == null) - return; - if (this.authentication == this.Authentication) - return; - this.authentication = this.Authentication; - this.useSecretBasedProperties = this.authentication.Properties is SecretBasedAuthenticationProperties; - } - - protected virtual async Task OnUseSecretBasedPropertiesChanged() - { - if (Authentication == null) - return; - this.useSecretBasedProperties = !useSecretBasedProperties; - if (!this.useSecretBasedProperties) - return; - this.Authentication.SecretRef = "undefined"; - await this.OnChange.InvokeAsync(this.Authentication); - this.StateHasChanged(); - } - - protected virtual async Task OnAuthenticationSchemeChangedAsync(AuthenticationScheme scheme) - { - if (this.authentication == null) - return; - this.useSecretBasedProperties = false; - await this.OnPropertyChangedAsync(nameof(Authentication.Scheme), a => - { - a.Scheme = scheme; - a.Properties = a.Scheme switch - { - AuthenticationScheme.Basic => new BasicAuthenticationProperties(), - AuthenticationScheme.Bearer => new BearerAuthenticationProperties(), - AuthenticationScheme.OAuth2 => new OAuth2AuthenticationProperties(), - _ => throw new NotSupportedException($"The specified {nameof(AuthenticationScheme)} '{a.Scheme}' is not supported") - }; - }); - } - - protected virtual async Task OnAuthenticationPropertiesChangedAsync(Action patch) - where TAuthenticationProperties : AuthenticationProperties - { - var authenticationProperties = (TAuthenticationProperties)this.authentication!.Properties; - patch(authenticationProperties); - await this.OnPropertyChangedAsync(nameof(Authentication.Properties), a => a.Properties = authenticationProperties); - } - - protected virtual async Task OnPropertyChangedAsync(string property, Action patchAction) - { - if (this.authentication == null) - return; - patchAction(this.authentication); - await this.OnChange.InvokeAsync(this.authentication); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/AuthenticationSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/AuthenticationSelector.razor deleted file mode 100644 index 15b5eb0c2..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/AuthenticationSelector.razor +++ /dev/null @@ -1,89 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - -@code { - - private string CreateNewOptionValue = "__wff:create"; - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - [Parameter] public string? CssClass{ get; set; } - - [Parameter] public bool AddCreateNewOption { get; set; } = true; - - [Parameter] public AuthenticationDefinition? Selected { get; set; } - - private List? authentications { get; set; } - [Parameter] public List? Authentications { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.authentications != this.Authentications) - this.authentications = this.Authentications; - } - - protected virtual async Task OnStateSelectorValueChangedAsync(ChangeEventArgs e) - { - if (e.Value == null) - return; - var authentication = Workflow.GetAuthentication((string)e.Value!); - if ((string)e.Value == CreateNewOptionValue) - { - if (this.Workflow.Auth == null) - this.Workflow.Auth = new(); - authentication = new AuthenticationDefinition() { Name = $"authentication-{this.Workflow.Auth.Count + 1}", Scheme = AuthenticationScheme.Basic, Properties = new BasicAuthenticationProperties() }; - this.Workflow.Auth.Add(authentication); - } - this.Selected = authentication; - await this.OnChange.InvokeAsync(authentication); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/BranchCollectionEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/BranchCollectionEditor.razor deleted file mode 100644 index 53e2ad557..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/BranchCollectionEditor.razor +++ /dev/null @@ -1,90 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if (branches != null) -{ - - - - - - - - - - - @foreach (var branch in branches) - { - -
-
- - - - - - - - - } - -
NameAction Execution ModeActions
@branch.Name@EnumHelper.Stringify(branch.ActionMode)@branch.Actions?.Count - -
- -} -else -{ - -} - -@code { - - private List? branches { get; set; } - [Parameter] public List? Branches { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.branches != this.Branches) { - this.branches = this.Branches; - } - } - - protected virtual async Task OnCreateBranchAsync() - { - if (this.branches == null) - return; - var branch = new BranchDefinition() { Name = $"branch-{this.branches.Count() + 1}" }; - this.branches.Add(branch); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - - protected virtual async Task OnDeleteBranchAsync(BranchDefinition branch) - { - this.branches?.Remove(branch); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/BranchEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/BranchEditor.razor deleted file mode 100644 index 0b10493b3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/BranchEditor.razor +++ /dev/null @@ -1,71 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(branch != null) -{ - - - - - - - -
-
- - - - - - -
Name - -
Actions
- -
-} - -@code { - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private BranchDefinition? branch; - [Parameter] public BranchDefinition? Branch { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.branch != this.Branch) - this.branch = this.Branch; - } - - protected virtual async Task OnChangeAsync(Action patch) - { - if (this.branch == null) - return; - patch(this.branch); - await this.OnChange.InvokeAsync(this.branch); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DataCaseEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DataCaseEditor.razor deleted file mode 100644 index 055ed139c..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DataCaseEditor.razor +++ /dev/null @@ -1,85 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(dataCase != null) -{ - - - - - - - - - - - - - - - -
Name - -
Condition - -
Outcome - -
-} - -@code { - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private SwitchStateDefinition? state; - [Parameter] public SwitchStateDefinition? State { get; set; } - - private DataCaseDefinition? dataCase; - [Parameter] public DataCaseDefinition? DataCase { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.dataCase != this.DataCase) - this.dataCase = this.DataCase; - if (this.state != this.State) - this.state = this.State; - } - - protected virtual async Task OnChangeAsync(Action? patch) - { - if (this.dataCase == null) - return; - if (patch != null) - patch(this.dataCase); - await this.OnChange.InvokeAsync(this.dataCase); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DefaultCaseEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DefaultCaseEditor.razor deleted file mode 100644 index 73da0287c..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DefaultCaseEditor.razor +++ /dev/null @@ -1,73 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(dataCase != null) -{ - - - - - - - - - - - -
Name - -
Outcome - -
-} - -@code { - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private SwitchStateDefinition? state; - [Parameter] public SwitchStateDefinition? State { get; set; } - - private DefaultCaseDefinition? dataCase; - [Parameter] public DefaultCaseDefinition? DataCase { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.dataCase != this.DataCase) - this.dataCase = this.DataCase; - if (this.state != this.State) - this.state = this.State; - } - - protected virtual async Task OnChangeAsync(Action? patch) - { - if (this.dataCase == null) - return; - if (patch != null) - patch(this.dataCase); - await this.OnChange.InvokeAsync(this.dataCase); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DynamicObjectEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DynamicObjectEditor.razor deleted file mode 100644 index 35fb1fafd..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/DynamicObjectEditor.razor +++ /dev/null @@ -1,111 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@using Neuroglia.Serialization -@using Newtonsoft.Json -@using Synapse.Dashboard.Services -@namespace Synapse.Dashboard -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IJsonSerializer JsonSerializer -@inject IYamlConverter YamlConverter - - - - -@code { - private DynamicObject? obj; - [Parameter] public DynamicObject? Object { get; set; } - [Parameter] public EventCallback OnChange { get; set; } - - private MonacoEditor? editor; - private string? rawObject = ""; - private bool updating = false; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.obj != this.Object) { - this.obj = this.Object; - this.rawObject = await this.FormatValueAsString(this.Object)! ?? ""; - if (this.editor != null) - { - await this.OnEditorInit(this.editor); - } - } - } - - protected virtual async Task ToggleLanguage(string language) - { - string text = await this.editor!.GetValue(); - try - { - if (language == PreferedLanguage.YAML) - { - this.rawObject = await this.YamlConverter.JsonToYaml(text); - } - else - { - this.rawObject = await this.YamlConverter.YamlToJson(text); - } - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - await this.MonacoEditorHelper.ChangePreferedLanguage(language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - } - await this.OnEditorInit(this.editor); - this.StateHasChanged(); - } - - protected async Task OnEditorInit(MonacoEditorBase editorBase) - { - this.updating = true; - var model = await this.editor!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - await this.editor!.SetValue(this.rawObject); - this.updating = false; - } - - protected async Task OnInputChangedAsync(ModelContentChangedEvent e) - { - if (!this.updating) { - var text = await this.editor!.GetValue(); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - text = await this.YamlConverter.YamlToJson(text); - try - { - this.obj = await this.JsonSerializer.DeserializeAsync(text); - await this.OnChange.InvokeAsync(this.obj); - } - catch (Exception ex) {} - } - } - - protected async Task? FormatValueAsString(DynamicObject? value) - { - if (value == null) - return ""; - var text = JsonConvert.SerializeObject(value, Formatting.Indented); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - text = await this.YamlConverter.JsonToYaml(text); - return text; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EndEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EndEditor.razor deleted file mode 100644 index bb1038cbc..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EndEditor.razor +++ /dev/null @@ -1,77 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - - - - - - - - - - -
Terminate - -
Compensate - -
- -@code { - - private EndDefinition? end; - [Parameter] public EndDefinition? End { get; set; } - - private ControlFlow controlFlow; - [Parameter] public ControlFlow ControlFlow{ get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - var updated = false; - if(this.end != this.End) - { - this.end = this.End; - updated = true; - } - if(this.controlFlow != this.ControlFlow) - { - this.controlFlow = this.ControlFlow; - updated = true; - } - if (!updated) - return; - } - - protected virtual async Task OnChangedAsync(Action patch) - { - if (this.end == null) - this.end = new(); - patch(this.end); - if (!this.end.Terminate && !this.end.Compensate) - this.end = null; - await this.OnChange.InvokeAsync(new(WorkflowOutcomeType.End, this.end)); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventCaseEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventCaseEditor.razor deleted file mode 100644 index 1b663aa17..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventCaseEditor.razor +++ /dev/null @@ -1,83 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(eventCase != null) -{ - - - - - - - - - - - - - - - -
Name - -
Event - - -
Outcome - -
-} - -@code { - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private SwitchStateDefinition? state; - [Parameter] public SwitchStateDefinition? State { get; set; } - - private EventCaseDefinition? eventCase; - [Parameter] public EventCaseDefinition? EventCase { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.eventCase != this.EventCase) - this.eventCase = this.EventCase; - if (this.state != this.State) - this.state = this.State; - } - - protected virtual async Task OnChangeAsync(Action? patch) - { - if (this.eventCase == null) - return; - if (patch != null) - patch(this.eventCase); - await this.OnChange.InvokeAsync(this.eventCase); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventCollectionSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventCollectionSelector.razor deleted file mode 100644 index 3b21d7c2b..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventCollectionSelector.razor +++ /dev/null @@ -1,69 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - -@code { - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - [Parameter] public string? CssClass{ get; set; } - - [Parameter] public bool AddCreateNewOption { get; set; } = true; - - [Parameter] public List? Selected { get; set; } - - private List? events { get; set; } - [Parameter] public List? Events { get; set; } - - [Parameter] public EventKind? EventKind { get; set; } - - [Parameter] public EventCallback> OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.events != this.Events) { - this.events = this.Events; - } - } - - protected virtual async Task OnStateSelectorValueChangedAsync(ChangeEventArgs e) - { - if (e.Value == null) - return; - var eventNames = ((string[])e.Value).ToList(); - if (eventNames == null) - eventNames = new(); - var events = new List(); - if (eventNames != null) - events.AddRange(eventNames.Select(n => Workflow.GetEvent(n)!).ToList()); - this.Selected = events; - await this.OnChange.InvokeAsync(events); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventDataFilterEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventDataFilterEditor.razor deleted file mode 100644 index 2c277b0a2..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventDataFilterEditor.razor +++ /dev/null @@ -1,98 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - - - - - - - - - - - - - - - -
Use data - -
Data - -
To state data - -
- -@code { - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private EventDataFilterDefinition? dataFilter; - [Parameter] public EventDataFilterDefinition? DataFilter { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.DataFilter == this.dataFilter) - return; - this.dataFilter = this.DataFilter; - } - - protected virtual async Task OnUseDataChangedAsync(bool useResults) - { - if (this.dataFilter == null) - this.dataFilter = new(); - await this.OnChangeAsync(f => - { - f.UseData = useResults; - if (!useResults) - { - f.Data = null; - f.ToStateData = null; - } - }); - } - - protected virtual async Task OnChangeAsync(Action patch) - { - if (this.dataFilter == null) - this.dataFilter = new(); - patch(this.dataFilter); - - await this.OnChange.InvokeAsync(this.dataFilter); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventEditor.razor deleted file mode 100644 index a430d9a84..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventEditor.razor +++ /dev/null @@ -1,126 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(evt != null) -{ - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Kind - -
Source
Type
Correlations
- - - - - - - - - @if(evt.Correlations != null) - { - foreach(var correl in evt.Correlations) - { - -
-
- - - - - - - } - } - -
Attribute nameAttribute value
@correl.ContextAttributeName@correl.ContextAttributeValue - - - - - - - - - - - -
Attribute name
Attribute value
-
- -
-} - -@code { - - private EventDefinition? evt { get; set; } - [Parameter] public EventDefinition? Event { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.Event != this.evt) - this.evt = this.Event; - } - - protected virtual async Task OnPropertyChanged(string property, Action patch) - { - if (this.evt == null) - return; - patch(this.evt); - await this.OnChange.InvokeAsync(this.evt); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventSelector.razor deleted file mode 100644 index c2b8f29fe..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventSelector.razor +++ /dev/null @@ -1,80 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - -@code { - - private string CreateNewOptionValue = "__wff:create"; - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - [Parameter] public string? CssClass{ get; set; } - - [Parameter] public bool AddCreateNewOption { get; set; } = true; - - [Parameter] public EventDefinition? Selected { get; set; } - - private List? events { get; set; } - [Parameter] public List? Events { get; set; } - - [Parameter] public EventKind? EventKind { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.events != this.Events) { - this.events = this.Events; - } - } - - protected virtual async Task OnStateSelectorValueChangedAsync(ChangeEventArgs e) - { - if (e.Value == null) - return; - var evt = Workflow.GetEvent((string)e.Value!); - if ((string)e.Value == CreateNewOptionValue) - { - if (this.Workflow.Events == null) - this.Workflow.Events = new(); - evt = new EventDefinition() { Name = $"event-{this.Workflow.Events.Count + 1}" }; - if (this.EventKind.HasValue) - evt.Kind = this.EventKind.Value; - this.Workflow.Events.Add(evt); - } - this.Selected = evt; - await this.OnChange.InvokeAsync(evt); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventStateTriggerEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventStateTriggerEditor.razor deleted file mode 100644 index 4c5151e58..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/EventStateTriggerEditor.razor +++ /dev/null @@ -1,98 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(trigger != null) -{ - - - - - - - - - - - -
-
- - - - - - -
-
- - - - - - -
Action execution mode - -
Events - -
Data filter
- -
Actions
- -
-} - -@code { - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private EventStateTriggerDefinition? trigger; - [Parameter] public EventStateTriggerDefinition? Trigger { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.trigger != this.Trigger) - this.trigger = this.Trigger; - } - - protected virtual async Task OnChangeAsync(Action patch) - { - if (this.trigger == null) - return; - patch(this.trigger); - await this.OnChange.InvokeAsync(this.trigger); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/FunctionEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/FunctionEditor.razor deleted file mode 100644 index 80be140ee..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/FunctionEditor.razor +++ /dev/null @@ -1,155 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Microsoft.OpenApi.Readers -@using Neuroglia.Data.Services -@inject HttpClient HttpClient -@inject ILogger Logger -@inject ISchemaRegistry SchemaRegistry - -@if(function != null) -{ - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Type - -
Operation -
- @{ - string[] operationComponents = null!; - Uri? resourceUri = null; - var operationId = string.Empty; - if (!string.IsNullOrWhiteSpace(function.Operation)) - { - operationComponents = function.Operation.Split('#', StringSplitOptions.RemoveEmptyEntries); - resourceUri = new(operationComponents[0]); - if(operationComponents.Length == 2) - operationId = operationComponents[1]; - } - } - - @switch (function.Type) - { - case FunctionType.OData: - - break; - case FunctionType.Rest: - - break; - case FunctionType.Rpc: - - break; - } -
-
Authentication - @if(Workflow != null) - { - - } - else - { - - } -
Metadata
- -
-} - -@code { - - [CascadingParameter] public WorkflowDefinition? Workflow { get; set; } - - private FunctionDefinition? function { get; set; } - [Parameter] public FunctionDefinition? Function { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.function != this.Function) { - this.function = this.Function; - } - } - - protected virtual async Task OnResourceUriChangedAsync(string resourceUri) - { - if (this.function == null) - return; - await this.OnPropertyChanged(nameof(function.Operation), f => f.Operation = resourceUri); - } - - protected virtual async Task OnOperationChangedAsync(Uri? resourceUri,string operationId) - { - await this.OnPropertyChanged(nameof(function.Operation), f => f.Operation = $"{resourceUri}#{operationId}"); - } - - protected virtual async Task OnPropertyChanged(string property, Action patch) - { - if (this.function == null) - return; - patch(this.function); - await this.OnChange.InvokeAsync(this.function); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/FunctionSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/FunctionSelector.razor deleted file mode 100644 index 11e6409f7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/FunctionSelector.razor +++ /dev/null @@ -1,76 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - -@code { - - private string CreateNewOptionValue = "__wff:create"; - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - [Parameter] public string? CssClass{ get; set; } - - [Parameter] public bool AddCreateNewOption { get; set; } = true; - - [Parameter] public FunctionDefinition? Selected { get; set; } - - private List? functions { get; set; } - [Parameter] public List? Functions { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.functions != this.Functions) { - this.functions = this.Functions; - } - } - - protected virtual async Task OnStateSelectorValueChangedAsync(ChangeEventArgs e) - { - if (e.Value == null) - return; - var function = Workflow.GetFunction((string)e.Value!); - if ((string)e.Value == CreateNewOptionValue) - { - if (this.Workflow.Functions == null) - this.Workflow.Functions = new(); - function = new FunctionDefinition() { Name = $"function-{this.Workflow.Functions.Count + 1}" }; - this.Workflow.Functions.Add(function); - } - this.Selected = function; - await this.OnChange.InvokeAsync(function); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/GrpcOperationSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/GrpcOperationSelector.razor deleted file mode 100644 index 07dab1ad5..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/GrpcOperationSelector.razor +++ /dev/null @@ -1,122 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Google.Protobuf.Reflection -@using Neuroglia.Data -@using Neuroglia.Data.Services -@inject ISchemaRegistry SchemaRegistry - - - - -@code { - - [Parameter] public Uri? DocumentUri { get; set; } - - [Parameter] public string? ServiceName { get; set; } - - [Parameter] public string? OperationId { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - private ISchemaDescriptor? schema; - private ServiceDescriptorProto? service; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.DocumentUri == null) - return; - this.schema = await this.SchemaRegistry.GetProtoSchemaAsync(this.DocumentUri); - } - - protected virtual void OnServiceChanged(string? serviceName) - { - if (schema == null) - return; - if(string.IsNullOrWhiteSpace(serviceName)) - { - this.service = null; - this.ServiceName = null; - return; - } - this.service = this.schema.Document.Services.Single(s => s.Name == serviceName); - this.ServiceName = this.service.Name; - this.OperationId = null; - this.StateHasChanged(); - } - - protected virtual async Task OnOperationChangedAsync(string? operationId) - { - if (schema == null || service == null) - return; - await this.OnChange.InvokeAsync($"{this.service.Name}#{operationId}"); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor deleted file mode 100644 index e06ef152e..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor +++ /dev/null @@ -1,199 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IYamlConverter YamlConverter -@inject IJSRuntime JsRuntime - -
- - -
- - Jq Expression tester - -
-
- -
-
-
-
-

Sample Input:

- - -
-
-

Output:

- - -
-
-
- - - -
- -
- -@code { - private string expression = ""; - [Parameter] public string? Expression { get; set; } - private string sample = ""; - [Parameter] public string? Sample { get; set; } - [Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary? AdditionalAttributes { get; set; } - [Parameter] public EventCallback OnChange { get; set; } - - private Modal? testBenchDialog; - private MonacoEditor inputEditor = null!; - private MonacoEditor outputEditor = null!; - private string uid = Guid.NewGuid().ToString(); - private string inputEditorId => "jq-editor-" + this.uid; - private string outputEditorId => "jq-output-" + this.uid; - private string testExpression = ""; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.Expression != null && this.expression != this.Expression) - { - this.expression = this.Expression; - } - if (this.Sample != null && this.sample != this.Sample) - { - this.sample = this.Sample; - } - } - - private async Task OnExpressionChangeAsync(string? expression) - { - this.expression = expression ?? ""; - await this.OnChange.InvokeAsync(expression); - } - - private async Task ShowTestBenchAsync() - { - this.testExpression = this.expression; - await this.testBenchDialog!.ShowAsync(); - } - - private async Task SetEditorsLanguageAsync() - { - var inputModel = await this.inputEditor!.GetModel(); - var outputModel = await this.outputEditor!.GetModel(); - await MonacoEditorBase.SetModelLanguage(inputModel, this.MonacoEditorHelper.PreferedLanguage); - await MonacoEditorBase.SetModelLanguage(outputModel, this.MonacoEditorHelper.PreferedLanguage); - } - - private async Task ToggleLanguage(string language) - { - string input = await this.inputEditor!.GetValue(); - string output = await this.outputEditor!.GetValue(); - try - { - if (language == PreferedLanguage.YAML) { - input = await this.YamlConverter.JsonToYaml(input); - output = await this.YamlConverter.JsonToYaml(output); - } - else - { - input = await this.YamlConverter.YamlToJson(input); - output = await this.YamlConverter.YamlToJson(output); - } - } - catch(Exception ex) - { - Console.WriteLine(ex.ToString()); - input = await this.inputEditor!.GetValue(); - output = await this.outputEditor!.GetValue(); - await this.MonacoEditorHelper.ChangePreferedLanguage(language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - } - await this.SetEditorsLanguageAsync(); - await this.inputEditor!.SetValue(input); - await this.outputEditor!.SetValue(output); - this.StateHasChanged(); - } - - private async Task OnInputEditorDidInit(MonacoEditorBase editorBase) - { - await this.SetEditorsLanguageAsync(); - await this.inputEditor!.SetValue(this.sample); - } - - private async Task OnOutputEditorDidInit(MonacoEditorBase editorBase) - { - await this.SetEditorsLanguageAsync(); - } - - private async Task ProcessExpressionAsync() - { - if (string.IsNullOrWhiteSpace(this.testExpression)) - return; - var trimmedExpression = this.testExpression.TrimStart('$').TrimStart('{').TrimEnd('}').Trim(); - if (string.IsNullOrWhiteSpace(trimmedExpression)) - return; - var json = await this.inputEditor.GetValue(); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - json = await this.YamlConverter.YamlToJson(json); - } - try - { - var output = await this.JsRuntime.InvokeAsync("jq.raw", json, trimmedExpression); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - output = await this.YamlConverter.JsonToYaml(output); - } - await this.outputEditor.SetValue(output); - } - catch - { - // todo: display error ? - } - } - - private async Task SaveExpressionAsync() - { - await this.OnExpressionChangeAsync(this.testExpression); - await this.HideTesBenchAsync(); - } - - private async Task HideTesBenchAsync() - { - await this.testBenchDialog!.ToggleAsync(); - } - - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.css b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.css deleted file mode 100644 index 3beb6246a..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.css +++ /dev/null @@ -1,4 +0,0 @@ -.play-icon { - right: 0.375rem; - top: 0.75rem; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.min.css b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.min.css deleted file mode 100644 index ae3e003f2..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.play-icon{right:.375rem;top:.75rem;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.scss b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.scss deleted file mode 100644 index ec7d96be4..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.scss +++ /dev/null @@ -1,4 +0,0 @@ -.play-icon { - right: 0.375rem; - top: 0.75rem; -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/MetadataEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/MetadataEditor.razor deleted file mode 100644 index e2f1884f7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/MetadataEditor.razor +++ /dev/null @@ -1,137 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Neuroglia.Serialization - - - - @if(metadata != null) - { - var metadataDictionary = (IDictionary)metadata.ToObject()!; - var index = -1; - @foreach (var kvp in metadataDictionary) - { - index++; - -
-
- - - - - - - - } - } - -
@kvp.Key@kvp.Value - - - - - - - - - - - -
Name
Value
-
- - -@code { - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private DynamicObject? metadata; - [Parameter] public DynamicObject? Metadata { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (Metadata == null) - return; - if (this.metadata == this.Metadata) - return; - this.metadata = this.Metadata; - } - - protected virtual async Task OnAddMetadataPropertyAsync() - { - if (this.Workflow == null) - return; - if (this.metadata == null) - this.metadata = new(); - this.metadata.Set("undefined", "undefined"); - await this.OnChange.InvokeAsync(this.metadata); - this.StateHasChanged(); - } - - protected virtual async Task OnMetadataPropertyNameChangedAsync(int index, string name) - { - if (this.Workflow == null || this.metadata == null) - return; - if (this.metadata == null) - this.metadata = new(); - var obsolete = this.metadata.DynamicProperties.ElementAt(index); - this.metadata.Remove(obsolete.Key); - if (index >= this.metadata.DynamicProperties.Count) - index = this.metadata.DynamicProperties.Count - 1; - if (index < 0) - index = 0; - if (this.metadata.DynamicProperties.Any()) - this.metadata.Insert(index, name, obsolete.Value); - else - this.metadata.Set(name, obsolete.Value); - await this.OnChange.InvokeAsync(this.metadata); - this.StateHasChanged(); - } - - protected virtual async Task OnMetadataPropertyValueChangedAsync(string name, object? value) - { - if (this.Workflow == null || this.metadata == null) - return; - if (this.metadata == null) - this.metadata = new(); - if (!this.metadata.TryGet(name, out var currentValue) || value == currentValue) - return; - this.metadata.Set(name, value); - await this.OnChange.InvokeAsync(this.metadata); - this.StateHasChanged(); - } - - protected virtual async Task OnMetadataPropertyRemovedAsync(string name) - { - if (this.Workflow == null || this.metadata == null) - return; - if (this.metadata == null) - this.metadata = new(); - if (!this.metadata.Remove(name)) - return; - if (!this.metadata.DynamicProperties.Any()) - this.metadata = null; - await this.OnChange.InvokeAsync(this.metadata); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ODataEntitySetSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ODataEntitySetSelector.razor deleted file mode 100644 index f33c4d0f3..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ODataEntitySetSelector.razor +++ /dev/null @@ -1,72 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Microsoft.Data.OData -@using Neuroglia.Data -@using Neuroglia.Data.Services -@inject ISchemaRegistry SchemaRegistry - - - -@code { - - [Parameter] public Uri? DocumentUri { get; set; } - - [Parameter] public string? OperationId { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - private ISchemaDescriptor? schema; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.DocumentUri == null) - return; - this.schema = await this.SchemaRegistry.GetODataSchemaAsync(this.DocumentUri); - } - - protected virtual async Task OnOperationChangedAsync(string? operation) - { - await this.OnChange.InvokeAsync(operation); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/OpenApiOperationSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/OpenApiOperationSelector.razor deleted file mode 100644 index 904f97168..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/OpenApiOperationSelector.razor +++ /dev/null @@ -1,75 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Microsoft.OpenApi.Models -@using Neuroglia.Data -@using Neuroglia.Data.Services -@inject ISchemaRegistry SchemaRegistry - - - -@code { - - [Parameter] public Uri? DocumentUri { get; set; } - - [Parameter] public string? OperationId { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - private ISchemaDescriptor? schema; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.DocumentUri == null) - return; - this.schema = await this.SchemaRegistry.GetOpenApiSchemaAsync(this.DocumentUri); - } - - protected virtual async Task OnOperationChangedAsync(string? operation) - { - await this.OnChange.InvokeAsync(operation); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/OutcomeEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/OutcomeEditor.razor deleted file mode 100644 index 12465251f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/OutcomeEditor.razor +++ /dev/null @@ -1,81 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - - - - - - -
Type - -
- -@switch (Outcome?.Type) -{ - case WorkflowOutcomeType.End: - - break; - case WorkflowOutcomeType.Transition: - - break; - default: - The specified outcome type '@EnumHelper.Stringify(outcome!.Type)' is not supported - break; -} - -@code -{ - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private WorkflowOutcome? outcome; - [Parameter] public WorkflowOutcome? Outcome { get; set; } - - [Parameter] public List ForbiddenTransitions { get; set; } = new(); - - [Parameter] public ControlFlow ControlFlow { get; set; } = ControlFlow.Default; - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.outcome != this.Outcome) - this.outcome = this.Outcome; - } - - protected virtual async Task OnOutcomeTypeChangedAsync(ChangeEventArgs e) - { - this.outcome = new WorkflowOutcome(EnumHelper.Parse((string)e.Value!)); - await this.OnChange.InvokeAsync(this.outcome); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ScheduleEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ScheduleEditor.razor deleted file mode 100644 index 6b00bf814..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/ScheduleEditor.razor +++ /dev/null @@ -1,124 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - - - - - - @switch(scheduleType) - { - case ScheduleDefinitionType.Cron: - - - - - - - - - break; - case ScheduleDefinitionType.Interval: - - - - - break; - default: - throw new NotSupportedException($"The specified {nameof(ScheduleDefinitionType)} '{scheduleType}' is not supported"); - } - -
Type - -
Expression - -
Valid until - -
Interval - -
- -@code { - private ScheduleDefinitionType scheduleType; - - private ScheduleDefinition? schedule; - [Parameter] public ScheduleDefinition? Schedule { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.scheduleType = this.schedule == null ? ScheduleDefinitionType.Cron : this.schedule.Type; - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - var updated = false; - if (this.schedule != this.Schedule) - { - this.schedule = this.Schedule; - updated = true; - } - if (!updated) - return; - } - - private async Task OnTypeChangedAsync(ScheduleDefinitionType scheduleType) - { - await this.OnChangedAsync(s => - { - switch(scheduleType) - { - case ScheduleDefinitionType.Cron: - s.Interval = null; - s.Cron = new(); - break; - case ScheduleDefinitionType.Interval: - s.Interval = TimeSpan.Zero; - s.Cron = null; - break; - default: - throw new NotSupportedException($"The specified {nameof(ScheduleDefinitionType)} '{scheduleType}' is not supported"); - } - }); - } - - private async Task OnChangedAsync(Action patch) - { - if (this.schedule == null) - this.schedule = new(); - patch(this.schedule); - if (this.schedule.Cron == null && !this.schedule.Interval.HasValue) - this.schedule = null; - this.scheduleType = this.schedule == null ? ScheduleDefinitionType.Cron : this.schedule.Type; - await this.OnChange.InvokeAsync(this.schedule); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/SecretSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/SecretSelector.razor deleted file mode 100644 index 870f44edc..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/SecretSelector.razor +++ /dev/null @@ -1,76 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - -@code { - - private string CreateNewOptionValue = "__wfs:create"; - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - [Parameter] public bool Disabled { get; set; } = false; - - [Parameter] public string? CssClass{ get; set; } - - [Parameter] public bool AddCreateNewOption { get; set; } = true; - - [Parameter] public string? Selected { get; set; } - - [Parameter] public List? Secrets { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected virtual async Task OnStateSelectorValueChangedAsync(ChangeEventArgs e) - { - if (e.Value == null) - return; - var secret = (string)e.Value!; - if (secret == CreateNewOptionValue) - { - secret = $"secret-{(this.Workflow.Secrets == null ? 0 : this.Workflow.Secrets.Count + 1)}"; - if (this.Workflow.Secrets == null) - this.Workflow.Secrets = new(); - this.Workflow.Secrets.Add(secret); - } - this.Selected = secret; - await this.OnChange.InvokeAsync(secret); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateDataFilterEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateDataFilterEditor.razor deleted file mode 100644 index cdd1fdae7..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateDataFilterEditor.razor +++ /dev/null @@ -1,76 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - - - - - - - - - - -
Input - -
Output - -
- -@code { - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private StateDataFilterDefinition? dataFilter; - [Parameter] public StateDataFilterDefinition? DataFilter { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.DataFilter == this.dataFilter) - return; - this.dataFilter = this.DataFilter; - } - - protected virtual async Task OnChangeAsync(Action patch) - { - if (this.dataFilter == null) - this.dataFilter = new(); - patch(this.dataFilter); - if (string.IsNullOrWhiteSpace(this.dataFilter.Input) - && string.IsNullOrWhiteSpace(this.dataFilter.Output)) - this.dataFilter = null; - await this.OnChange.InvokeAsync(this.dataFilter); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditor.razor deleted file mode 100644 index d9f2eba81..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditor.razor +++ /dev/null @@ -1,257 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -
- - - - - - - -
-
- - - - - - - - -
-
- - - - - - - - -
-
- - - - - - - - -
-
- - - - - - - - -
-
- - - - - - - -
General
- - - - - - - - - - - - - - - - - - - - - - - -
Name - -
Type - -
Data input schema - -
Compensated by -
Used for compensation
-
Data filter
- -
Outcome
- -
Metadata
- -
@state.Type.ToString()
- @switch (state) - { - case CallbackStateDefinition callbackState: - - break; - case EventStateDefinition eventState: - - break; - case ForEachStateDefinition forEachState: - - break; - case InjectStateDefinition injectState: - - break; - case OperationStateDefinition operationState: - - break; - case ParallelStateDefinition parallelState: - - break; - case SleepStateDefinition sleepState: - - break; - case SwitchStateDefinition switchState: - - break; - } -
-
- -@code { - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private StateDefinition state { get; set; } = null!; - [Parameter] public StateDefinition State { get; set; } = null!; - - [Parameter] public EventCallback OnChange { get; set; } - - private StateDefinition? compensatedBy; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) { - this.state = this.State; - if (this.state?.CompensatedBy != null && this.compensatedBy?.Name != this.state.CompensatedBy) - this.compensatedBy = this.Workflow.GetState(this.state.CompensatedBy); - } - } - - protected virtual Action PatchStateName(ChangeEventArgs e) { - return (StateDefinition state) => { - var previousName = state.Name; - state.Name = (string)e.Value!; - if (this.Workflow.StartStateName == previousName) { - this.Workflow.StartStateName = state.Name; - } - this.Workflow.States.ForEach(s => - { - if (!state.UsedForCompensation) { - if (s.TransitionToStateName == previousName) { - s.TransitionToStateName = state.Name; - } - } - else if (s.CompensatedBy == previousName) { - s.CompensatedBy = state.Name; - } - // todo: update conditions when errors/timeout are implemented - }); - }; - } - - protected virtual async Task OnStateTypeChangedAsync(StateType stateType) - { - var stateIndex = this.Workflow.States.IndexOf(this.state); - if (this.Workflow.States.Remove(this.state)) - { - if (stateIndex > this.Workflow.States.Count) - stateIndex = this.Workflow.States.Count; - } - else - { - stateIndex = this.Workflow.States.Count; - } - if (stateIndex < 0) - stateIndex = 0; - this.state = this.state.OfType(stateType); - this.Workflow.States.Insert(stateIndex, this.state); - await this.OnPropertyChangedAsync(nameof(state.Type), _ => { }); - } - - protected virtual async Task OnCompensatedBySelectionChangedAsync(StateDefinition? state) - { - if (this.state == null) - return; - if(state == null) - { - this.state.CompensatedBy = null; - this.compensatedBy = null; - return; - } - this.compensatedBy = state; - await this.OnPropertyChangedAsync(nameof(state.CompensatedBy), s => - { - s.CompensatedBy = state.Name; - }); - } - - protected virtual async Task OnPropertyChangedAsync(string property, Action patchAction) - { - patchAction(this.state); - await this.OnChange.InvokeAsync(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/CallbackStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/CallbackStateEditor.razor deleted file mode 100644 index 68ffd398a..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/CallbackStateEditor.razor +++ /dev/null @@ -1,90 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if (state != null) -{ - - - -
- - - - - - - - - -
- - - - - -
Action
- -
Callback event - -
Event data filter
- -
-} - -@code { - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - private CallbackStateDefinition? state = null; - [Parameter] public CallbackStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) - { - this.state = this.State; - if (this.state != null && this.state.Action == null) - this.state.Action = new() { Function = new() { RefName = "undefined" } }; - } - } - - protected virtual async Task OnChangeAsync(Action? patch = null) - { - if (this.state == null) - return; - if(patch != null) - patch(this.state); - if (this.state.EventDataFilter.UseData - && string.IsNullOrWhiteSpace(this.state.EventDataFilter.Data) - && string.IsNullOrWhiteSpace(this.state.EventDataFilter.ToStateData)) - this.state.EventDataFilter = null!; - await this.OnChange.InvokeAsync(this.state); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/EventStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/EventStateEditor.razor deleted file mode 100644 index 77bb65fe8..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/EventStateEditor.razor +++ /dev/null @@ -1,107 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(state != null) -{ - - - - - - - - - - - - - -
Exclusive - -
Triggers
- - - - - - - - - - @foreach (var trigger in state.Triggers) - { - -
-
- - - - - - - - - } - -
EventsAction execution modeActions
@string.Join(' ', trigger.Events)@EnumHelper.Stringify(trigger.ActionMode)@trigger.Actions.Count - -
- -
-} - -@code { - - private EventStateDefinition? state = null; - [Parameter] public EventStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) - this.state = this.State; - } - - protected virtual async Task OnCreateTriggerAsync() - { - if (this.state == null) - return; - if (this.state.Triggers == null) - this.state.Triggers = new(); - var e = new EventStateTriggerDefinition(); - await this.OnChangedAsync(s => - { - s.Triggers!.Add(e); - }); - } - - protected virtual async Task OnChangedAsync(Action patch) - { - if (this.state == null) - return; - patch(this.state); - await this.OnChange.InvokeAsync(this.state); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/ForEachStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/ForEachStateEditor.razor deleted file mode 100644 index 8c90e2f25..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/ForEachStateEditor.razor +++ /dev/null @@ -1,113 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(state != null) -{ - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - -
Input collection - -
Output collection - -
Iteration parameter - -
Batch size - -
Action execution mode - -
Actions
- -
-} - -@code { - - private ForEachStateDefinition? state = null; - [Parameter] public ForEachStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) - this.state = this.State; - } - - protected virtual async Task OnChangedAsync(Action patch) - { - if (this.state == null) - return; - patch(this.state); - await this.OnChange.InvokeAsync(this.state); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/InjectStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/InjectStateEditor.razor deleted file mode 100644 index 699cc786f..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/InjectStateEditor.razor +++ /dev/null @@ -1,50 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(this.state != null) -{ - - - - - - - - - -
Data
- -
-} - -@code { - - private InjectStateDefinition? state = null; - [Parameter] public InjectStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) { - this.state = this.State; - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/OperationStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/OperationStateEditor.razor deleted file mode 100644 index 291d9a564..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/OperationStateEditor.razor +++ /dev/null @@ -1,72 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(state != null) -{ - - - - - - - -
-
- - - - - - -
Execution mode - -
Actions
- -
-} - -@code { - private OperationStateDefinition? state = null; - [Parameter] public OperationStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) - this.state = this.State; - } - - protected virtual async Task OnChangedAsync(Action patch) - { - if (this.state == null) - return; - patch(this.state); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/ParallelStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/ParallelStateEditor.razor deleted file mode 100644 index 07689a5db..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/ParallelStateEditor.razor +++ /dev/null @@ -1,84 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(state != null) -{ - - - - - - - @if(state.CompletionType == ParallelCompletionType.AtLeastN) - { - - - - - } - -
-
- - - - - - -
Completion type - -
Completed branches - -
Branches
- -
-} - -@code { - private ParallelStateDefinition? state = null; - [Parameter] public ParallelStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) - this.state = this.State; - } - - protected virtual async Task OnChangedAsync(Action patch) - { - if (this.state == null) - return; - patch(this.state); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/SleepStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/SleepStateEditor.razor deleted file mode 100644 index 8745dc436..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/SleepStateEditor.razor +++ /dev/null @@ -1,58 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(this.state != null) -{ - - - - - - - -
Duration - -
-} - -@code { - - private SleepStateDefinition? state = null; - [Parameter] public SleepStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) { - this.state = this.State; - } - } - - protected virtual async Task OnChangedAsync(Action patch) - { - if (this.state == null) - return; - patch(this.state); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/SwitchStateEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/SwitchStateEditor.razor deleted file mode 100644 index 60d404e65..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateEditors/SwitchStateEditor.razor +++ /dev/null @@ -1,228 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - -@if(this.state != null) -{ - - - - - - - -
- - - - - -
- - - - - -
Type - -
Cases
- - - - - - - - - - - @switch(state.SwitchType) - { - case SwitchStateType.Data: - if(state.DataConditions != null) - { - foreach (var dataCase in state.DataConditions) - { - -
-
- - - - - - - - - } - } - - break; - case SwitchStateType.Event: - if(state.EventConditions != null) - { - foreach (var eventCase in state.EventConditions) - { - -
-
- - - - - - - - - } - } - break; - default: - throw new NotSupportedException($"The specified {nameof(state.SwitchType)} '{state.SwitchType}' is not supported"); - } - -
NameConditionOutcome
@dataCase.Name@dataCase.Condition@dataCase.GetOutcome().ToString() - - @eventCase.Name@eventCase.Event@eventCase.GetOutcome().ToString() - -
- -
Default case
- -
-} - -@code { - - private SwitchStateDefinition? state = null; - [Parameter] public SwitchStateDefinition? State { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.state != this.State) { - this.state = this.State; - if (this.state == null) - return; - await this.OnChangeAsync(state => - { - if (state.DataConditions == null && state.EventConditions == null) - state.DataConditions = new() { new() { Name = "case-1" } }; - if(state.DefaultCondition == null) - state.DefaultCondition = new(); - }); - } - } - - protected virtual async Task OnSwitchStateTypeChangedAsync(SwitchStateType type) - { - if (this.state == null) - return; - Action patch; - switch (this.state.SwitchType) - { - case SwitchStateType.Data: - patch = state => - { - if (state.DataConditions == null) - state.DataConditions = new(); - state.EventConditions = null; - state.EventTimeout = null; - }; - break; - case SwitchStateType.Event: - patch = state => - { - if (state.EventConditions == null) - state.EventConditions = new(); - state.DataConditions = null; - }; - break; - default: - throw new NotSupportedException($"The specified {nameof(state.SwitchType)} '{state.SwitchType}' is not supported"); - } - await this.OnChangeAsync(patch); - } - - protected virtual async Task OnCreateCaseAsync() - { - if (this.state == null) - return; - Action patch; - switch (this.state.SwitchType) - { - case SwitchStateType.Data: - patch = state => - { - if (state.DataConditions == null) - state.DataConditions = new(); - state.EventConditions = null; - state.EventTimeout = null; - state.DataConditions.Add(new() { Name = $"case-{(state.DataConditions.Count + 1)}" }); - }; - break; - case SwitchStateType.Event: - patch = state => - { - if (state.EventConditions == null) - state.EventConditions = new(); - state.DataConditions = null; - state.EventConditions.Add(new() { Name = $"case-{(state.EventConditions.Count + 1)}" }); - }; - break; - default: - throw new NotSupportedException($"The specified {nameof(state.SwitchType)} '{state.SwitchType}' is not supported"); - } - await this.OnChangeAsync(patch); - } - - protected virtual async Task OnChangeAsync(Action? patch = null) - { - if (this.state == null) - return; - if (patch != null) - patch(this.state); - await this.OnChange.InvokeAsync(); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateSelector.razor deleted file mode 100644 index 9a213a719..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/StateSelector.razor +++ /dev/null @@ -1,85 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - -@code { - - private string CreateNewOptionValue = "__wfs:create"; - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - [Parameter] public bool Disabled { get; set; } = false; - - [Parameter] public string? CssClass{ get; set; } - - [Parameter] public bool AddCreateNewOption { get; set; } = true; - - [Parameter] public StateDefinition? Selected { get; set; } - - [Parameter] public List? States { get; set; } - - [Parameter] public EventCallback OnChange { get; set; } - - [Parameter] public ControlFlow ControlFlow { get; set; } = ControlFlow.Default; - - protected virtual async Task OnStateSelectorValueChangedAsync(ChangeEventArgs e) - { - if (e.Value == null) - return; - var state = Workflow.GetState((string)e.Value!); - if ((string)e.Value == CreateNewOptionValue) - { - state = new OperationStateDefinition() { Name = $"state-{this.Workflow.States.Count + 1}", IsEnd = true }; - if (this.ControlFlow == ControlFlow.Compensation) - state.UsedForCompensation = true; - this.Workflow.States.Add(state); - } - this.Selected = state; - await this.OnChange.InvokeAsync(state); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/TransitionEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/TransitionEditor.razor deleted file mode 100644 index 6dc18ccf1..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/TransitionEditor.razor +++ /dev/null @@ -1,65 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard - - - - - - - - -
Next state - -
- -@code { - - [CascadingParameter] public WorkflowDefinition Workflow { get; set; } = null!; - - [Parameter] public List ForbiddenTransitions { get; set; } = new(); - - [Parameter] public TransitionDefinition Transition { get; set; } = new(); - - [Parameter] public ControlFlow ControlFlow { get; set; } = ControlFlow.Default; - - [Parameter] public EventCallback OnChange { get; set; } - - private StateDefinition? transitionTo; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.Transition == null) - this.Transition = new(); - else - this.transitionTo = this.Workflow.GetState(this.Transition.NextState); - } - - protected virtual async Task OnStateSelectedAsync(StateDefinition state) - { - this.transitionTo = state; - this.Transition.NextState = state?.Name!; - this.StateHasChanged(); - await this.OnChange.InvokeAsync(new(WorkflowOutcomeType.Transition, this.Transition.NextState)); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/WorkflowEditor.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/WorkflowEditor.razor deleted file mode 100644 index 3a9cc20ad..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/WorkflowEditor.razor +++ /dev/null @@ -1,602 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Newtonsoft.Json.Schema -@using System.Dynamic -@using Synapse.Dashboard.Pages.Workflows.Editor.Actions -@using Synapse.Dashboard.Pages.Workflows.Editor.State -@inherits StatefulComponent -@inject IDispatcher Dispatcher - -@if(workflow != null) -{ - - - - -
- -
-
General
-
- - - - - - - - - - - - - - - - -
Name
Id -
- -
- - -
-
-
Description
Version
Spec version -
Expression language -
Keep active
- -
-
- - -
- -
-
States
-
- - @if(workflow.States.Any()) - { - - - - - - - - - - - @foreach (var state in workflow.States) - { - -
-
- - - - - - - - - - } - -
NameTypeOutcome
@state.Name@EnumHelper.Stringify(state.Type)@(state.IsEnd ? "End workflow" : $"Transition to '{state.TransitionToStateName}'") - @if(state == workflow.GetStartState()) - { - start - } - @if (state.IsEnd) - { - end - } - @if(!string.IsNullOrWhiteSpace(state.CompensatedBy)) - { - compensated - } - @if(state.UsedForCompensation) - { - compensating - } - @if(state.Errors != null - && state.Errors.Any()) - { - fault - } - - -
- } - else - { - - } - -
-
- - -
- -
-
Events
-
- - - - - - - - - - - - - @if(workflow.Events != null) - { - @foreach (var e in workflow.Events) - { - -
-
- - - - - - - - - - - } - } - -
NameKindSourceType
@e.Name@EnumHelper.Stringify(e.Kind)@e.Source@e.Type - @if(e.Correlations != null && e.Correlations.Any()) - { - correlated - } - - -
- - -
-
- - -
- -
-
Functions
-
- - - - - - - - - - - - @if(workflow.Functions != null) - { - @foreach (var function in workflow.Functions) - { - -
-
- - - - - - - - - - } - } - -
NameTypeOperation
@function.Name@EnumHelper.Stringify(function.Type)@function.Operation - @if(!string.IsNullOrWhiteSpace(function.AuthRef)) - { - secured - } - - -
- - -
-
- - -
- -
-
Secrets
-
- - - - @if(workflow?.Secrets != null) - { - var index = -1; - foreach(var secret in Workflow.Secrets!) - { - index++; - -
-
- - - - - - - } - } - -
@secret - - - - - - - -
Secret name
-
- - -
-
- - -
- -
-
Authentication
-
- - - - - - - - - - - @if(workflow?.Auth != null) - { - @foreach (var authentication in Workflow!.Auth!) - { - -
-
- - - - - - - - } - } - -
NameScheme
@authentication.Name@EnumHelper.Stringify(authentication.Scheme) - -
- - -
-
- - -
- -
-
Annotations
-
- - - - @if(workflow.Annotations != null) - { - var index = -1; - @foreach (var annotation in workflow.Annotations) - { - index++; - -
-
- - - - - - - } - } - -
@annotation - - - - - -
Annotation
-
- - -
-
- - -
- -
-
Metadata
-
- - - -
-
- -
- -} -else -{ - -} - -@code { - - private bool autoGenerateId = true; - - private WorkflowDefinition? workflow { get; set; } - [Parameter] public WorkflowDefinition? Workflow { get; set; } - [Parameter] public EventCallback OnPropertyChanged { get; set; } - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.workflow != this.Workflow) { - this.workflow = this.Workflow; - } - } - - protected virtual void OnToggleExpand(string sectionName, bool isExpanded) - { - this.Dispatcher.Dispatch(new ToggleExpand(sectionName, isExpanded)); - this.StateHasChanged(); - } - - protected virtual async Task OnCreateStateAsync() - { - if (this.workflow == null) - return; - var state = new OperationStateDefinition() - { - Name = $"state-{workflow.States.Count + 1}", - IsEnd = true - }; - await this.OnPropertyChangedAsync(nameof(workflow.States), d => d.States.Add(state)); - } - - protected virtual async Task OnCreateEventAsync() - { - if (this.workflow == null) - return; - if (this.workflow.Events == null) - this.workflow.Events = new(); - var e = new EventDefinition() { Name = $"event-{workflow.Events.Count + 1}" }; - await this.OnPropertyChangedAsync(nameof(workflow.Events), d => - { - d.Events!.Add(e); - }); - } - - protected virtual async Task OnCreateFunctionAsync() - { - if (this.workflow == null) - return; - if (this.workflow.Functions == null) - this.workflow.Functions = new(); - var function = new FunctionDefinition() { Name = $"function-{workflow.Functions.Count + 1}" }; - await this.OnPropertyChangedAsync(nameof(workflow.Functions), d => - { - d.Functions!.Add(function); - }); - } - - protected virtual async Task OnAddSecretAsync() - { - if (this.Workflow == null) - return; - if (this.Workflow.Secrets == null) - this.Workflow.Secrets = new(); - var secret = $"secret-{Workflow.Secrets.Count + 1}"; - await this.OnPropertyChangedAsync(nameof(Workflow.Secrets), d => - { - d.Secrets!.Add(secret); - }); - } - - protected virtual async Task OnsecretChangedAsync(int index, string secret) - { - if (this.Workflow == null || this.Workflow.Secrets == null) - return; - await this.OnPropertyChangedAsync(nameof(Workflow.Secrets), d => - { - if (d.Secrets == null) - d.Secrets = new(); - d.Secrets.RemoveAt(index); - if(index >= d.Secrets.Count) - index = d.Secrets.Count - 1; - if (index < 0) - index = 0; - d.Secrets.Insert(index, secret); - }); - } - - protected virtual async Task OnAddAnnotationAsync() - { - if (this.Workflow == null) - return; - if (this.Workflow.Annotations == null) - this.Workflow.Annotations = new(); - var secret = $"annotation-{Workflow.Annotations.Count + 1}"; - await this.OnPropertyChangedAsync(nameof(Workflow.Annotations), d => - { - d.Annotations!.Add(secret); - }); - } - - protected virtual async Task OnAnnotationChangedAsync(int index, string annotation) - { - if (this.Workflow == null || this.Workflow.Annotations == null) - return; - await this.OnPropertyChangedAsync(nameof(Workflow.Annotations), d => - { - if (d.Annotations == null) - d.Annotations = new(); - d.Annotations.RemoveAt(index); - if(index >= d.Annotations.Count) - index = d.Annotations.Count - 1; - if (index < 0) - index = 0; - d.Annotations.Insert(index, annotation); - }); - } - - protected virtual async Task OnAddMetadataAsync() - { - if (this.Workflow == null) - return; - await this.OnPropertyChangedAsync(nameof(Workflow.Metadata), d => - { - if (d.Metadata == null) - d.Metadata = new(); - d.Metadata.Set("undefined", "undefined"); - }); - } - - protected virtual async Task OnMetadataPropertyNameChangedAsync(int index, string name) - { - if (this.Workflow == null || this.Workflow.Metadata == null) - return; - await this.OnPropertyChangedAsync(nameof(Workflow.Metadata), d => - { - if (d.Metadata == null) - d.Metadata = new(); - var obsolete = d.Metadata.DynamicProperties.ElementAt(index); - d.Metadata.Remove(obsolete.Key); - if (index >= d.Metadata.DynamicProperties.Count) - index = d.Metadata.DynamicProperties.Count - 1; - if (index < 0) - index = 0; - if (d.Metadata.DynamicProperties.Any()) - d.Metadata.Insert(index, name, obsolete.Value); - else - d.Metadata.Set(name, obsolete.Value); - }); - } - - protected virtual async Task OnCreateAuthenticationAsync() - { - if (this.Workflow == null) - return; - if (this.Workflow.Auth == null) - this.Workflow.Auth = new(); - var auth = new AuthenticationDefinition() { Name = $"authentication-{Workflow.Auth.Count + 1}", Scheme = AuthenticationScheme.Basic, Properties = new BasicAuthenticationProperties() }; - await this.OnPropertyChangedAsync(nameof(Workflow.Auth), d => - { - d.Auth!.Add(auth); - }); - } - - protected virtual async Task OnStatePropertyChangedAsync(StateDefinition state) - { - if (this.workflow == null) - return; - await this.OnPropertyChangedAsync(nameof(workflow.States)); - } - - protected virtual async Task OnAutoGenerateIdChanged(bool autoGenerate) - { - this.autoGenerateId = autoGenerate; - if (this.autoGenerateId) - await this.OnPropertyChangedAsync(nameof(workflow.Name), w => w.Id = w.Name.Slugify("-").ToLowerInvariant()); - } - - protected virtual async Task OnNameChangedAsync(string? name) - { - if (this.workflow == null) - return; - await this.OnPropertyChangedAsync(nameof(workflow.Name), w => - { - w.Name = name; - if (autoGenerateId && !string.IsNullOrWhiteSpace(name)) - w.Id = name.Slugify("-").ToLowerInvariant(); - }); - } - - protected virtual async Task OnFunctionEditorChanged(FunctionDefinition? functionDefinition) { - await this.OnPropertyChangedAsync(nameof(workflow.Functions)); - } - - protected virtual async Task OnEventEditorChanged(EventDefinition? eventDefinition) - { - await this.OnPropertyChangedAsync(nameof(workflow.Events)); - } - - protected virtual async Task OnPropertyChangedAsync(string property, Action? patchAction = null) - { - if (this.workflow == null) - return; - if (patchAction != null) - patchAction(this.workflow); - await this.OnPropertyChanged.InvokeAsync(this.workflow); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/WorkflowSelector.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/WorkflowSelector.razor deleted file mode 100644 index be32f04a0..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowEditor/WorkflowSelector.razor +++ /dev/null @@ -1,134 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Semver -@inherits StatefulComponent - -@if(workflowsByDefinitionId != null) -{ -
- - -
-} -else -{ - -} - -@code { - - private string? selectedId; - [Parameter] public string? SelectedId { get; set; } - - private string? selectedVersion; - [Parameter] public string? SelectedVersion { get; set; } = "latest"; - - [Parameter] public EventCallback OnChange { get; set; } - - private IDisposable? subscription; - private V1Workflow? selected; - private IDictionary>? workflowsByDefinitionId; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.subscription = this.Feature - .Subscribe(workflows => - { - this.workflowsByDefinitionId = workflows - .GroupBy(w => w.Definition.Id!) - .OrderBy(w => w.Key) - .ToDictionary(g => g.Key, g => g.OrderByDescending(w => SemVersion.Parse(w.Definition.Version, SemVersionStyles.Any)).ToList()); - this.StateHasChanged(); - }); - this.Dispatcher.Dispatch(new ListV1Workflows()); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - var updated = false; - if (this.selectedId != this.SelectedId) - { - this.selectedId = this.SelectedId; - updated = true; - } - if (this.selectedVersion != this.SelectedVersion) - { - this.selectedVersion = this.SelectedVersion; - updated = true; - } - if (!updated) - return; - this.UpdateSelectionState(); - } - - protected virtual void UpdateSelectionState() - { - if (this.workflowsByDefinitionId == null) - return; - var workflowVersions = this.workflowsByDefinitionId.FirstOrDefault(g => g.Key == this.selectedId); - if (string.IsNullOrWhiteSpace(this.selectedVersion) || this.selectedVersion == "latest") - this.selected = workflowVersions.Value?.First(); - else - this.selected = workflowVersions.Value?.First(w => w.Definition.Version == this.selectedVersion); - this.StateHasChanged(); - } - - protected virtual async Task OnIdChangedAsync(string? id) - { - this.selectedId = id; - this.selectedVersion = "latest"; - this.UpdateSelectionState(); - await this.OnChange.InvokeAsync(new(this.selectedId!, this.selectedVersion)); - this.StateHasChanged(); - } - - protected virtual async Task OnVersionChangedAsync(string? version) - { - this.selectedVersion = version; - this.UpdateSelectionState(); - await this.OnChange.InvokeAsync(new(this.selectedId!, this.selectedVersion!)); - this.StateHasChanged(); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - this.subscription?.Dispose(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowToolbar/WorkflowToolbar.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowToolbar/WorkflowToolbar.razor deleted file mode 100644 index 6812af7cc..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowToolbar/WorkflowToolbar.razor +++ /dev/null @@ -1,224 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@using Neuroglia.Serialization -@using Newtonsoft.Json -@using Newtonsoft.Json.Linq -@using Newtonsoft.Json.Schema -@using Synapse.Apis.Management -@inject ISynapseManagementApi SynapseApi -@inject IJsonSerializer JsonSerializer -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IYamlConverter YamlConverter -@inject HttpClient HttpClient - -@if (workflow != null) -{ - - - Edit workflow - - - - Error - -

An error occured:

-
@((MarkupString)error)
- -
- -
- - - Start new instance - - @if (dataInputSchema != null) - { - - } - else - { -

Input the JSON payload to start the workflow with:

- - - } - @if (inputValidationErrors != null) - { -
-
    - @foreach (var error in inputValidationErrors) - { -
  • Line @error.LineNumber, Position @error.LinePosition: @error.Message
  • - } -
-
- } - -
- - -
-
-} - -@code { - [Parameter] public V1Workflow Workflow { get; set; } = null!; - protected V1Workflow workflow { get; set; } = null!; - - protected Modal errorModal = null!; - protected Modal workflowInputModal = null!; - protected MonacoEditor? workflowInputEditor; - protected JsonForm? jsonForm; - protected string error = null!; - protected JSchema? dataInputSchema; - protected IList? inputValidationErrors; - protected string editorValueBuffer = ""; - - protected virtual async Task ToggleLanguage(string language) - { - string text = await this.workflowInputEditor!.GetValue(); - try - { - if (language == PreferedLanguage.YAML) { - this.editorValueBuffer = await this.YamlConverter.JsonToYaml(text); - } - else - { - this.editorValueBuffer = await this.YamlConverter.YamlToJson(text); - } - } - catch(Exception ex) - { - Console.WriteLine(ex.ToString()); - await this.MonacoEditorHelper.ChangePreferedLanguage(language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - this.editorValueBuffer = text; - } - await this.OnMonacoEditorDidInit(this.workflowInputEditor); - this.StateHasChanged(); - } - - protected override async Task OnParametersSetAsync() - { - if (this.Workflow != null && this.workflow != this.Workflow) - { - this.workflow = this.Workflow; - await this.LoadDataInputSchemaAsync(); - } - } - - protected async Task LoadDataInputSchemaAsync() - { - var schema = this.Workflow.Definition.DataInputSchema?.Schema; - if(schema == null - && this.Workflow.Definition.DataInputSchemaUri != null) - { - var json = await this.HttpClient.GetStringAsync(this.Workflow.Definition.DataInputSchemaUri); - schema = JSchema.Parse(json); - } - this.dataInputSchema = schema; - } - - protected async Task OnMonacoEditorDidInit(MonacoEditorBase editor) - { - var model = await (editor as MonacoEditor)!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - await (editor as MonacoEditor)!.SetValue(this.editorValueBuffer); - } - - public async Task OnShowWorkflowInputModal(string value = "{}") - { - this.inputValidationErrors = null; - if(this.workflowInputEditor != null) - await this.workflowInputEditor.SetValue(value); - if(this.dataInputSchema != null - && this.workflowInputEditor != null) - { - var example = this.dataInputSchema.GenerateExample(); - if(example != null) - { - var json = JObject.FromObject(example).ToString(Formatting.Indented); - await this.workflowInputEditor.SetValue(json); - } - } - await this.workflowInputModal.ShowAsync(); - } - - protected async Task OnStartWorkflowAsync() - { - var json = string.Empty; - if(this.workflowInputEditor != null) - { - json = await this.workflowInputEditor.GetValue(); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) { - json = await this.YamlConverter.YamlToJson(json); - } - if(this.dataInputSchema != null) - { - var jobj = null as JObject; - if (string.IsNullOrWhiteSpace(json)) - jobj = new(); - else - jobj = JObject.Parse(json); - if(!jobj.IsValid(this.dataInputSchema, out IList errors)) - { - this.inputValidationErrors = errors; - return; - } - } - } - else - { - var value = await this.jsonForm.GetValueAsync(); - if(this.dataInputSchema != null) - { - var jobj = JObject.FromObject(value); - if(!jobj.IsValid(this.dataInputSchema, out IList errors)) - { - this.inputValidationErrors = errors; - return; - } - } - json = await this.JsonSerializer.SerializeAsync(value); - } - await this.workflowInputModal.HideAsync(); - this.inputValidationErrors = null; - this.StateHasChanged(); - if(this.workflowInputEditor != null) - await this.workflowInputEditor.SetValue("{}"); - var inputData = await this.JsonSerializer.DeserializeAsync(json); - try - { - var workflowInstance = await this.SynapseApi.CreateWorkflowInstanceAsync(new() - { - WorkflowId = this.Workflow.Id, - ActivationType = V1WorkflowInstanceActivationType.Manual, - InputData = inputData, - AutoStart = true - }); - } - catch(Exception ex) - { - this.error = ex.ToString(); - await this.errorModal.ShowAsync(); - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowsMetrics/WorkflowsMetrics.razor b/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowsMetrics/WorkflowsMetrics.razor deleted file mode 100644 index 33ae44a65..000000000 --- a/src/dashboard/Synapse.Dashboard/Features/Workflows/WorkflowsMetrics/WorkflowsMetrics.razor +++ /dev/null @@ -1,110 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@inject IStyleManager StyleManager - -@if (Workflow != null) -{ -

-
-
-
- @Workflow.RunningInstanceCount - Running instances -
-
- @Workflow.CompletedInstanceCount - Completed instances -
-
- @Workflow.FaultedInstanceCount - Faulted instances -
-
- @Workflow.CancelledInstanceCount - Cancelled instances -
-
-
-
- @Workflow.AverageInstanceDuration - Average instance duration -
-
- @Workflow.ShortestInstanceDuration - Shortest instance duration -
-
- @Workflow.LongestInstanceDuration - Longest instance duration -
-
-
- -
-
-
-} - -@code { - - private ChartConfiguration executedInstancesChart = null!; - - [Parameter] public V1Workflow Workflow { get; set; } = null!; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BuildDailyExecutedInstancesChartAsync(); - } - - private async ValueTask BuildDailyExecutedInstancesChartAsync() - { - this.executedInstancesChart = new() - { - Type = ChartType.Pie, - Data = - { - Labels = new() - { - "Completed", - "Faulted", - "Cancelled" - }, - Datasets = new() - { - new ChartDataset() - { - Data = new() - { - Workflow.CompletedInstanceCount, - Workflow.FaultedInstanceCount, - Workflow.CancelledInstanceCount - }, - BackgroundColor = new() - { - await this.StyleManager.GetVariableValueAsync("--bs-success"), - await this.StyleManager.GetVariableValueAsync("--bs-danger"), - await this.StyleManager.GetVariableValueAsync("--bs-warning") - } - } - } - } - }; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Layout/ApplicationLayout.cs b/src/dashboard/Synapse.Dashboard/Layout/ApplicationLayout.cs new file mode 100644 index 000000000..c7f40aef6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/ApplicationLayout.cs @@ -0,0 +1,54 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; + +namespace Synapse.Dashboard.Layout; + +/// +/// Represents the default implementation of the interface +/// +public class ApplicationLayout + : IApplicationLayout +{ + + /// + public event PropertyChangedEventHandler? PropertyChanged; + + /// + /// Gets the application title's + /// + public RenderFragment? TitleFragment => this.Title?.ChildContent; + + private ApplicationTitle? _Title; + /// + /// Gets/sets the application's title + /// + public ApplicationTitle? Title + { + get => this._Title; + set + { + if (this._Title == value) return; + this._Title = value; + this.OnTitleChanged(); + } + } + + /// + public void OnTitleChanged() + { + if (this.PropertyChanged != null) this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Title))); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Layout/ApplicationTitle.razor b/src/dashboard/Synapse.Dashboard/Layout/ApplicationTitle.razor new file mode 100644 index 000000000..76bdcccf1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/ApplicationTitle.razor @@ -0,0 +1,51 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Layout +@implements IDisposable +@inject IApplicationLayout Layout +@code +{ + + /// + /// Gets the application's title content + /// + [Parameter] + public RenderFragment? ChildContent { get; set; } + + /// + protected override void OnInitialized() + { + base.OnInitialized(); + if (this.Layout != null) this.Layout.Title = this; + } + + /// + protected override bool ShouldRender() + { + var shouldRender = base.ShouldRender(); + if (shouldRender) this.Layout.OnTitleChanged(); + return shouldRender; + } + + /// + public void Dispose() + { + if (this.Layout != null) this.Layout.Title = null; + GC.SuppressFinalize(this); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Layout/EmptyLayout.razor b/src/dashboard/Synapse.Dashboard/Layout/EmptyLayout.razor new file mode 100644 index 000000000..7c27bce78 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/EmptyLayout.razor @@ -0,0 +1,21 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Layout +@inherits LayoutComponentBase + + +@Body \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Layout/IApplicationLayout.cs b/src/dashboard/Synapse.Dashboard/Layout/IApplicationLayout.cs new file mode 100644 index 000000000..547acadf5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/IApplicationLayout.cs @@ -0,0 +1,35 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; + +namespace Synapse.Dashboard.Layout; + +/// +/// Defines the fundamentals of a service used to manage the application's layout +/// +public interface IApplicationLayout + : INotifyPropertyChanged +{ + + /// + /// Gets the application's current title, which is aggregated to produce the current page's title + /// + ApplicationTitle? Title { get; set; } + + /// + /// Handles changes in the application's title + /// + void OnTitleChanged(); + +} diff --git a/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor new file mode 100644 index 000000000..80f761773 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor @@ -0,0 +1,136 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Layout +@inherits LayoutComponentBase +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject IOptions Options +@inject NavigationManager NavigationManager + +
+ + +
+ @Body +
+
+ + + +@code{ + + ClaimsPrincipal? user; + + protected override async Task OnInitializedAsync() + { + user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + AuthenticationStateProvider.AuthenticationStateChanged += OnAuthenticationStateChanged; + await base.OnInitializedAsync(); + } + + async void OnAuthenticationStateChanged(Task authenticationStateTask) + { + user = (await authenticationStateTask).User; + this.StateHasChanged(); + } + + bool IsActive(string href, NavLinkMatch navLinkMatch = NavLinkMatch.Prefix) + { + var relativePath = NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLower(); + return navLinkMatch == NavLinkMatch.All ? relativePath == href.ToLower() : relativePath.StartsWith(href.ToLower()); + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.css b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.css new file mode 100644 index 000000000..8134c9484 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.css @@ -0,0 +1,16 @@ +.page.h-100 { + max-height: 100%; + overflow: hidden; +} + +.content { + max-height: 100%; + overflow: hidden; +} + +nav.header.navbar { + border-top: 1px solid; + border-image-slice: 1; + border-width: 1px; + border-image-source: linear-gradient(90deg, #8c68cd 0%, #7d59bd 100%); +} diff --git a/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.min.css b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.min.css new file mode 100644 index 000000000..1a3d9e106 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.min.css @@ -0,0 +1 @@ +.page.h-100{max-height:100%;overflow:hidden;}.content{max-height:100%;overflow:hidden;}nav.header.navbar{border-top:1px solid;border-image-slice:1;border-width:1px;border-image-source:linear-gradient(90deg,#8c68cd 0%,#7d59bd 100%);} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.scss b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.scss new file mode 100644 index 000000000..811d3d476 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor.scss @@ -0,0 +1,18 @@ +@import "../../wwwroot/css/theme/_variables.scss"; + +.page.h-100 { + max-height:100%; + overflow: hidden; +} + +.content { + max-height: 100%; + overflow: hidden; +} + +nav.header.navbar { + border-top: 1px solid; + border-image-slice: 1; + border-width: 1px; + border-image-source: linear-gradient(90deg, $primary-dark 0%, $primary-bg-subtle-dark 100%); +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Layout/RedirectToLogin.razor b/src/dashboard/Synapse.Dashboard/Layout/RedirectToLogin.razor new file mode 100644 index 000000000..c3fef9a01 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Layout/RedirectToLogin.razor @@ -0,0 +1,14 @@ +@namespace Synapse.Dashboard.Layout +@inject NavigationManager Navigation +@inject IOptions Options + +@code { + + protected override void OnInitialized() + { + Navigation.NavigateTo(this.Options.Value.Authentication.Oidc == null + ? "authentication/bearer/login" + : "authentication"); + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowActivityMappingConfiguration.cs b/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowActivityMappingConfiguration.cs deleted file mode 100644 index 749ae71ed..000000000 --- a/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowActivityMappingConfiguration.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowActivities; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Mapping.Configurations -{ - internal class V1WorkflowActivityMappingConfiguration - : IMappingConfiguration, - IMappingConfiguration - { - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(e => e.AggregateId, options => options.MapFrom(activity => activity.Id)); - } - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(activity => activity.Id, options => options.MapFrom(e => e.AggregateId)); - } - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowInstanceMappingConfiguration.cs b/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowInstanceMappingConfiguration.cs deleted file mode 100644 index a1ad896e3..000000000 --- a/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowInstanceMappingConfiguration.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; -using Neuroglia.Mapping; -using Synapse.Integration.Events.WorkflowInstances; -using Synapse.Integration.Models; -using System.Collections.ObjectModel; - -namespace Synapse.Dashboard.Mapping.Configurations -{ - internal class V1WorkflowInstanceMappingConfiguration - : IMappingConfiguration, - IMappingConfiguration - { - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(e => e.AggregateId, options => options.MapFrom(instance => instance.Id)); - } - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(instance => instance.Id, options => options.MapFrom(e => e.AggregateId)); - mapping.ForMember(instance => instance.Activities, options => options.MapFrom(e => new Collection())); - } - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowMappingConfiguration.cs b/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowMappingConfiguration.cs deleted file mode 100644 index 8c765ed97..000000000 --- a/src/dashboard/Synapse.Dashboard/Mapping/Configurations/V1WorkflowMappingConfiguration.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; -using Neuroglia.Mapping; -using Synapse.Integration.Events.Workflows; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Mapping.Configurations -{ - internal class V1WorkflowMappingConfiguration - : IMappingConfiguration, - IMappingConfiguration - { - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(e => e.AggregateId, options => options.MapFrom(instance => instance.Id)); - } - - void IMappingConfiguration.Configure(IMappingExpression mapping) - { - mapping.ForMember(instance => instance.Id, options => options.MapFrom(e => e.AggregateId)); - } - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Mapping/MappingProfile.cs b/src/dashboard/Synapse.Dashboard/Mapping/MappingProfile.cs deleted file mode 100644 index 84099bd3e..000000000 --- a/src/dashboard/Synapse.Dashboard/Mapping/MappingProfile.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using AutoMapper; -using Neuroglia.Mapping; - -namespace Synapse.Application.Mapping -{ - - /// - /// Represents the application mapping - /// - public class MappingProfile - : Profile - { - - /// - /// Initializes a new - /// - public MappingProfile() - { - this.AllowNullCollections = true; - this.MappingConfigurationTypes = new List(); - this.Initialize(); - } - - /// - /// Gets a containing the types of all existing s - /// - protected List MappingConfigurationTypes { get; } - - /// - /// Initializes the - /// - protected void Initialize() - { - foreach (Type mappingConfigurationType in this.GetType().Assembly - .GetTypes() - .Where(t => !t.IsAbstract && !t.IsInterface && t.IsClass && typeof(IMappingConfiguration).IsAssignableFrom(t))) - { - this.MappingConfigurationTypes.Add(mappingConfigurationType); - this.ApplyConfiguration((IMappingConfiguration)Activator.CreateInstance(mappingConfigurationType, new object[] { })!); - } - } - - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Models/UploadableFile.cs b/src/dashboard/Synapse.Dashboard/Models/UploadableFile.cs deleted file mode 100644 index 9416e08a4..000000000 --- a/src/dashboard/Synapse.Dashboard/Models/UploadableFile.cs +++ /dev/null @@ -1,77 +0,0 @@ -using Microsoft.AspNetCore.Components.Forms; -using Microsoft.AspNetCore.Http; -using System.Net.Http.Headers; - -namespace Synapse.Dashboard -{ - /// - /// Wraps a as - /// - public class UploadableFile - : IFormFile, IDisposable - { - /// - public string ContentType { get => this.Content.Headers.ContentType?.ToString() ?? ""; } - - /// - public string ContentDisposition { get => this.Content.Headers.ContentDisposition?.ToString() ?? ""; } - - /// - public IHeaderDictionary Headers { get; set; } - - /// - public long Length { get => this.BrowserFile.Size; } - - /// - public string Name { get => this.BrowserFile.Name; } - - /// - public string FileName { get => this.BrowserFile.Name; } - - /// - public Stream OpenReadStream() - { - return this.Content.ReadAsStream(); - } - - /// - public void CopyTo(Stream target) - { - this.Content.CopyTo(target, null, new CancellationToken()); - } - - /// - public async Task CopyToAsync(Stream target, CancellationToken cancellationToken = default(CancellationToken)) - { - await this.Content.CopyToAsync(target, cancellationToken); - } - - /// - public void Dispose() - { - this.Content.Dispose(); - } - - /// - /// The base - /// - private IBrowserFile BrowserFile { get; set; } - - /// - /// The content - /// - private StreamContent Content { get; set; } - - /// - /// Creates a new with the specified - /// - /// - public UploadableFile(IBrowserFile browserFile) - { - this.BrowserFile = browserFile; - this.Content = new StreamContent(this.BrowserFile.OpenReadStream()); - this.Content.Headers.ContentType = new MediaTypeHeaderValue(this.BrowserFile.ContentType); - this.Headers = null!; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Models/WorkflowOutcome.cs b/src/dashboard/Synapse.Dashboard/Models/WorkflowOutcome.cs deleted file mode 100644 index e9d93099f..000000000 --- a/src/dashboard/Synapse.Dashboard/Models/WorkflowOutcome.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia; -using ServerlessWorkflow.Sdk.Models; - -namespace Synapse.Dashboard -{ - /// - /// Represents a workflow activity outcome - /// - public class WorkflowOutcome - { - - /// - /// Initializes a new - /// - /// The outcome type - /// The outcome definition object - public WorkflowOutcome(WorkflowOutcomeType type, object? definition = null) - { - this.Type = type; - this.Definition = definition; - } - - /// - /// Gets the outcome type - /// - public WorkflowOutcomeType Type { get; } - - /// - /// Gets the outcome definition object - /// - public object? Definition { get; } - - /// - public override string ToString() - { - return this.Type switch - { - WorkflowOutcomeType.Transition => this.Definition == null ? "Transitions" : $"Transitions to {(this.Definition is string stateName ? stateName : ((TransitionDefinition)this.Definition!).NextState)}", - WorkflowOutcomeType.End => "Ends workflow", - _ => throw new NotSupportedException($"The specified {nameof(WorkflowOutcomeType)} '{EnumHelper.Stringify(this.Type)}' is not supported") - }; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Models/WorkflowOutcomeType.cs b/src/dashboard/Synapse.Dashboard/Models/WorkflowOutcomeType.cs deleted file mode 100644 index e8c77840d..000000000 --- a/src/dashboard/Synapse.Dashboard/Models/WorkflowOutcomeType.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Runtime.Serialization; - -namespace Synapse.Dashboard -{ - /// - /// Enumerates all supported workflow outcomes - /// - public enum WorkflowOutcomeType - { - /// - /// Indicates the end of the workflow - /// - [EnumMember(Value = "end")] - End, - /// - /// Indicates a transition to another state - /// - [EnumMember(Value = "transition")] - Transition - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/About/View.razor b/src/dashboard/Synapse.Dashboard/Pages/About/View.razor new file mode 100644 index 000000000..0e6e77a66 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/About/View.razor @@ -0,0 +1,78 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@namespace Synapse.Dashboard.Components +@page "/about" +@inject IBreadcrumbManager BreadcrumbManager + +Synapse - About + + +
+ +

SYNAPSE

+
+
+ +
+ + + + + + + + + + + + + + + + + + + +
Version + @typeof(MainLayout).Assembly.GetName().Version!.ToString(3) +
LicenseApache 2.0
CopyrightCopyright © 2024-Present The Synapse Authors. All Rights Reserved.
Repositoryhttps://github.com/serverlessworkflow/synapse/
+
+ +
+
+
+ GitHub logo +

+ A question, an idea, want to contribute? +
+ Join in the fun on GitHub! + New contributors are welcome! +

+
+
+
+ +@code { + /// + protected override void OnInitialized() + { + base.OnInitialized(); + BreadcrumbManager.Use(Breadcrumbs.About); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Application/Info.razor b/src/dashboard/Synapse.Dashboard/Pages/Application/Info.razor deleted file mode 100644 index b215829dd..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Application/Info.razor +++ /dev/null @@ -1,273 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/application/info" -@using Synapse.Dashboard.Features -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbService - -Application Info - -@if(applicationInfo != null) -{ -

- Synapse logo -

SYNAPSE

-

Serverless Workflow Management System

-
- -
- -
General Info
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name@applicationInfo.Name
Version - @applicationInfo.Version -
Operational System@applicationInfo.OSDescription
NET Framework@applicationInfo.FrameworkDescription
Environment@applicationInfo.EnvironmentName
Serverless Workflow SDK@applicationInfo.ServerlessWorkflowSdkVersion
Workflow Runtime@applicationInfo.WorkflowRuntimeName
Supported Runtime Expression Languages@((MarkupString)string.Join(',', applicationInfo.SupportedRuntimeExpressionLanguages.Select(l => $@"{l}")))
LicenseApache 2.0
CopyrightCopyright © 2022-Present The Synapse Authors. All Rights Reserved.
Repositoryhttps://github.com/serverlessworkflow/synapse/
Websitehttps://synapse-wfms.io/
- -
- -
-
Environment Variables
-
- - - - - - - - - - @foreach(var env in applicationInfo.EnvironmentVariables) - { - - - - - } - -
NameValue
@env.Key@env.Value
- -
- -
-
Plugins
-
- - @if(applicationInfo.Plugins == null - || !applicationInfo.Plugins.Any()) - { - No plugins detected - } - else - { - foreach (var plugin in applicationInfo.Plugins) - { -
- -
@plugin.Metadata.Name@plugin.Metadata.Version@EnumHelper.Stringify(plugin.Metadata.Type)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @if(plugin.Metadata.LicenseUri != null) - { - - - - - } - @if(plugin.Metadata.ReadmeUri != null) - { - - - - - } - @if(plugin.Metadata.WebsiteUri != null) - { - - - - - } - @if(plugin.Metadata.RepositoryUri != null) - { - - - - - } - -
Type@EnumHelper.Stringify(plugin.Metadata.Type)
Name@plugin.Metadata.Name
Description@plugin.Metadata.Description
Version@plugin.Metadata.Version
Authors@plugin.Metadata.Authors
Copyright@plugin.Metadata.Copyright
Tags@((MarkupString)string.Join(',', plugin.Metadata.Tags.Select(l => $@"{l}")))
Location@plugin.Location
Loaded@plugin.IsLoaded
License@plugin.Metadata.LicenseUri
Readme@plugin.Metadata.ReadmeUri
Website@plugin.Metadata.WebsiteUri
Repository@plugin.Metadata.RepositoryUri
- -
-
- } - } - -
-
-
-
-
-
-
- CNCF logo -

Synapse is a Cloud Native Computing Foundation project

-
-
-
-
- Serverless Workflow logo -

Synapse is a runtime implementation of the Serverless Workflow spec

-
-
-
-
- Github logo -

- A question, an idea, wanting to contribute? -
- Join in the fun on Github! - New users are always welcome! -

-
-
-
-
- -} -else -{ - -} - -@code { - - protected IDisposable? subscription = null!; - private V1ApplicationInfo applicationInfo = null!; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.About); - this.subscription = this.Feature - .Subscribe(applicationState => - { - this.applicationInfo = applicationState.Info!; - this.StateHasChanged(); - }); - this.Dispatcher.Dispatch(new GetV1ApplicationInfo()); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - this.subscription?.Dispose(); - this.subscription = null; - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Authentication/Bearer/Login.razor b/src/dashboard/Synapse.Dashboard/Pages/Authentication/Bearer/Login.razor new file mode 100644 index 000000000..a5d25c8e6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Authentication/Bearer/Login.razor @@ -0,0 +1,57 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/authentication/bearer/login" +@layout EmptyLayout +@inject ISecurityTokenManager SecurityTokenManager +@inject IJSRuntime JsRuntime +@inject NavigationManager NavigationManager +@inject ApplicationAuthenticationStateProvider AuthenticationStateManager + +Token Authentication +
+
+
+ +

SYNAPSE

+
+ + +
+
+ +@code { + + ElementReference logininput; + string? token; + + async Task OnLoginAsync() + { + if (string.IsNullOrWhiteSpace(token)) return; + await SecurityTokenManager.SetTokenAsync(token); + AuthenticationStateManager.NotifyNotifyAuthenticationStateChanged(); + NavigationManager.NavigateTo("/"); + } + + async Task OnEnterKeyDown(KeyboardEventArgs e) + { + if (e.Key != "Enter") return; + await this.OnLoginAsync(); + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Authentication/Oidc/Oidc.razor b/src/dashboard/Synapse.Dashboard/Pages/Authentication/Oidc/Oidc.razor new file mode 100644 index 000000000..5ed6deb91 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Authentication/Oidc/Oidc.razor @@ -0,0 +1,26 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/authentication/oidc/{action}" +@layout EmptyLayout + + + +@code { + + [Parameter] public string? Action { get; set; } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Authentication/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Authentication/View.razor new file mode 100644 index 000000000..47e935a84 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Authentication/View.razor @@ -0,0 +1,46 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/authentication" +@inject IOptions Options + +Authentication + + + diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Actions.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Actions.cs deleted file mode 100644 index c691bc91b..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Actions.cs +++ /dev/null @@ -1,350 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Correlations.Create; - -///

-/// Represents the Flux action used to initialize the -/// -public class InitializeState -{ - - /// - /// Initializes a new - /// - /// A boolean indicating whether or not to reset the state if it has already been initialized - public InitializeState(bool reset = false) - { - this.Reset = reset; - } - - /// - /// Gets a boolean indicating whether or not to reset the state if it has already been initialized - /// - public bool Reset { get; } - -} - -/// -/// Represents the Flux action used to create a new -/// -public class CreateCorrelation -{ - - /// - /// Initializes a new - /// - /// An object that describes the command to execute - public CreateCorrelation(V1CreateCorrelationCommand command) - { - this.Command = command; - } - - /// - /// Gets an object that describes the command to execute - /// - public V1CreateCorrelationCommand Command { get; } - -} - -/// -/// Represents a Flux action used to handle the differed result of a action -/// -public class HandleCreateCorrelationResult -{ - - /// - /// Initializes a new - /// - /// The newly created , if any - public HandleCreateCorrelationResult(V1Correlation result) - { - this.Result = result; - } - - /// - /// Gets the newly created , if any - /// - public V1Correlation Result { get; } - -} - -/// -/// Represents the Flux action used to patch the to create -/// -public class PatchCorrelation -{ - - /// - /// Initializes a new - /// - /// The used to patch the - public PatchCorrelation(Action patch) - { - this.Patch = patch; - } - - /// - /// Gets the used to patch the - /// - public Action Patch { get; } - -} - -/// -/// Represents the Flux action to add a new to the to create -/// -public class AddConditionToCorrelation -{ - - - -} - -/// -/// Represents the Flux action to remove a from the to create -/// -public class RemoveConditionFromCorrelation -{ - - /// - /// Initializes a new - /// - /// The to remove - public RemoveConditionFromCorrelation(V1CorrelationCondition condition) - { - this.Condition = condition; - } - - /// - /// Gets the to remove - /// - public V1CorrelationCondition Condition { get; } - -} - -/// -/// Represents the Flux action to add a new to a of the to create -/// -public class AddFilterToCorrelationCondition -{ - - /// - /// Initializes a new - /// - /// The to add a new to - public AddFilterToCorrelationCondition(V1CorrelationCondition condition) - { - this.Condition = condition; - } - - /// - /// Gets the to add a new to - /// - public V1CorrelationCondition Condition { get; } - -} - -/// -/// Represents the Flux action to remove a from a of the to create -/// -public class RemoveFilterFromCorrelationCondition -{ - - /// - /// Initializes a new - /// - /// The to remove the specified from - /// The to remove from the specified - public RemoveFilterFromCorrelationCondition(V1CorrelationCondition condition, V1EventFilter filter) - { - this.Condition = condition; - this.Filter = filter; - } - - /// - /// Gets the to remove the specified from - /// - public V1CorrelationCondition Condition { get; } - - /// - /// Gets the to remove from the specified - /// - public V1EventFilter Filter { get; } - -} - -/// -/// Represents the Flux action used to add or update the specified 's attribute -/// -public class AddOrUpdateConditionFilterAttribute -{ - - /// - /// Initializes a new - /// - /// The the to patch belongs to - /// The to patch - /// The name of the attribute to add or update - /// The value of the attribute to add or update - public AddOrUpdateConditionFilterAttribute(V1CorrelationCondition condition, V1EventFilter filter, string attributeName, string attributeValue) - { - this.Condition = condition; - this.Filter = filter; - this.AttributeName = attributeName; - this.AttributeValue = attributeValue; - } - - /// - /// Gets the the to patch belongs to - /// - public V1CorrelationCondition Condition { get; } - - /// - /// Gets the to patch - /// - public V1EventFilter Filter { get; } - - /// - /// Gets the name of the attribute to add or update - /// - public string AttributeName { get; } - - /// - /// Gets the value of the attribute to add or update - /// - public string AttributeValue { get; } - -} - -/// -/// Represents the Flux action used to remove an attribute from a -/// -public class RemoveAttributeFromConditionFilter -{ - - /// - /// Initializes a new - /// - /// The the to patch belongs to - /// The to patch - /// The name of the attribute to remove - public RemoveAttributeFromConditionFilter(V1CorrelationCondition condition, V1EventFilter filter, string attributeName) - { - this.Condition = condition; - this.Filter = filter; - this.AttributeName = attributeName; - } - - /// - /// Gets the the to patch belongs to - /// - public V1CorrelationCondition Condition { get; } - - /// - /// Gets the to patch - /// - public V1EventFilter Filter { get; } - - /// - /// Gets the name of the attribute to remove - /// - public string AttributeName { get; } - -} - -/// -/// Represents the Flux action used to add or update the specified 's correlation mapping -/// -public class AddOrUpdateConditionFilterCorrelationMapping -{ - - /// - /// Initializes a new - /// - /// The the to patch belongs to - /// The to patch - /// The name of the attribute to add or update - /// The value of the correlation mapping to add or update - public AddOrUpdateConditionFilterCorrelationMapping(V1CorrelationCondition condition, V1EventFilter filter, string attributeName, string attributeValue) - { - this.Condition = condition; - this.Filter = filter; - this.AttributeName = attributeName; - this.AttributeValue = attributeValue; - } - - /// - /// Gets the the to patch belongs to - /// - public V1CorrelationCondition Condition { get; } - - /// - /// Gets the to patch - /// - public V1EventFilter Filter { get; } - - /// - /// Gets the name of the attribute to add or update - /// - public string AttributeName { get; } - - /// - /// Gets the value of the attribute to add or update - /// - public string AttributeValue { get; } - -} - -/// -/// Represents the Flux action used to remove a correlation mapping from a -/// -public class RemoveCorrelationMappingFromConditionFilter -{ - - /// - /// Initializes a new - /// - /// The the to patch belongs to - /// The to patch - /// The name of the attribute to remove - public RemoveCorrelationMappingFromConditionFilter(V1CorrelationCondition condition, V1EventFilter filter, string attributeName) - { - this.Condition = condition; - this.Filter = filter; - this.AttributeName = attributeName; - } - - /// - /// Gets the the to patch belongs to - /// - public V1CorrelationCondition Condition { get; } - - /// - /// Gets the to patch - /// - public V1EventFilter Filter { get; } - - /// - /// Gets the name of the attribute to remove - /// - public string AttributeName { get; } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Effects.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Effects.cs deleted file mode 100644 index 63e486943..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Effects.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; - -namespace Synapse.Dashboard.Pages.Correlations.Create; - -///

-/// Defines Flux effects for -related actions -/// -[Effect] -public static class Effects -{ - - /// - /// Handles the specified action - /// - /// The action to handle - /// The current - /// A new awaitable - public static async Task On(CreateCorrelation action, IEffectContext context) - { - try - { - var api = context.Services.GetRequiredService(); - var correlation = await api.CreateCorrelationAsync(action.Command); - context.Services.GetRequiredService().NavigateTo($"/correlations/{correlation.Id}"); - } - catch(Exception ex) - { - context.Services.GetRequiredService>().LogError("An error occured while creating a new correlation: {ex}", ex); - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Reducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Reducers.cs deleted file mode 100644 index 46086101c..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Reducers.cs +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Correlations.Create; - -///

-/// Defines Flux reducers for -related actions -/// -[Reducer] -public static class Reducers -{ - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, InitializeState action) - { - if (state.Initialized && !action.Reset) return state with { Saving = false }; - return state with - { - Initialized = true, - Saving = false, - Command = new() - { - ActivationType = V1CorrelationActivationType.Explicit, - Conditions = new List() - { - new() - { - Filters = new List() - { - new() - { - Attributes = new(), - CorrelationMappings = new() - } - } - } - }, - Outcome = new() - { - Type = V1CorrelationOutcomeType.Start - } - } - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, AddConditionToCorrelation action) - { - if (state.Command == null) return state; - var command = state.Command.Clone()!; - if (command.Conditions == null) command.Conditions = new List(); - command.Conditions.Add(new() { Filters = new List() { new() } }); - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, RemoveConditionFromCorrelation action) - { - if (state.Command == null) return state; - var conditionIndex = state.Command.Conditions.ToList().IndexOf(action.Condition); - if (conditionIndex < 0) return state; - var command = state.Command.Clone()!; - if (command.Conditions == null) return state; - var condition = command.Conditions.ElementAt(conditionIndex); - command.Conditions.Remove(condition); - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, AddFilterToCorrelationCondition action) - { - if (state.Command == null) return state; - var conditionIndex = state.Command.Conditions.ToList().IndexOf(action.Condition); - if(conditionIndex < 0) return state; - var command = state.Command.Clone()!; - var condition = command.Conditions.ElementAt(conditionIndex); - if (condition.Filters == null) condition.Filters = new List(); - condition.Filters.Add(new() { Attributes = new(), CorrelationMappings = new() }); - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, RemoveFilterFromCorrelationCondition action) - { - if (state.Command == null) return state; - var conditionIndex = state.Command.Conditions.ToList().IndexOf(action.Condition); - if (conditionIndex < 0) return state; - var filterIndex = state.Command.Conditions.ElementAt(conditionIndex).Filters.ToList().IndexOf(action.Filter); - if (filterIndex < 0) return state; - var command = state.Command.Clone()!; - var condition = command.Conditions.ElementAt(conditionIndex); - if (condition.Filters == null) return state; - var filter = condition.Filters.ElementAt(filterIndex); - condition.Filters.Remove(filter); - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, PatchCorrelation action) - { - if (state.Command == null) return state; - var command = state.Command.Clone()!; - action.Patch(command); - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, AddOrUpdateConditionFilterAttribute action) - { - if (state.Command == null) return state; - var conditionIndex = state.Command.Conditions.ToList().IndexOf(action.Condition); - if (conditionIndex < 0) return state; - var filterIndex = state.Command.Conditions.ElementAt(conditionIndex).Filters.ToList().IndexOf(action.Filter); - if (filterIndex < 0) return state; - var command = state.Command.Clone()!; - var condition = command.Conditions.ElementAt(conditionIndex); - if (condition.Filters == null) return state; - var filter = condition.Filters.ElementAt(filterIndex); - if (filter.Attributes == null) filter.Attributes = new(); - filter.Attributes[action.AttributeName.ToLowerInvariant()] = action.AttributeValue; - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, RemoveAttributeFromConditionFilter action) - { - if (state.Command == null) return state; - var conditionIndex = state.Command.Conditions.ToList().IndexOf(action.Condition); - if (conditionIndex < 0) return state; - var filterIndex = state.Command.Conditions.ElementAt(conditionIndex).Filters.ToList().IndexOf(action.Filter); - if (filterIndex < 0) return state; - var command = state.Command.Clone()!; - var condition = command.Conditions.ElementAt(conditionIndex); - if (condition.Filters == null) return state; - var filter = condition.Filters.ElementAt(filterIndex); - if (!filter.Attributes.Remove(action.AttributeName)) return state; - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, AddOrUpdateConditionFilterCorrelationMapping action) - { - if (state.Command == null) return state; - var conditionIndex = state.Command.Conditions.ToList().IndexOf(action.Condition); - if (conditionIndex < 0) return state; - var filterIndex = state.Command.Conditions.ElementAt(conditionIndex).Filters.ToList().IndexOf(action.Filter); - if (filterIndex < 0) return state; - var command = state.Command.Clone()!; - var condition = command.Conditions.ElementAt(conditionIndex); - if (condition.Filters == null) return state; - var filter = condition.Filters.ElementAt(filterIndex); - if (filter.CorrelationMappings == null) filter.CorrelationMappings = new(); - filter.CorrelationMappings[action.AttributeName.ToLowerInvariant()] = action.AttributeValue; - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, RemoveCorrelationMappingFromConditionFilter action) - { - if (state.Command == null) return state; - var conditionIndex = state.Command.Conditions.ToList().IndexOf(action.Condition); - if (conditionIndex < 0) return state; - var filterIndex = state.Command.Conditions.ElementAt(conditionIndex).Filters.ToList().IndexOf(action.Filter); - if (filterIndex < 0) return state; - var command = state.Command.Clone()!; - var condition = command.Conditions.ElementAt(conditionIndex); - if (condition.Filters == null) return state; - var filter = condition.Filters.ElementAt(filterIndex); - if (!filter.CorrelationMappings.Remove(action.AttributeName)) return state; - return state with { Command = command }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, CreateCorrelation action) - { - return state with { Saving = true }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateCorrelationState On(CreateCorrelationState state, HandleCreateCorrelationResult action) - { - return state with { Saving = false }; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Selectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Selectors.cs deleted file mode 100644 index bac3a7798..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/Selectors.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Correlations.Create; - -///

-/// Defines -related selectors -/// -public static class Selectors -{ - - /// - /// Selects the to create - /// - /// The current - /// A new - public static IObservable SelectCreateCorrelationCommand(IStore store) - { - return store.GetFeature() - .Select(state => state.Command) - .DistinctUntilChanged(); - } - - /// - /// Selects a boolean indicating whether or not the is being saved - /// - /// The current - /// A new - public static IObservable SelectIsCorrelationBeingSaved(IStore store) - { - return store.GetFeature() - .Select(state => state.Saving) - .DistinctUntilChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/State.cs deleted file mode 100644 index 79af6f699..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/State.cs +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Commands.Correlations; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Correlations.Create; - -///

-/// Represents the Flux state that holds the information required by the '/correlations/new' view -/// -[Feature] -public record CreateCorrelationState -{ - - /// - /// Gets a boolean indicating whether or not the has been initialized - /// - public bool Initialized { get; init; } - - /// - /// Gets a boolean indicating whether or not the to create is being saved - /// - public bool Saving { get; init; } - - /// - /// Gets an object that describes the command used to create a new - /// - public V1CreateCorrelationCommand? Command { get; init; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/View.razor deleted file mode 100644 index 294a5aaff..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/Create/View.razor +++ /dev/null @@ -1,315 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/correlations/new" -@using System.Reactive.Subjects -@using System.Reactive.Linq -@using Synapse.Integration.Commands.Correlations -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbManager -@inject NavigationManager NavigationManager - -New correlation - -@if(saving || command == null) -{ - -} -else -{ - - - - - -
- -
-
- -
-
-
-

Information

-
-
- - - - - - - - - - - - - - - - - - - -
Activation@EnumHelper.Stringify(command.ActivationType)
Lifetime - -
Condition Type - -
Outcome - - - - - - - - - - - -
Type@EnumHelper.Stringify(command.Outcome.Type)
Target - -
-
-
-
- -
-
-

Conditions

-
-
- @for (int i = 0; i < command.Conditions.Count; i++) - { - var conditionIndex = i; - var condition = command.Conditions.ElementAt(conditionIndex); - -
-
-
-
Condition @(conditionIndex + 1)
-
-
- -
-
-
- -
-
Filters
- @for (int j = 0; j < condition.Filters.Count; j++) - { - var filterIndex = j; - var filter = condition.Filters.ElementAt(filterIndex); - -
-
-
-
Filter @(filterIndex + 1)
-
-
- -
-
-
- -
- Context Attributes - -
- - - @if (filter.Attributes != null - && filter.Attributes.Any()) - { - foreach (var attr in filter.Attributes) - { - - - - - - } - } - -
- -
-
-
- Correlation Mappings - -
- - - @if (filter.CorrelationMappings != null - && filter.CorrelationMappings.Any()) - { - foreach (var mapping in filter.CorrelationMappings) - { - - - - - - } - } - -
- -
-
- -
- } - -
- -
- } -
- -
-
- -} - -@code { - - private Subject? disposeNotifier; - private V1CreateCorrelationCommand? command; - private V1WorkflowReference? targetWorkflowRef => string.IsNullOrWhiteSpace(this.command?.Outcome.Target) ? null : V1WorkflowReference.Parse(this.command.Outcome.Target); - private bool saving; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.disposeNotifier = new Subject(); - Selectors.SelectCreateCorrelationCommand(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(command => - { - this.command = command; - this.StateHasChanged(); - }); - Selectors.SelectIsCorrelationBeingSaved(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(saving => - { - this.saving = saving; - this.StateHasChanged(); - }); - await this.BreadcrumbManager.Use(Breadcrumbs.CreateCorrelation); - this.Dispatcher.Dispatch(new InitializeState()); - } - - private void OnPropertyChanged(Action patch) - { - this.Dispatcher.Dispatch(new PatchCorrelation(patch)); - } - - private void OnAddOrUpdateConditionFilterAttribute(V1CorrelationCondition condition, V1EventFilter filter, string attributeName, string attributeValue) - { - this.Dispatcher.Dispatch(new AddOrUpdateConditionFilterAttribute(condition, filter, attributeName, attributeValue)); - } - - private void OnRemoveAttributeFromConditionFilter(V1CorrelationCondition condition, V1EventFilter filter, string attributeName) - { - this.Dispatcher.Dispatch(new RemoveAttributeFromConditionFilter(condition, filter, attributeName)); - } - - private void OnAddOrUpdateConditionFilterCorrelationMapping(V1CorrelationCondition condition, V1EventFilter filter, string attributeName, string attributeValue) - { - this.Dispatcher.Dispatch(new AddOrUpdateConditionFilterCorrelationMapping(condition, filter, attributeName, attributeValue)); - } - - private void OnRemoveCorrelationMappingFromConditionFilter(V1CorrelationCondition condition, V1EventFilter filter, string attributeName) - { - this.Dispatcher.Dispatch(new RemoveCorrelationMappingFromConditionFilter(condition, filter, attributeName)); - } - - private void OnAddFilterToCondition(V1CorrelationCondition condition) - { - this.Dispatcher.Dispatch(new AddFilterToCorrelationCondition(condition)); - } - - private void OnRemoveFilterFromCondition(V1CorrelationCondition condition, V1EventFilter filter) - { - this.Dispatcher.Dispatch(new RemoveFilterFromCorrelationCondition(condition, filter)); - } - - private void OnAddCondition() - { - this.Dispatcher.Dispatch(new AddConditionToCorrelation()); - } - - private void OnRemoveCondition(V1CorrelationCondition condition) - { - this.Dispatcher.Dispatch(new RemoveConditionFromCorrelation(condition)); - } - - private void OnNavigateToCorrelationList() - { - this.NavigationManager.NavigateTo("/correlations"); - } - - private void OnSaveCorrelation() - { - if(this.command == null) return; - this.Dispatcher.Dispatch(new CreateCorrelation(this.command)); - } - - private void OnReset() - { - this.Dispatcher.Dispatch(new InitializeState(true)); - } - - protected override void Dispose(bool disposing) - { - if (!disposing) return; - if (this.disposeNotifier != null) - { - this.disposeNotifier.OnNext(true); - this.disposeNotifier.OnCompleted(); - this.disposeNotifier.Dispose(); - this.disposeNotifier = null; - } - base.Dispose(disposing); - } - - class Attribute - { - - public string Name { get; set; } = null!; - - public string Value { get; set; } = null!; - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.css b/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.css deleted file mode 100644 index 5f282702b..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.css +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.min.css b/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.min.css deleted file mode 100644 index 5f282702b..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.min.css +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.razor b/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.razor deleted file mode 100644 index 7b8c34a01..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.razor +++ /dev/null @@ -1,132 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/correlations" -@using Neuroglia.Data -@using Neuroglia.Data.Flux -@using Synapse.Integration.Models -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IServiceProvider ServiceProvider -@inject IBreadcrumbManager BreadcrumbService -@inject NavigationManager NavigationManager - -Correlations - - - - - - - -

-
- -
-
- - - - - @EnumHelper.Stringify(correl.Item.ActivationType) - - - - - @EnumHelper.Stringify(correl.Item.Outcome.Type) - - - - - - @EnumHelper.Stringify(correl.Item.ConditionType) - - - - - @EnumHelper.Stringify(correl.Item.Lifetime) - - - - - - -
- -@code { - - protected IDisposable? subscription = null!; - private List? correlations; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.Correlations); - this.subscription = V1CorrelationCollectionSelectors - .SelectCurrentCorrelations(this.Store) - .Subscribe(correlations => - { - this.correlations = correlations; - this.StateHasChanged(); - }); - this.Dispatcher.Dispatch(new ListV1Correlations()); - } - - void OnSearchCorrelations(string term) - { - this.Dispatcher.Dispatch(new SearchV1Correlations(term)); - } - - void OnClearCorrelationSearch() - { - this.Dispatcher.Dispatch(new ListV1Correlations()); - } - - void OnViewCorrelation(V1Correlation correlation) - { - this.NavigationManager.NavigateTo($"/correlations/{correlation.Id}"); - } - - void OnNewCorrelation() - { - this.NavigationManager.NavigateTo("/correlations/new"); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - this.subscription?.Dispose(); - this.subscription = null; - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.scss b/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.scss deleted file mode 100644 index 5f282702b..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List.scss +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/List/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Correlations/List/View.razor new file mode 100644 index 000000000..144459878 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Correlations/List/View.razor @@ -0,0 +1,199 @@ +@page "/correlations" +@attribute [Authorize] +@namespace Synapse.Dashboard.Pages.Correlations.List +@inherits NamespacedResourceManagementComponent +@inject IBreadcrumbManager BreadcrumbManager + +Correlations + +
+ @if (Loading) + { + + } +
+

Correlations

+ @(Resources?.Count ?? 0) items +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + @if (Resources != null && Resources.Any()) + { + + + + + + + + + + + + + + + + + + } + +
NamespaceNameCreation TimeLast ModifiedStatusLifetimeConsumption StrategyEventsOutcome TypeOutcome TargetContexts + +
@correlation.Metadata.Namespace@correlation.Metadata.Name@correlation.Metadata.CreationTimestamp?.DateTime.RelativeFormat()@correlation.Status?.LastModified?.DateTime.RelativeFormat()@correlation.Status?.Phase@correlation.Spec.Lifetime@GetEventConsumptionStrategy(correlation)@GetFilterCount(correlation)@GetOutcomeType(correlation)@GetOutcomeTarget(correlation)@correlation.Status?.Contexts.Count + + + + +
+
+ + + + + + + + + +@code{ + + string GetStatusPhaseTitle(string? phase) + { + return phase switch + { + CorrelationStatusPhase.Active => "The correlation has been picked up by a correlator and is actively correlation ingested events", + CorrelationStatusPhase.Inactive => "The correlation is inactive and is not correlating events", + CorrelationStatusPhase.Cancelled => "The correlation has been cancelled", + CorrelationStatusPhase.Completed => "The correlation has been completed", + _ => "Unknown/unsupported status phase" + }; + } + + string GetLifetimeTitle(string lifetime) + { + return lifetime switch + { + CorrelationLifetime.Durable => "A durable, multi-use correlation", + CorrelationLifetime.Ephemeral => "A single use correlation", + _ => "Unknown/unsupported correlation lifetime" + }; + } + + string GetEventConsumptionStrategyTitle(Correlation correlation) + { + if (correlation.Spec.Events.All != null) return "Consumes all the defined events"; + else if (correlation.Spec.Events.Any != null) return "Consumes any of the defined events"; + else return "Consumes a single event"; + } + + string GetOutcomeTypeTitle(Correlation correlation) + { + if (correlation.Spec.Outcome.Start != null) return "Starts a new instance workflow of the specified workflow"; + else return "Correlate ingested events to a specific workflow instance"; + } + + string GetOutcomeTargetTitle(Correlation correlation) + { + if (correlation.Spec.Outcome.Start != null) return "The qualified name of the workflow to start"; + else return "The qualified name of the workflow instance to correlate events to"; + } + + string GetStatusPhaseClass(Correlation correlation) + { + return correlation.Status?.Phase switch + { + CorrelationStatusPhase.Active => "primary", + CorrelationStatusPhase.Cancelled => "warning", + CorrelationStatusPhase.Completed => "success", + _ => "secondary" + }; + } + + string GetEventConsumptionStrategy(Correlation correlation) + { + if (correlation.Spec.Events.All != null) return "all"; + else if (correlation.Spec.Events.Any != null) return "any"; + else return "one"; + } + + int GetFilterCount(Correlation correlation) + { + if (correlation.Spec.Events.All != null) return correlation.Spec.Events.All.Count; + else if (correlation.Spec.Events.Any != null) return correlation.Spec.Events.Any.Count; + else return 1; + } + + string GetOutcomeType(Correlation correlation) + { + if (correlation.Spec.Outcome.Start != null) return "start"; + else return "correlate"; + } + + string GetOutcomeTarget(Correlation correlation) + { + if (correlation.Spec.Outcome.Start != null) return correlation.Spec.Outcome.Start.Workflow.ToString(); + else return correlation.Spec.Outcome.Correlate!.Instance; + } + + string GetOutcomeTargetRef(Correlation correlation) + { + if (correlation.Spec.Outcome.Start != null) + { + var definitionSegments = correlation.Spec.Outcome.Start.Workflow.ToString().Split(':'); + var version = definitionSegments.Last(); + var nameNs = definitionSegments.First(); + var segments = nameNs.Split('.'); + var ns = segments.Last(); + var name = segments.Take(segments.Count() - 1).Join('.'); + return $"/workflows/details/{ns}/{name}/{version}"; + } + else return $"/workflow-instances/{correlation.Spec.Outcome.Correlate!.Instance}"; + } + + /// + protected override void OnInitialized() + { + base.OnInitialized(); + BreadcrumbManager.Use(Breadcrumbs.Correlations); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Actions.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Actions.cs deleted file mode 100644 index 5c4fcadad..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Actions.cs +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Newtonsoft.Json.Schema; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Correlations.View; - -///

-/// Represents the Flux action used to retrieve a by id -/// -public class GetCorrelationById -{ - - /// - /// Initializes a new - /// - /// The id of the to get - public GetCorrelationById(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to get - /// - public string Id { get; } - -} - -/// -/// Represents the Flux action used to handle the differed result of a action -/// -public class HandleGetCorrelationByIdResult -{ - - /// - /// Initializes a new - /// - /// The differed result of a action - public HandleGetCorrelationByIdResult(V1Correlation? result) - { - this.Result = result; - } - - /// - /// Gets the differed result of a action - /// - public V1Correlation? Result { get; } - -} - -/// -/// Represents the Flux action used to display the modal used to publish a new -/// -public class ShowPublishCloudEventModal -{ - - /// - /// Initializes a new - /// - /// The to produce - public ShowPublishCloudEventModal(V1Event e) - { - this.Event = e; - } - - /// - /// Initializes a new - /// - /// The to publish a new for - public ShowPublishCloudEventModal(V1CorrelationCondition condition) - { - var e = V1Event.Create(); - var filter = condition.Filters.FirstOrDefault(); - if(filter != null) - { - foreach (var attribute in filter.Attributes) - { - e.SetAttribute(attribute.Key, attribute.Value); - } - foreach (var attribute in filter.CorrelationMappings) - { - if (e.Attributes.ContainsKey(attribute.Key) && string.IsNullOrWhiteSpace(attribute.Value)) continue; - e.SetAttribute(attribute.Key, attribute.Value); - } - } - this.Event = e; - } - - /// - /// Gets the to produce - /// - public V1Event Event { get; } - -} - -/// -/// Represents the Flux action used to hide the publish cloud event modal -/// -public class HidePublishCloudEventModal -{ - - /// - /// Initializes a new - /// - /// A boolean indicating whether or not to reset the modal's fields - public HidePublishCloudEventModal(bool reset = false) - { - this.Reset = reset; - } - - /// - /// Gets a boolean indicating whether or not to reset the modal's fields - /// - public bool Reset { get; } - -} - -/// -/// Represents the Flux action used to delete an existing -/// -public class DeleteCorrelation -{ - - /// - /// Initializes a new - /// - /// The id of the to delete - public DeleteCorrelation(string correlationId) - { - this.CorrelationId = correlationId; - } - - /// - /// Gets the id of the to delete - /// - public string CorrelationId { get; } - -} - -/// -/// Represents the Flux action used to handle the differed result of a action -/// -public class HandleDeleteCorrelationResult -{ - -} - -/// -/// Represents the Flux action used to delete a 's -/// -public class DeleteCorrelationContext -{ - - /// - /// Initializes a new - /// - /// The id of the that owns the to delete - /// The id of the to delete - public DeleteCorrelationContext(string correlationId, string contextId) - { - this.CorrelationId = correlationId; - this.ContextId = contextId; - } - - /// - /// Gets the id of the that owns the to delete - /// - public string CorrelationId { get; } - - /// - /// Gets the id of the to delete - /// - public string ContextId { get; } - -} - -/// -/// Represents the Flux action used to handle the differed result of a action -/// -public class HandleDeleteCorrelationContextResult -{ - -} - -/// -/// Represents the Flux action used to delete a correlated to a specified -/// -public class DeleteCorrelatedEvent -{ - - /// - /// Initializes a new - /// - /// The id of the the to delete belongs to - /// The id of the the to delete belongs to - /// The id of the correlated to delete - public DeleteCorrelatedEvent(string correlationId, string contextId, string eventId) - { - this.CorrelationId = correlationId; - this.ContextId = contextId; - this.EventId = eventId; - } - - /// - /// Gets the id of the the to delete belongs to - /// - public string CorrelationId { get; } - - /// - /// Gets the id of the the to delete belongs to - /// - public string ContextId { get; } - - /// - /// Gets the id of the correlated to delete - /// - public string EventId { get; } - -} - -/// -/// Represents the Flux action used to handle the differed result of a action -/// -public class HandleDeleteCorrelatedEventResult -{ - - /// - /// Initializes a new - /// - /// The id of the the deleted belongs to - /// The id of the the deleted belongs to - /// The id of the deleted correlated - public HandleDeleteCorrelatedEventResult(string correlationId, string contextId, string eventId) - { - this.CorrelationId = correlationId; - this.ContextId = contextId; - this.EventId = eventId; - } - - /// - /// Gets the id of the the deleted belongs to - /// - public string CorrelationId { get; } - - /// - /// Gets the id of the the deleted belongs to - /// - public string ContextId { get; } - - /// - /// Gets the id of the deleted correlated - /// - public string EventId { get; } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Effects.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Effects.cs deleted file mode 100644 index 01a3c68bd..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Effects.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Microsoft.AspNetCore.Components; -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; - -namespace Synapse.Dashboard.Pages.Correlations.View; - -///

-/// Defines Flux effects for -related actions -/// -[Effect] -public static class Effects -{ - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(GetCorrelationById action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - try - { - var correlation = await api.GetCorrelationByIdAsync(action.Id); - context.Dispatcher.Dispatch(new HandleGetCorrelationByIdResult(correlation)); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError("An error occured while retrieving the correlation with the specified id '{correlationId}': {ex}", action.Id, ex); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(DeleteCorrelation action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - try - { - await api.DeleteCorrelationAsync(action.CorrelationId); - context.Dispatcher.Dispatch(new HandleDeleteCorrelationResult()); - context.Services.GetRequiredService().NavigateTo("/correlations"); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError("An error occured while deleting the correlation with id '{correlationId}': {ex}", action.CorrelationId, ex); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(DeleteCorrelationContext action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - try - { - await api.DeleteCorrelationContextAsync(action.CorrelationId, action.ContextId); - context.Dispatcher.Dispatch(new HandleDeleteCorrelationContextResult()); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError("An error occured while deleting the context with id '{correlationContextId}', owned by the correlation with id '{correlationId}': {ex}", action.ContextId, action.CorrelationId, ex); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(DeleteCorrelatedEvent action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - try - { - await api.DeleteCorrelationContextEventAsync(action.CorrelationId, action.ContextId, action.EventId); - context.Dispatcher.Dispatch(new HandleDeleteCorrelatedEventResult(action.CorrelationId, action.ContextId, action.EventId)); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError("An error occured while deleting the event with id '{eventId}' in the context with id '{contextId}', owned by the correlation with id '{correlationId}': {ex}", action.EventId, action.ContextId, action.CorrelationId, ex); - } - } - -} - diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Reducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Reducers.cs deleted file mode 100644 index 22a2b767f..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Reducers.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Correlations.View; - -///

-/// Defines Flux reducers for -related actions -/// -[Reducer] -public static class Reducers -{ - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CorrelationViewState On(CorrelationViewState state, HandleGetCorrelationByIdResult action) - { - return state with - { - Correlation = action.Result - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CorrelationViewState On(CorrelationViewState state, ShowPublishCloudEventModal action) - { - return state with - { - PublishCloudEventModalOpened = true, - Event = action.Event ?? new() - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CorrelationViewState On(CorrelationViewState state, HidePublishCloudEventModal action) - { - return state with - { - PublishCloudEventModalOpened = false - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CorrelationViewState On(CorrelationViewState state, AddContextToV1Correlation action) - { - if (state.Correlation?.Id != action.CorrelationId) return state; - var correlation = state.Correlation.Clone()!; - if (correlation.Contexts == null) correlation.Contexts = new List(); - if(!correlation.Contexts.Any(c => c.Id == action.Context.Id)) correlation.Contexts.Add(action.Context); - return state with { Correlation = correlation }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CorrelationViewState On(CorrelationViewState state, CorrelateV1Event action) - { - if (state.Correlation?.Id != action.CorrelationId) return state; - var correlation = state.Correlation.Clone()!; - var context = correlation.Contexts?.FirstOrDefault(c => c.Id == action.ContextId); - if (context == null) return state; - if (context.PendingEvents == null) context.PendingEvents = new List(); - if(!context.PendingEvents.Any(c => c.Id == action.Event.Id)) context.PendingEvents.Add(action.Event); - return state with { Correlation = correlation }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CorrelationViewState On(CorrelationViewState state, RemoveContextFromV1Correlation action) - { - if (state.Correlation?.Id != action.CorrelationId) return state; - var correlation = state.Correlation.Clone()!; - var context = correlation.Contexts?.FirstOrDefault(c => c.Id == action.ContextId); - if (correlation.Contexts == null || context == null) return state; - correlation.Contexts.Remove(context); - return state with { Correlation = correlation }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CorrelationViewState On(CorrelationViewState state, HandleDeleteCorrelatedEventResult action) - { - var correlation = state.Correlation.Clone()!; - var context = correlation.Contexts?.FirstOrDefault(c => c.Id == action.ContextId); - if (context == null) return state; - var evt = context.PendingEvents?.FirstOrDefault(e => e.Id.Equals(action.EventId, StringComparison.InvariantCultureIgnoreCase)); - if(evt == null) return state; - context.PendingEvents!.Remove(evt); - return state with { Correlation = correlation }; - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Selectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Selectors.cs deleted file mode 100644 index 7b8a25f71..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/Selectors.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Correlations.View; - -///

-/// Defines selectors for the -/// -public static class Selectors -{ - - /// - /// Selects the current , if any - /// - /// The global - /// A new used to observe the current - public static IObservable SelectCurrentCorrelation(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Correlation) - .DistinctUntilChanged(); - } - - /// - /// Selects a boolean indicating whether or not the publish cloud event modal is visible or not - /// - /// The global - /// A new used to observe the boolean - public static IObservable SelectPublishCloudEventModalOpened(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.PublishCloudEventModalOpened) - .DistinctUntilChanged(); - } - - /// - /// Selects the cloud event to publish, if any - /// - /// The global - /// A new used to observe the current - public static IObservable SelectCurrentEvent(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Event) - .DistinctUntilChanged(); - } - -} - diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/State.cs deleted file mode 100644 index 21426ad6d..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/State.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0(the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Correlations.View; - -///

-/// Represents the Flux state used by the correlation view page -/// -[Feature] -public record CorrelationViewState -{ - - /// - /// Gets a boolean indicating whether or not the modal used to publish is opened - /// - public bool PublishCloudEventModalOpened { get; init; } - - /// - /// Gets the current , if any - /// - public V1Correlation? Correlation { get; init; } - - /// - /// Gets the current , if any - /// - public V1Event? Event { get; init; } - -} - diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/View.razor deleted file mode 100644 index b8339f7b7..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Correlations/View/View.razor +++ /dev/null @@ -1,384 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/correlations/{correlationId}" -@using Microsoft.AspNetCore.SignalR.Client -@using Neuroglia.Data.Flux -@using Neuroglia.Serialization -@using Newtonsoft.Json -@using Newtonsoft.Json.Serialization -@using System.Reactive.Subjects -@using System.Reactive.Linq -@using System.Text -@using Synapse.Integration.Models; -@using Synapse.Integration.Events.WorkflowInstances -@inherits StatefulComponent -@inject ISynapseManagementApi SynapseApi -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IBreadcrumbManager BreadcrumbManager -@inject IJsonSerializer Serializer -@inject IJSRuntime JS -@inject HubConnection HubConnection -@inject NavigationManager NavigationManager - -Correlation @correlation?.Id - -@if (correlation != null) -{ - - - - @if(correlation.ActivationType == V1CorrelationActivationType.Explicit) - { - - -
- } - -
-
- - var highlightedAttributes = correlation.Conditions.SelectMany(c => c.Filters.SelectMany(f => f.Attributes.Select(a => a.Key).Union(f.CorrelationMappings.Select(a => a.Key)))).Distinct(); -
-
-
- -

Information

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Id@correlation.Id
Created at@correlation.CreatedAt
Last Modified@correlation.LastModified
Activation@EnumHelper.Stringify(correlation.ActivationType)
Lifetime@EnumHelper.Stringify(correlation.Lifetime)
Condition Type@EnumHelper.Stringify(correlation.ConditionType)
Outcome - - - - - - - - - - - -
Type@EnumHelper.Stringify(correlation.Outcome.Type)
Target@correlation.Outcome.Target
-
-
- -

Conditions

- @foreach (var condition in correlation.Conditions) - { -
- @foreach(var filter in condition.Filters) - { -
Context Attributes
- @if(filter.Attributes != null - && filter.Attributes.Any()) - { - - - - - - - - - @foreach(var attr in filter.Attributes) - { - - - - - } - -
AttributeValue
@attr.Key@attr.Value
- } - else - { - None - } - -
Correlation Mappings
- @if(filter.CorrelationMappings != null - && filter.CorrelationMappings.Any()) - { - - - - - - - - - @foreach(var mapping in filter.CorrelationMappings) - { - - - - - } - -
AttributeValue
@mapping.Key@mapping.Value
- } - else - { - None - } - } - -
- } -
-
-

Contexts

- @foreach (var context in correlation.Contexts) - { -
- -
-
-
- @foreach(var mapping in context.Mappings) - { - @mapping.Key: @mapping.Value - } -
-
- -
-
-
- - -
Information
-
- - - - - - - - - - - - - - - -
Id@context.Id
Created at@context.CreatedAt
Last Modified@context.LastModified
-
- -
Correlation Mappings
-
- - - - - - - - - @foreach(var mapping in context.Mappings) - { - - - - - } - -
AttributeValue
@mapping.Key@mapping.Value
-
- -
Correlated Events (count: @context.PendingEvents.Count)
- @foreach(var e in context.PendingEvents) - { - -
-
-
@e.Id
-
- -
-
-
- - - - @foreach (var attr in e.Attributes) - { - - - - - } - - - - - -
@attr.Key@attr.Value
data
@JsonConvert.SerializeObject(e.Data, Formatting.Indented)
- -
- } - -
-
- } -
-
-
- -} -else -{ - -} -@code -{ - - private Subject? disposeNotifier; - private V1Correlation? correlation; - private PublishEventModal? publishEventModal; - private V1Event? cloudEvent; - - private string correlationId = null!; - [Parameter] public string CorrelationId { get; set; } = null!; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.disposeNotifier = new Subject(); - Selectors.SelectCurrentCorrelation(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(correlation => - { - this.correlation = correlation; - this.StateHasChanged(); - }); - Selectors.SelectPublishCloudEventModalOpened(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async isOpen => - { - if (isOpen && this.publishEventModal != null) await this.publishEventModal.ShowAsync(); - else if (this.publishEventModal != null) await this.publishEventModal.HideAsync(); - this.StateHasChanged(); - }); - Selectors.SelectCurrentEvent(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(e => - { - this.cloudEvent = e; - this.StateHasChanged(); - }); - await this.BreadcrumbManager.Use(Breadcrumbs.Correlations); - await this.BreadcrumbManager.AddItem(new BreadcrumbItem(this.CorrelationId, $"/correlations/{this.CorrelationId}")); - } - - protected override async Task OnParametersSetAsync() - { - if (this.correlationId != this.CorrelationId) - { - this.correlationId = this.CorrelationId; - this.Dispatcher.Dispatch(new GetCorrelationById(this.correlationId)); - } - } - - private void OnTriggerCondition(V1CorrelationCondition condition) - { - this.Dispatcher.Dispatch(new ShowPublishCloudEventModal(condition)); - } - - private void OnCloudEventChanged(Action patch) - { - - } - - private void OnPublishCloudEventModalActiveChange(bool active) - { - if (!active && this.State.PublishCloudEventModalOpened) this.Dispatcher.Dispatch(new HidePublishCloudEventModal()); - } - - private void OnNavigateToCorrelationList() - { - this.NavigationManager.NavigateTo("/correlations"); - } - - private void OnDeleteCorrelation() - { - if (this.correlation == null) return; - this.Dispatcher.Dispatch(new DeleteCorrelation(this.correlation.Id)); - } - - private void OnDeleteCorrelationContext(V1CorrelationContext context) - { - if (this.correlation == null) return; - this.Dispatcher.Dispatch(new DeleteCorrelationContext(this.correlation.Id, context.Id)); - } - - private void OnDeleteCorrelatedEvent(V1CorrelationContext context, V1Event e) - { - if (this.correlation == null) return; - this.Dispatcher.Dispatch(new DeleteCorrelatedEvent(this.correlation.Id, context.Id, e.Id)); - } - - private void OnSaveChanges() - { - - } - - protected override void Dispose(bool disposing) - { - if (!disposing) return; - if (this.disposeNotifier != null) - { - this.disposeNotifier.OnNext(true); - this.disposeNotifier.OnCompleted(); - this.disposeNotifier.Dispose(); - this.disposeNotifier = null; - } - base.Dispose(disposing); - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Correlators/List/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Correlators/List/View.razor new file mode 100644 index 000000000..36977ff76 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Correlators/List/View.razor @@ -0,0 +1,107 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/correlators" +@attribute [Authorize] +@namespace Synapse.Dashboard.Pages.Correlators.List +@inherits NamespacedResourceManagementComponent +@inject IBreadcrumbManager BreadcrumbManager + +Correlators + +
+ @if (Loading) + { + + } +
+

Correlators

+ @(Resources?.Count ?? 0) items +
+ + + +
+
+ + + + + + + + + + + + + @if (Resources != null && Resources.Any()) + { + + + + + + + + + + + } + +
NamespaceNameCreation TimeStatus + +
@resource.Metadata.Namespace@resource.Metadata.Name@resource.Metadata.CreationTimestamp?.DateTime.RelativeFormat()@resource.Status?.Phase + + + + +
+
+ + + + + + + + + +@code { + /// + protected override void OnInitialized() + { + base.OnInitialized(); + BreadcrumbManager.Use(Breadcrumbs.Correlators); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Index.razor b/src/dashboard/Synapse.Dashboard/Pages/Index.razor deleted file mode 100644 index 208bd7609..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Index.razor +++ /dev/null @@ -1,324 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@namespace Synapse.Dashboard -@page "/" -@using System.Dynamic -@using System.Collections.ObjectModel -@inject ISynapseManagementApi SynapseManagementApi -@inject IBreadcrumbManager BreadcrumbService -@inject IStyleManager StyleManager - -Overview - - Overview - - -

-
-
-

Today

-
-
-
-
-
- @dailyReport?.RunningInstances - Running instances -
-
- @dailyReport?.CompletedInstances - Completed instances -
-
- @dailyReport?.FaultedInstances - Failed instances -
-
- @dailyReport?.CancelledInstances - Cancelled instances -
-
-
-
- - @if(dailyReport != null) - { -
-

@(dailyReport.TotalInstances < 1 ? "-" : Math.Round(((double)dailyReport.CompletedInstances / dailyReport.TotalInstances) * 100, 0))

% - Success rate -
- } -
-
Executed instances
-
-
-
- - @if(dailyReport != null) - { -
-

@(dailyReport.TotalActivities < 1 ? "-" : Math.Round((((double)dailyReport.CompletedActivities + dailyReport.SkippedActivities) / dailyReport.TotalActivities) * 100, 0))

% - Success rate -
- } -
-
Executed activities
-
-
-
-
-
-
-

This week

-
-
-
-
-
Executed instances
- -
-
-
Executed activities
- -
-
-
-
-
- -@code{ - - private V1OperationalReport? dailyReport; - private List weekReports = new(); - private ChartConfiguration? dailyExecutedInstancesChart; - private ChartConfiguration? dailyExecutedActivitiesChart; - private ChartConfiguration? weeklyExecutedInstancesChart; - private ChartConfiguration? weeklyExecutedActivitiesChart; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.Home); - try - { - this.dailyReport = await this.SynapseManagementApi.GetOperationalReportAsync(); - } - catch { } - if (this.dailyReport == null) - this.dailyReport = new(DateTime.Now); - var firstDayOfWeek = DateTime.Now.GetFirstDayOfWeek(); - var lastDayOfWeek = DateTime.Now.GetLastDayOfWeek(); - var currentDay = firstDayOfWeek; - do - { - if (currentDay == DateTime.Now) - { - this.weekReports.Add(this.dailyReport); - } - else - { - V1OperationalReport report; - try - { - report = await this.SynapseManagementApi.GetOperationalReportAsync(currentDay); - } - catch - { - report = new(currentDay); - } - this.weekReports.Add(report); - } - currentDay = currentDay.AddDays(1); - } - while (currentDay <= lastDayOfWeek); - await this.BuildDailyExecutedInstancesChartAsync(); - await this.BuildDailyExecutedActivitiesChartAsync(); - await this.BuildWeeklyExecutedInstancesChartAsync(); - await this.BuildWeeklyExecutedActivitiesChartAsync(); - } - - private async ValueTask BuildDailyExecutedInstancesChartAsync() - { - if (this.dailyReport == null) - return; - this.dailyExecutedInstancesChart = new() - { - Type = ChartType.Doughnut, - Data = - { - Labels = new() - { - "Completed", - "Faulted", - "Cancelled" - }, - Datasets = new() - { - new ChartDataset() - { - Data = new() - { - dailyReport.CompletedInstances, - dailyReport.FaultedInstances, - dailyReport.CancelledInstances - }, - BackgroundColor = new() - { - await this.StyleManager.GetVariableValueAsync("--bs-success"), - await this.StyleManager.GetVariableValueAsync("--bs-danger"), - await this.StyleManager.GetVariableValueAsync("--bs-warning") - } - } - } - } - }; - } - - private async ValueTask BuildDailyExecutedActivitiesChartAsync() - { - if (this.dailyReport == null) - return; - this.dailyExecutedActivitiesChart = new() - { - Type = ChartType.Doughnut, - Data = - { - Labels = new() - { - "Completed", - "Faulted", - "Cancelled", - "Skipped" - }, - Datasets = new() - { - new ChartDataset() - { - Data = new() - { - dailyReport.CompletedActivities, - dailyReport.FaultedActivities, - dailyReport.CancelledActivities, - dailyReport.SkippedActivities - }, - BackgroundColor = new() - { - await this.StyleManager.GetVariableValueAsync("--bs-success"), - await this.StyleManager.GetVariableValueAsync("--bs-danger"), - await this.StyleManager.GetVariableValueAsync("--bs-warning"), - await this.StyleManager.GetVariableValueAsync("--bs-secondary") - } - } - } - } - }; - } - - private async Task BuildWeeklyExecutedInstancesChartAsync() - { - this.weeklyExecutedInstancesChart = new() - { - Type = ChartType.Line, - Data = new() - { - Labels = weekReports.Select(d => d.Date.ToShortDateString()).ToList(), - Datasets = new() - { - new ChartDataset() - { - Label = "Executed", - Data = weekReports.Select(d => d.ExecutedInstances).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-info"), - BorderWidth = 1.5 - }, - new ChartDataset() - { - Label = "Completed", - Data = weekReports.Select(d => d.CompletedInstances).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-success"), - BorderWidth = 1 - }, - new ChartDataset() - { - Label = "Faulted", - Data = weekReports.Select(d => d.FaultedInstances).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-danger"), - BorderWidth = 1 - }, - new ChartDataset() - { - Label = "Cancelled", - Data = weekReports.Select(d => d.CancelledInstances).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-warning"), - BorderWidth = 1 - } - } - } - }; - } - - private async Task BuildWeeklyExecutedActivitiesChartAsync() - { - this.weeklyExecutedActivitiesChart = new() - { - Type = ChartType.Line, - Data = new() - { - Labels = weekReports.Select(d => d.Date.ToShortDateString()).ToList(), - Datasets = new() - { - new ChartDataset() - { - Label = "Executed", - Data = weekReports.Select(d => d.ExecutedActivities).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-info"), - BorderWidth = 1.5 - }, - new ChartDataset() - { - Label = "Completed", - Data = weekReports.Select(d => d.CompletedActivities).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-success"), - BorderWidth = 1 - }, - new ChartDataset() - { - Label = "Faulted", - Data = weekReports.Select(d => d.FaultedActivities).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-danger"), - BorderWidth = 1 - }, - new ChartDataset() - { - Label = "Cancelled", - Data = weekReports.Select(d => d.CancelledActivities).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-warning"), - BorderWidth = 1 - }, - new ChartDataset() - { - Label = "Skipped", - Data = weekReports.Select(d => d.CancelledActivities).OfType().ToList(), - BorderColor = await this.StyleManager.GetVariableValueAsync("--bs-secondary"), - BorderWidth = 1 - } - } - } - }; - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Namespaces/List/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Namespaces/List/View.razor new file mode 100644 index 000000000..288a6f5f2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Namespaces/List/View.razor @@ -0,0 +1,109 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/namespaces/{name?}" +@attribute [Authorize] +@namespace Synapse.Dashboard.Pages.Namespaces.List +@inherits ClusterResourceManagementComponent +@inject IBreadcrumbManager BreadcrumbManager + +Namespaces + +
+ @if (Loading) + { + + } +
+

Namespaces

+ @(Resources?.Count ?? 0) items +
+ +
+ +
+ + + + + + + + + + + @if (Resources != null && Resources.Any()) + { + + + + + + + + + } + +
NameCreation Time + +
@resource.Metadata.Name@resource.Metadata.CreationTimestamp.ToString() + + + + +
+
+ + + + + + + + + +@code { + /// + protected override void OnInitialized() + { + base.OnInitialized(); + BreadcrumbManager.Use(Breadcrumbs.Namespaces); + } + + /// + /// Opens the targeted 's edition + /// + /// The to edit + protected override Task OnShowResourceEditorAsync(Namespace? resource = null) + { + if (this.EditorOffCanvas == null) return Task.CompletedTask; + var parameters = new Dictionary + { + { nameof(ResourceEditor.Resource), resource! }, + { nameof(ResourceEditor.IsCluster), true } + }; + string actionType = resource == null ? "creation" : "edition"; + return this.EditorOffCanvas.ShowAsync>(title: typeof(Namespace).Name + " " + actionType, parameters: parameters); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Operators/List/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Operators/List/View.razor new file mode 100644 index 000000000..dae84c6ef --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Operators/List/View.razor @@ -0,0 +1,107 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/operators/{namespace?}/{name?}" +@attribute [Authorize] +@namespace Synapse.Dashboard.Pages.Operators.List +@inherits NamespacedResourceManagementComponent +@inject IBreadcrumbManager BreadcrumbManager + +Operators + +
+ @if (Loading) + { + + } +
+

Operators

+ @(Resources?.Count ?? 0) items +
+ + + +
+
+ + + + + + + + + + + + + @if (Resources != null && Resources.Any()) + { + + + + + + + + + + + } + +
NamespaceNameCreation TimeStatus + +
@resource.Metadata.Namespace@resource.Metadata.Name@resource.Metadata.CreationTimestamp?.DateTime.RelativeFormat()@resource.Status?.Phase + + + + +
+
+ + + + + + + + + +@code { + /// + protected override void OnInitialized() + { + base.OnInitialized(); + BreadcrumbManager.Use(Breadcrumbs.Operators); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorActions.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorActions.cs deleted file mode 100644 index d3fbab3be..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorActions.cs +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Authentications -{ - - /// - /// Represents the Flux action used to initialize the - /// - public class InitializeState - { - - /// - /// Initializes a new - /// - /// A boolean indicating whether or not to initialize the state only if it does not yet exist - public InitializeState(bool ifNotExists = true) - { - this.IfNotExists = ifNotExists; - } - - /// - /// Initializes a new - /// - /// The id of the event definition collection to initialize the state with - public InitializeState(string collectionId) - { - this.CollectionId = collectionId; - } - - /// - /// Gets/sets the id of the event definition collection to initialize the state with - /// - public string? CollectionId { get; } - - /// - /// Gets/sets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - - } - - /// - /// Represents the Flux action used to set the initialized state - /// - public class InitializeStateSuccessful - { - - /// - /// Initializes a new - /// - /// The initial state - /// A boolean indicating whether or not to initialize the state only if it does not yet exist - public InitializeStateSuccessful(AuthenticationDefinitionCollectionEditorState initialState, bool ifNotExists = true) - { - this.InitialState = initialState ?? throw new ArgumentNullException(nameof(initialState)); - this.IfNotExists = ifNotExists; - } - - /// - /// Gets the initial state - /// - public AuthenticationDefinitionCollectionEditorState InitialState { get; } - - /// - /// Gets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - - } - - /// - /// Represents the Flux action to handle changes in the form based editor - /// - public class HandleFormBasedEditorChange - { - - /// - /// Initialised a new action with the provided definition - /// - /// The updated - public HandleFormBasedEditorChange(V1AuthenticationDefinitionCollection collection) - { - this.Collection = collection ?? throw new ArgumentNullException(nameof(collection)); - } - - /// - /// Gets the updated - /// - public V1AuthenticationDefinitionCollection Collection { get; } - - } - - /// - /// Represents the Flux action to handle changes in the text based editor - /// - public class HandleTextBasedEditorChange - { - - /// - /// Initializes a new action with the provided definition - /// - /// The updated serialized collection - public HandleTextBasedEditorChange(string serializedCollection) - { - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the updated serialized collection - /// - public string SerializedCollection { get; } - - } - - /// - /// Represents the Flux action to update the edited event definition collection - /// - public class UpdateCollection - { - - /// - /// Initializes a new - /// - /// The updated - public UpdateCollection(V1AuthenticationDefinitionCollection collection) - { - this.Collection = collection ?? throw new ArgumentNullException(nameof(collection)); - } - - /// - /// Gets the updated - /// - public V1AuthenticationDefinitionCollection Collection { get; } - } - - /// - /// Represents the Flux action used to update the edited event definition collection - /// - public class UpdateSerializedCollection - { - /// - /// Initializes a new - /// - /// The updated serialized collection - public UpdateSerializedCollection(string serializedCollection) - { - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the updated serialized collection - /// - public string SerializedCollection { get; } - - } - - /// - /// Represents the Flux action dispatched when the editor starts to update - /// - public class StartUpdating { } - - /// - /// Represents the Flux action dispatched when the editor finished updating - /// - public class StopUpdating { } - - /// - /// Represents the Flux action used to save the specified using the Synapse API - /// - public class SaveCollection - { - - /// - /// Initializes a new - /// - /// The to save - public SaveCollection(V1AuthenticationDefinitionCollection collection) - { - this.Collection = collection; - } - - /// - /// Gets the to save - /// - public V1AuthenticationDefinitionCollection Collection { get; } - - } - - /// - /// Represents the Flux action used to notify the UI about the completion of the specified 's save - /// - public class CollectionSaved - { - - /// - /// Initializes a new - /// - /// The that has been saved - public CollectionSaved(V1AuthenticationDefinitionCollection collection) - { - this.Collection = collection; - } - - /// - /// Gets the that has been saved - /// - public V1AuthenticationDefinitionCollection Collection { get; } - - } - - /// - /// Notifies the UI about the failure of a 's save - /// - public class CollectionSaveFailed - { - - /// - /// Initializes a new - /// - /// The error that has occured while saving the specified - public CollectionSaveFailed(string error) - { - this.Error = error; - } - - /// - /// Gets the error that has occured while saving the specified - /// - public string? Error { get; } - - } - - /// - /// Represents the Flux action used to change the language of the text editor - /// - public class ChangeEditorLanguage - { - - /// - /// Initializes a new - /// - /// The language to use - /// The serialized collection - public ChangeEditorLanguage(string language, string serializedCollection) - { - this.Language = language ?? throw new ArgumentNullException(nameof(language)); - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the language to use - /// - public string Language { get; } - - /// - /// Gets the serialized collection - /// - public string SerializedCollection { get; } - } - - /// - /// Represents the Flux action used to toggle the state of the specified expander - /// - public class ToggleExpand - { - - /// - /// Initializes a new - /// - /// The name of the state to toggle - /// A boolean indicating whether or not the state is expanded - public ToggleExpand(string name, bool isExpanded) - { - this.Name = name; - this.IsExpanded = isExpanded; - } - - /// - /// Gets the name of the state to toggle - /// - public string Name { get; } - - /// - /// Gets a boolean indicating whether or not the state is expanded - /// - public bool IsExpanded { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorEffects.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorEffects.cs deleted file mode 100644 index 1638818f9..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorEffects.cs +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using Neuroglia.Data.Flux; -using Neuroglia.Serialization; -using Newtonsoft.Json; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Apis.Management; -using Synapse.Dashboard.Services; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Authentications -{ - - /// - /// Defines Flux effects that apply to -related Flux actions - /// - [Effect] - public static class AuthenticationDefinitionCollectionEditorEffects - { - - /// - /// Handles the state initialization - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(InitializeState action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - V1AuthenticationDefinitionCollection collection; - if (string.IsNullOrWhiteSpace(action.CollectionId)) - collection = new() { Name = "Undefined", Version = "0.1.0", Authentications = new List() }; - else - collection = (await context.Services.GetRequiredService().GetAuthenticationDefinitionCollectionByIdAsync(action.CollectionId)); - var serializedCollection = JsonConvert.SerializeObject(collection, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - serializedCollection = await yamlConverter.JsonToYaml(serializedCollection); - var initialState = new AuthenticationDefinitionCollectionEditorState() - { - Collection = collection, - SerializedCollection = serializedCollection, - Updating = false, - Saving = false, - ExpanderStates = new Dictionary() - { - { "general", true }, - { "authentications", true } - } - }; - context.Dispatcher.Dispatch(new InitializeStateSuccessful(initialState, action.IfNotExists)); - } - catch(Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the form editor changes - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(HandleFormBasedEditorChange action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateCollection(action.Collection)); - var serializedCollection = JsonConvert.SerializeObject(action.Collection, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - var yamlConverter = context.Services.GetRequiredService(); - serializedCollection = await yamlConverter.JsonToYaml(serializedCollection); - } - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - context.Dispatcher.Dispatch(new StopUpdating()); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the text editor changes - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(HandleTextBasedEditorChange action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - var jsonSerializer = context.Services.GetRequiredService(); - var serializedCollection = action.SerializedCollection; - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - serializedCollection = await yamlConverter.YamlToJson(serializedCollection); - var collection = await jsonSerializer.DeserializeAsync(serializedCollection); - context.Dispatcher.Dispatch(new UpdateCollection(collection)); - context.Dispatcher.Dispatch(new StopUpdating()); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the text editor language changes - /// - /// The action - /// The context - /// - /// - public static async Task On(ChangeEditorLanguage action, IEffectContext context) - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - context.Dispatcher.Dispatch(new StartUpdating()); - try - { - var serializedCollection = action.Language == PreferedLanguage.YAML ? - await yamlConverter.JsonToYaml(action.SerializedCollection) : - await yamlConverter.YamlToJson(action.SerializedCollection); - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - await monacoEditorHelper.ChangePreferedLanguage(action.Language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - } - context.Dispatcher.Dispatch(new StopUpdating()); - } - - /// - /// Saves the specified collection, - /// - /// The Flux action - /// The context - /// A new awaitable - public static async Task On(SaveCollection action, IEffectContext context) - { - try - { - var api = context.Services.GetRequiredService(); - var collection = await api.CreateAuthenticationDefinitionCollectionAsync(new() - { - Name = action.Collection.Name, - Version = action.Collection.Version, - Description = action.Collection.Description, - Authentications = action.Collection.Authentications - }); - context.Dispatcher.Dispatch(new CollectionSaved(collection)); - context.Dispatcher.Dispatch(new InitializeState(false)); - var navigationManager = context.Services.GetRequiredService(); - navigationManager.NavigateTo($"/resources/collections/authentications"); - } - catch (Exception ex) - { - context.Dispatcher.Dispatch(new CollectionSaveFailed(ex.Message)); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorReducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorReducers.cs deleted file mode 100644 index a38e22ec1..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorReducers.cs +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Authentications -{ - - /// - /// Defines Flux reducers that apply to -related Flux actions - /// - [Reducer] - public static class AuthenticationDefinitionCollectionEditorReducers - { - - /// - /// Reduces the for the specified - /// - /// The to reduce - /// The Flux action to reduce - /// The reduced - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, InitializeStateSuccessful action) - { - if (action.IfNotExists && state.Initialized) - return state; - return action.InitialState with { Initialized = true }; - } - - /// - /// Changes the updating state when editing the definition form the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, StartUpdating action) - { - return state with - { - Updating = true - }; - } - - /// - /// Changes the updating state when failed to handle the value from the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, StopUpdating action) - { - return state with - { - Updating = false - }; - } - - /// - /// Changes the definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, UpdateCollection action) - { - return state with - { - Collection = action.Collection - }; - } - - /// - /// Notifies about the definition being saved - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, SaveCollection action) - { - return state with - { - Saving = true - }; - } - - /// - /// Notifies about the completion of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, CollectionSaved action) - { - return state with - { - Collection = action.Collection, - Saving = false - }; - } - - /// - /// Notifies about the failure of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, CollectionSaveFailed action) - { - return state with - { - Saving = false - }; - } - - /// - /// Changes the JSON definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, UpdateSerializedCollection action) - { - return state with - { - SerializedCollection = action.SerializedCollection - }; - } - - /// - /// Toggles the state of the specified expander - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static AuthenticationDefinitionCollectionEditorState On(AuthenticationDefinitionCollectionEditorState state, ToggleExpand action) - { - state.ExpanderStates![action.Name] = action.IsExpanded; - return state; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorSelectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorSelectors.cs deleted file mode 100644 index 5b6ef9ebf..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorSelectors.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Authentications -{ - - /// - /// Defines selectors for the - /// - public static class AuthenticationDefinitionCollectionEditorSelectors - { - - - /// - /// Selects the workflow definition - /// - /// - /// - public static IObservable SelectAuthenticationDefinitionCollection(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Collection) - .DistinctUntilChanged(); - } - - /// - /// Selects the workflow definition text - /// - /// - /// - public static IObservable SelectSerializedAuthenticationDefinitionCollection(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.SerializedCollection) - .DistinctUntilChanged(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorState.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorState.cs deleted file mode 100644 index af820d8fa..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/AuthenticationDefinitionCollectionEditorState.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Authentications -{ - - /// - /// Represents the object used to maintain the state of a function definition collection editor - /// - [Feature] - public record AuthenticationDefinitionCollectionEditorState - { - - /// - /// Initializes a new - /// - public AuthenticationDefinitionCollectionEditorState() - { - - } - - /// - /// Gets/sets the collection to edit - /// - public Integration.Models.V1AuthenticationDefinitionCollection? Collection { get; set; } - - /// - /// Gets/sets the raw JSON/YAML of the collection to edit - /// - public string? SerializedCollection { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor has been initialized - /// - public bool Initialized { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor is saving the collection - /// - public bool Saving { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor is being updated - /// - public bool Updating { get; set; } - - /// - /// Gets a dictionary containing the name mappings of the editor's expanders states - /// - public Dictionary? ExpanderStates { get; set; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/Create.razor b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/Create.razor deleted file mode 100644 index ee91063fe..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Authentications/Create.razor +++ /dev/null @@ -1,269 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/resources/collections/authentications/new" -@page "/resources/collections/authentications/edit/{collectionId}" -@using System.Reactive.Subjects -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbService -@inject IMonacoEditorHelper MonacoEditorHelper -@inject NavigationManager NavigationManager - -New authentication definition collection - - - New authentication definition collection - - - - - - - - - -@if(collection != null) -{ -

-
-
- -
-
General
-
- - - - - - - - - - - -
Name
Id -
- -
- - -
-
-
Description
Version
- -
-
-
- -
-
Authentications
-
- - - - - - - - - - - - - @if (collection.Authentications != null) - { - @foreach (var authentication in collection.Authentications) - { - -
-
- - - - - - - - } - } - -
NameKindSourceType
@authentication.Name@EnumHelper.Stringify(authentication.Scheme) - -
- - -
-
-
-
- -
-
-} - -@code -{ - - private bool autoGenerateId = true; - private string? collectionId; - [Parameter] public string? CollectionId { get; set; } - - private MonacoEditor? textBasedEditor; - private Subject? editorValue; - private Subject? disposeNotifier; - private V1AuthenticationDefinitionCollection? collection; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.CreateAuthenticationDefinitionCollection); - this.disposeNotifier = new Subject(); - this.editorValue = new Subject(); - this.editorValue - .Throttle(TimeSpan.FromMilliseconds(300)) - .DistinctUntilChanged() - .TakeUntil(this.disposeNotifier) - .Subscribe(text => this.Dispatcher.Dispatch(new HandleTextBasedEditorChange(text))); - AuthenticationDefinitionCollectionEditorSelectors.SelectSerializedAuthenticationDefinitionCollection(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (text) => - { - if (text != null && this.textBasedEditor != null) - { - var currentText = await this.textBasedEditor!.GetValue(); - if (currentText != text) - await this.textBasedEditor.SetValue(text); - } - }); - AuthenticationDefinitionCollectionEditorSelectors.SelectAuthenticationDefinitionCollection(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (definition) => - { - if (this.collection != definition) - { - this.collection = definition; - this.StateHasChanged(); - } - }); - if (string.IsNullOrWhiteSpace(this.collectionId)) - this.Dispatcher.Dispatch(new InitializeState()); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.collectionId != this.CollectionId) - { - this.collectionId = this.CollectionId; - if (!string.IsNullOrWhiteSpace(this.collectionId)) - this.Dispatcher.Dispatch(new InitializeState(this.collectionId)); - } - } - - protected async Task OnTextBasedEditorInit(MonacoEditorBase editor) - { - await this.OnSetTextBasedEditorLanguage(); - await this.textBasedEditor!.SetValue(this.State.SerializedCollection); - } - - protected async Task OnSetTextBasedEditorLanguage() - { - var model = await this.textBasedEditor!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - } - - protected virtual void OnToggleExpand(string sectionName, bool isExpanded) - { - this.Dispatcher.Dispatch(new ToggleExpand(sectionName, isExpanded)); - this.StateHasChanged(); - } - - protected virtual async Task OnAutoGenerateIdChanged(bool autoGenerate) - { - this.autoGenerateId = autoGenerate; - if (this.autoGenerateId) - await this.OnPropertyChanged(nameof(collection.Name), w => w.Id = w.Name.Slugify("-").ToLowerInvariant()); - } - - protected virtual async Task OnNameChanged(string? name) - { - if (this.collection == null) - return; - await this.OnPropertyChanged(nameof(collection.Name), w => - { - w.Name = name; - if (autoGenerateId && !string.IsNullOrWhiteSpace(name)) - w.Id = name.Slugify("-").ToLowerInvariant(); - }); - } - - protected virtual async Task OnCreateAuthentication() - { - if (this.collection == null) - return; - if (this.collection.Authentications == null) - this.collection.Authentications = new List(); - var e = new AuthenticationDefinition() { Name = $"authentication-{collection.Authentications.Count + 1}", Scheme = AuthenticationScheme.Basic, Properties = new BasicAuthenticationProperties() { Username = "username", Password = "password" } }; - await this.OnPropertyChanged(nameof(collection.Authentications), d => - { - d.Authentications!.Add(e); - }); - } - - protected virtual async Task OnAuthenticationChanged(AuthenticationDefinition? e) - { - await this.OnPropertyChanged(nameof(collection.Authentications)); - } - - protected virtual async Task OnPropertyChanged(string property, Action? patch = null) - { - if (this.collection == null) - return; - if (patch != null) - patch(this.collection); - this.Dispatcher.Dispatch(new HandleFormBasedEditorChange(collection)); - await Task.CompletedTask; - } - - protected async Task OnSerializedCollectionChanged(ModelContentChangedEvent e) - { - if (!this.State.Updating && this.editorValue != null) - { - var text = await this.textBasedEditor!.GetValue(); - this.editorValue.OnNext(text); - } - } - - protected virtual void OnResetCollection() - { - this.Dispatcher.Dispatch(new InitializeState(false)); - } - - protected virtual void OnSaveCollection() - { - if (this.collection == null) - return; - this.Dispatcher.Dispatch(new SaveCollection(this.collection)); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/Create.razor b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/Create.razor deleted file mode 100644 index 824bede17..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/Create.razor +++ /dev/null @@ -1,277 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/resources/collections/events/new" -@page "/resources/collections/events/edit/{collectionId}" -@using System.Reactive.Subjects -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbService -@inject IMonacoEditorHelper MonacoEditorHelper -@inject NavigationManager NavigationManager - -New event definition collection - - - New event definition collection - - - - - - - - - -@if(collection != null) -{ -

-
-
- -
-
General
-
- - - - - - - - - - - -
Name
Id -
- -
- - -
-
-
Description
Version
- -
-
-
- -
-
Events
-
- - - - - - - - - - - - - @if (collection.Events != null) - { - @foreach (var e in collection.Events) - { - -
-
- - - - - - - - - - - } - } - -
NameKindSourceType
@e.Name@EnumHelper.Stringify(e.Kind)@e.Source@e.Type - @if (e.Correlations != null && e.Correlations.Any()) - { - correlated - } - - -
- - -
-
-
-
- -
-
-} - -@code -{ - - private bool autoGenerateId = true; - private string? collectionId; - [Parameter] public string? CollectionId { get; set; } - - private MonacoEditor? textBasedEditor; - private Subject? editorValue; - private Subject? disposeNotifier; - private V1EventDefinitionCollection? collection; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.CreateEventDefinitionCollection); - this.disposeNotifier = new Subject(); - this.editorValue = new Subject(); - this.editorValue - .Throttle(TimeSpan.FromMilliseconds(300)) - .DistinctUntilChanged() - .TakeUntil(this.disposeNotifier) - .Subscribe(text => this.Dispatcher.Dispatch(new HandleTextBasedEditorChange(text))); - EventDefinitionCollectionEditorSelectors.SelectSerializedEventDefinitionCollection(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (text) => - { - if (text != null && this.textBasedEditor != null) - { - var currentText = await this.textBasedEditor!.GetValue(); - if (currentText != text) - await this.textBasedEditor.SetValue(text); - } - }); - EventDefinitionCollectionEditorSelectors.SelectEventDefinitionCollection(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (definition) => - { - if (this.collection != definition) - { - this.collection = definition; - this.StateHasChanged(); - } - }); - if (string.IsNullOrWhiteSpace(this.collectionId)) - this.Dispatcher.Dispatch(new InitializeState()); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.collectionId != this.CollectionId) - { - this.collectionId = this.CollectionId; - if (!string.IsNullOrWhiteSpace(this.collectionId)) - this.Dispatcher.Dispatch(new InitializeState(this.collectionId)); - } - } - - protected async Task OnTextBasedEditorInit(MonacoEditorBase editor) - { - await this.OnSetTextBasedEditorLanguage(); - await this.textBasedEditor!.SetValue(this.State.SerializedCollection); - } - - protected async Task OnSetTextBasedEditorLanguage() - { - var model = await this.textBasedEditor!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - } - - protected virtual void OnToggleExpand(string sectionName, bool isExpanded) - { - this.Dispatcher.Dispatch(new ToggleExpand(sectionName, isExpanded)); - this.StateHasChanged(); - } - - protected virtual async Task OnAutoGenerateIdChanged(bool autoGenerate) - { - this.autoGenerateId = autoGenerate; - if (this.autoGenerateId) - await this.OnPropertyChanged(nameof(collection.Name), w => w.Id = w.Name.Slugify("-").ToLowerInvariant()); - } - - protected virtual async Task OnNameChanged(string? name) - { - if (this.collection == null) - return; - await this.OnPropertyChanged(nameof(collection.Name), w => - { - w.Name = name; - if (autoGenerateId && !string.IsNullOrWhiteSpace(name)) - w.Id = name.Slugify("-").ToLowerInvariant(); - }); - } - - protected virtual async Task OnCreateEvent() - { - if (this.collection == null) - return; - if (this.collection.Events == null) - this.collection.Events = new List(); - var e = new EventDefinition() { Name = $"event-{collection.Events.Count + 1}" }; - await this.OnPropertyChanged(nameof(collection.Events), d => - { - d.Events!.Add(e); - }); - } - - protected virtual async Task OnEventChanged(EventDefinition? e) - { - await this.OnPropertyChanged(nameof(collection.Events)); - } - - protected virtual async Task OnPropertyChanged(string property, Action? patch = null) - { - if (this.collection == null) - return; - if (patch != null) - patch(this.collection); - this.Dispatcher.Dispatch(new HandleFormBasedEditorChange(collection)); - await Task.CompletedTask; - } - - protected async Task OnSerializedCollectionChanged(ModelContentChangedEvent e) - { - if (!this.State.Updating && this.editorValue != null) - { - var text = await this.textBasedEditor!.GetValue(); - this.editorValue.OnNext(text); - } - } - - protected virtual void OnResetCollection() - { - this.Dispatcher.Dispatch(new InitializeState(false)); - } - - protected virtual void OnSaveCollection() - { - if (this.collection == null) - return; - this.Dispatcher.Dispatch(new SaveCollection(this.collection)); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorActions.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorActions.cs deleted file mode 100644 index 38bec23b8..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorActions.cs +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Events -{ - - /// - /// Represents the Flux action used to initialize the - /// - public class InitializeState - { - - /// - /// Initializes a new - /// - /// A boolean indicating whether or not to initialize the state only if it does not yet exist - public InitializeState(bool ifNotExists = true) - { - this.IfNotExists = ifNotExists; - } - - /// - /// Initializes a new - /// - /// The id of the event definition collection to initialize the state with - public InitializeState(string collectionId) - { - this.CollectionId = collectionId; - } - - /// - /// Gets/sets the id of the event definition collection to initialize the state with - /// - public string? CollectionId { get; } - - /// - /// Gets/sets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - - } - - /// - /// Represents the Flux action used to set the initialized state - /// - public class InitializeStateSuccessful - { - - /// - /// Initializes a new - /// - /// The initial state - /// A boolean indicating whether or not to initialize the state only if it does not yet exist - public InitializeStateSuccessful(EventDefinitionCollectionEditorState initialState, bool ifNotExists = true) - { - this.InitialState = initialState ?? throw new ArgumentNullException(nameof(initialState)); - this.IfNotExists = ifNotExists; - } - - /// - /// Gets the initial state - /// - public EventDefinitionCollectionEditorState InitialState { get; } - - /// - /// Gets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - - } - - /// - /// Represents the Flux action to handle changes in the form based editor - /// - public class HandleFormBasedEditorChange - { - - /// - /// Initialised a new action with the provided definition - /// - /// The updated - public HandleFormBasedEditorChange(V1EventDefinitionCollection collection) - { - this.Collection = collection ?? throw new ArgumentNullException(nameof(collection)); - } - - /// - /// Gets the updated - /// - public V1EventDefinitionCollection Collection { get; } - - } - - /// - /// Represents the Flux action to handle changes in the text based editor - /// - public class HandleTextBasedEditorChange - { - - /// - /// Initializes a new action with the provided definition - /// - /// The updated serialized collection - public HandleTextBasedEditorChange(string serializedCollection) - { - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the updated serialized collection - /// - public string SerializedCollection { get; } - - } - - /// - /// Represents the Flux action to update the edited event definition collection - /// - public class UpdateCollection - { - - /// - /// Initializes a new - /// - /// The updated - public UpdateCollection(V1EventDefinitionCollection collection) - { - this.Collection = collection ?? throw new ArgumentNullException(nameof(collection)); - } - - /// - /// Gets the updated - /// - public V1EventDefinitionCollection Collection { get; } - } - - /// - /// Represents the Flux action used to update the edited event definition collection - /// - public class UpdateSerializedCollection - { - /// - /// Initializes a new - /// - /// The updated serialized collection - public UpdateSerializedCollection(string serializedCollection) - { - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the updated serialized collection - /// - public string SerializedCollection { get; } - - } - - /// - /// Represents the Flux action dispatched when the editor starts to update - /// - public class StartUpdating { } - - /// - /// Represents the Flux action dispatched when the editor finished updating - /// - public class StopUpdating { } - - /// - /// Represents the Flux action used to save the specified using the Synapse API - /// - public class SaveCollection - { - - /// - /// Initializes a new - /// - /// The to save - public SaveCollection(V1EventDefinitionCollection collection) - { - this.Collection = collection; - } - - /// - /// Gets the to save - /// - public V1EventDefinitionCollection Collection { get; } - - } - - /// - /// Represents the Flux action used to notify the UI about the completion of the specified 's save - /// - public class CollectionSaved - { - - /// - /// Initializes a new - /// - /// The that has been saved - public CollectionSaved(V1EventDefinitionCollection collection) - { - this.Collection = collection; - } - - /// - /// Gets the that has been saved - /// - public V1EventDefinitionCollection Collection { get; } - - } - - /// - /// Notifies the UI about the failure of a 's save - /// - public class CollectionSaveFailed - { - - /// - /// Initializes a new - /// - /// The error that has occured while saving the specified - public CollectionSaveFailed(string error) - { - this.Error = error; - } - - /// - /// Gets the error that has occured while saving the specified - /// - public string? Error { get; } - - } - - /// - /// Represents the Flux action used to change the language of the text editor - /// - public class ChangeEditorLanguage - { - - /// - /// Initializes a new - /// - /// The language to use - /// The serialized collection - public ChangeEditorLanguage(string language, string serializedCollection) - { - this.Language = language ?? throw new ArgumentNullException(nameof(language)); - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the language to use - /// - public string Language { get; } - - /// - /// Gets the serialized collection - /// - public string SerializedCollection { get; } - } - - /// - /// Represents the Flux action used to toggle the state of the specified expander - /// - public class ToggleExpand - { - - /// - /// Initializes a new - /// - /// The name of the state to toggle - /// A boolean indicating whether or not the state is expanded - public ToggleExpand(string name, bool isExpanded) - { - this.Name = name; - this.IsExpanded = isExpanded; - } - - /// - /// Gets the name of the state to toggle - /// - public string Name { get; } - - /// - /// Gets a boolean indicating whether or not the state is expanded - /// - public bool IsExpanded { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorEffects.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorEffects.cs deleted file mode 100644 index feb4138f7..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorEffects.cs +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using Neuroglia.Data.Flux; -using Neuroglia.Serialization; -using Newtonsoft.Json; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Apis.Management; -using Synapse.Dashboard.Services; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Events -{ - - /// - /// Defines Flux effects that apply to -related Flux actions - /// - [Effect] - public static class EventDefinitionCollectionEditorEffects - { - - /// - /// Handles the state initialization - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(InitializeState action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - V1EventDefinitionCollection collection; - if (string.IsNullOrWhiteSpace(action.CollectionId)) - collection = new() { Name = "Undefined", Version = "0.1.0", Events = new List() }; - else - collection = (await context.Services.GetRequiredService().GetEventDefinitionCollectionByIdAsync(action.CollectionId)); - var serializedCollection = JsonConvert.SerializeObject(collection, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - serializedCollection = await yamlConverter.JsonToYaml(serializedCollection); - var initialState = new EventDefinitionCollectionEditorState() - { - Collection = collection, - SerializedCollection = serializedCollection, - Updating = false, - Saving = false, - ExpanderStates = new Dictionary() - { - { "general", true }, - { "events", true } - } - }; - context.Dispatcher.Dispatch(new InitializeStateSuccessful(initialState, action.IfNotExists)); - } - catch(Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the form editor changes - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(HandleFormBasedEditorChange action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateCollection(action.Collection)); - var serializedCollection = JsonConvert.SerializeObject(action.Collection, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - var yamlConverter = context.Services.GetRequiredService(); - serializedCollection = await yamlConverter.JsonToYaml(serializedCollection); - } - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - context.Dispatcher.Dispatch(new StopUpdating()); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the text editor changes - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(HandleTextBasedEditorChange action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - var jsonSerializer = context.Services.GetRequiredService(); - var serializedCollection = action.SerializedCollection; - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - serializedCollection = await yamlConverter.YamlToJson(serializedCollection); - var collection = await jsonSerializer.DeserializeAsync(serializedCollection); - context.Dispatcher.Dispatch(new UpdateCollection(collection)); - context.Dispatcher.Dispatch(new StopUpdating()); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the text editor language changes - /// - /// The action - /// The context - /// - /// - public static async Task On(ChangeEditorLanguage action, IEffectContext context) - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - context.Dispatcher.Dispatch(new StartUpdating()); - try - { - var serializedCollection = action.Language == PreferedLanguage.YAML ? - await yamlConverter.JsonToYaml(action.SerializedCollection) : - await yamlConverter.YamlToJson(action.SerializedCollection); - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - await monacoEditorHelper.ChangePreferedLanguage(action.Language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - } - context.Dispatcher.Dispatch(new StopUpdating()); - } - - /// - /// Saves the specified collection, - /// - /// The Flux action - /// The context - /// A new awaitable - public static async Task On(SaveCollection action, IEffectContext context) - { - try - { - var api = context.Services.GetRequiredService(); - var collection = await api.CreateEventDefinitionCollectionAsync(new() - { - Name = action.Collection.Name, - Version = action.Collection.Version, - Description = action.Collection.Description, - Events = action.Collection.Events - }); - context.Dispatcher.Dispatch(new CollectionSaved(collection)); - context.Dispatcher.Dispatch(new InitializeState(false)); - var navigationManager = context.Services.GetRequiredService(); - navigationManager.NavigateTo($"/resources/collections/events"); - } - catch (Exception ex) - { - context.Dispatcher.Dispatch(new CollectionSaveFailed(ex.Message)); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorReducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorReducers.cs deleted file mode 100644 index 985b30e53..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorReducers.cs +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Events -{ - - /// - /// Defines Flux reducers that apply to -related Flux actions - /// - [Reducer] - public static class EventDefinitionCollectionEditorReducers - { - - /// - /// Reduces the for the specified - /// - /// The to reduce - /// The Flux action to reduce - /// The reduced - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, InitializeStateSuccessful action) - { - if (action.IfNotExists && state.Initialized) - return state; - return action.InitialState with { Initialized = true }; - } - - /// - /// Changes the updating state when editing the definition form the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, StartUpdating action) - { - return state with - { - Updating = true - }; - } - - /// - /// Changes the updating state when failed to handle the value from the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, StopUpdating action) - { - return state with - { - Updating = false - }; - } - - /// - /// Changes the definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, UpdateCollection action) - { - return state with - { - Collection = action.Collection - }; - } - - /// - /// Notifies about the definition being saved - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, SaveCollection action) - { - return state with - { - Saving = true - }; - } - - /// - /// Notifies about the completion of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, CollectionSaved action) - { - return state with - { - Collection = action.Collection, - Saving = false - }; - } - - /// - /// Notifies about the failure of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, CollectionSaveFailed action) - { - return state with - { - Saving = false - }; - } - - /// - /// Changes the JSON definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, UpdateSerializedCollection action) - { - return state with - { - SerializedCollection = action.SerializedCollection - }; - } - - /// - /// Toggles the state of the specified expander - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static EventDefinitionCollectionEditorState On(EventDefinitionCollectionEditorState state, ToggleExpand action) - { - state.ExpanderStates![action.Name] = action.IsExpanded; - return state; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorSelectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorSelectors.cs deleted file mode 100644 index 6f278992e..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorSelectors.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Events -{ - - /// - /// Defines selectors for the - /// - public static class EventDefinitionCollectionEditorSelectors - { - - - /// - /// Selects the workflow definition - /// - /// - /// - public static IObservable SelectEventDefinitionCollection(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Collection) - .DistinctUntilChanged(); - } - - /// - /// Selects the workflow definition text - /// - /// - /// - public static IObservable SelectSerializedEventDefinitionCollection(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.SerializedCollection) - .DistinctUntilChanged(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorState.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorState.cs deleted file mode 100644 index da13eb5bb..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Events/EventDefinitionCollectionEditorState.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Events -{ - - /// - /// Represents the object used to maintain the state of a function definition collection editor - /// - [Feature] - public record EventDefinitionCollectionEditorState - { - - /// - /// Initializes a new - /// - public EventDefinitionCollectionEditorState() - { - - } - - /// - /// Gets/sets the collection to edit - /// - public Integration.Models.V1EventDefinitionCollection? Collection { get; set; } - - /// - /// Gets/sets the raw JSON/YAML of the collection to edit - /// - public string? SerializedCollection { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor has been initialized - /// - public bool Initialized { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor is saving the collection - /// - public bool Saving { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor is being updated - /// - public bool Updating { get; set; } - - /// - /// Gets a dictionary containing the name mappings of the editor's expanders states - /// - public Dictionary? ExpanderStates { get; set; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/Create.razor b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/Create.razor deleted file mode 100644 index f5f242663..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/Create.razor +++ /dev/null @@ -1,275 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/resources/collections/functions/new" -@page "/resources/collections/functions/edit/{collectionId}" -@using System.Reactive.Subjects -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbService -@inject IMonacoEditorHelper MonacoEditorHelper -@inject NavigationManager NavigationManager - -New function definition collection - - - New function definition collection - - - - - - - - - -@if(collection != null) -{ -

-
-
- -
-
General
-
- - - - - - - - - - - -
Name
Id -
- -
- - -
-
-
Description
Version
- -
-
-
- -
-
Functions
-
- - - - - - - - - - - - @if (collection.Functions != null) - { - @foreach (var function in collection.Functions) - { - -
-
- - - - - - - - - - } - } - -
NameTypeOperation
@function.Name@EnumHelper.Stringify(function.Type)@function.Operation - @if (!string.IsNullOrWhiteSpace(function.AuthRef)) - { - secured - } - - -
- - -
-
-
-
- -
-
-} - -@code -{ - - private bool autoGenerateId = true; - private string? collectionId; - [Parameter] public string? CollectionId { get; set; } - - private MonacoEditor? textBasedEditor; - private Subject? editorValue; - private Subject? disposeNotifier; - private V1FunctionDefinitionCollection? collection; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.CreateFunctionDefinitionCollection); - this.disposeNotifier = new Subject(); - this.editorValue = new Subject(); - this.editorValue - .Throttle(TimeSpan.FromMilliseconds(300)) - .DistinctUntilChanged() - .TakeUntil(this.disposeNotifier) - .Subscribe(text => this.Dispatcher.Dispatch(new HandleTextBasedEditorChange(text))); - FunctionDefinitionCollectionEditorSelectors.SelectSerializedFunctionDefinitionCollection(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (text) => - { - if (text != null && this.textBasedEditor != null) - { - var currentText = await this.textBasedEditor!.GetValue(); - if (currentText != text) - await this.textBasedEditor.SetValue(text); - } - }); - FunctionDefinitionCollectionEditorSelectors.SelectFunctionDefinitionCollection(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (definition) => - { - if (this.collection != definition) - { - this.collection = definition; - this.StateHasChanged(); - } - }); - if (string.IsNullOrWhiteSpace(this.collectionId)) - this.Dispatcher.Dispatch(new InitializeState()); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.collectionId != this.CollectionId) - { - this.collectionId = this.CollectionId; - if (!string.IsNullOrWhiteSpace(this.collectionId)) - this.Dispatcher.Dispatch(new InitializeState(this.collectionId)); - } - } - - protected async Task OnTextBasedEditorInit(MonacoEditorBase editor) - { - await this.OnSetTextBasedEditorLanguage(); - await this.textBasedEditor!.SetValue(this.State.SerializedCollection); - } - - protected async Task OnSetTextBasedEditorLanguage() - { - var model = await this.textBasedEditor!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - } - - protected virtual void OnToggleExpand(string sectionName, bool isExpanded) - { - this.Dispatcher.Dispatch(new ToggleExpand(sectionName, isExpanded)); - this.StateHasChanged(); - } - - protected virtual async Task OnAutoGenerateIdChanged(bool autoGenerate) - { - this.autoGenerateId = autoGenerate; - if (this.autoGenerateId) - await this.OnPropertyChanged(nameof(collection.Name), w => w.Id = w.Name.Slugify("-").ToLowerInvariant()); - } - - protected virtual async Task OnNameChanged(string? name) - { - if (this.collection == null) - return; - await this.OnPropertyChanged(nameof(collection.Name), w => - { - w.Name = name; - if (autoGenerateId && !string.IsNullOrWhiteSpace(name)) - w.Id = name.Slugify("-").ToLowerInvariant(); - }); - } - - protected virtual async Task OnCreateFunction() - { - if (this.collection == null) - return; - if (this.collection.Functions == null) - this.collection.Functions = new List(); - var function = new FunctionDefinition() { Name = $"function-{collection.Functions.Count + 1}" }; - await this.OnPropertyChanged(nameof(collection.Functions), d => - { - d.Functions!.Add(function); - }); - } - - protected virtual async Task OnFunctionChanged(FunctionDefinition? function) - { - await this.OnPropertyChanged(nameof(collection.Functions)); - } - - protected virtual async Task OnPropertyChanged(string property, Action? patch = null) - { - if (this.collection == null) - return; - if (patch != null) - patch(this.collection); - this.Dispatcher.Dispatch(new HandleFormBasedEditorChange(collection)); - await Task.CompletedTask; - } - - protected async Task OnSerializedCollectionChanged(ModelContentChangedEvent e) - { - if (!this.State.Updating && this.editorValue != null) - { - var text = await this.textBasedEditor!.GetValue(); - this.editorValue.OnNext(text); - } - } - - protected virtual void OnResetCollection() - { - this.Dispatcher.Dispatch(new InitializeState(false)); - } - - protected virtual void OnSaveCollection() - { - if (this.collection == null) - return; - this.Dispatcher.Dispatch(new SaveCollection(this.collection)); - this.StateHasChanged(); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorActions.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorActions.cs deleted file mode 100644 index 3d34a57a6..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorActions.cs +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using ServerlessWorkflow.Sdk.Models; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Functions -{ - - /// - /// Represents the Flux action used to initialize the - /// - public class InitializeState - { - - /// - /// Initializes a new - /// - /// A boolean indicating whether or not to initialize the state only if it does not yet exist - public InitializeState(bool ifNotExists = true) - { - this.IfNotExists = ifNotExists; - } - - /// - /// Initializes a new - /// - /// The id of the function definition collection to initialize the state with - public InitializeState(string collectionId) - { - this.CollectionId = collectionId; - } - - /// - /// Gets/sets the id of the function definition collection to initialize the state with - /// - public string? CollectionId { get; } - - /// - /// Gets/sets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - - } - - /// - /// Represents the Flux action used to set the initialized state - /// - public class InitializeStateSuccessful - { - - /// - /// Initializes a new - /// - /// The initial state - /// A boolean indicating whether or not to initialize the state only if it does not yet exist - public InitializeStateSuccessful(FunctionDefinitionCollectionEditorState initialState, bool ifNotExists = true) - { - this.InitialState = initialState ?? throw new ArgumentNullException(nameof(initialState)); - this.IfNotExists = ifNotExists; - } - - /// - /// Gets the initial state - /// - public FunctionDefinitionCollectionEditorState InitialState { get; } - - /// - /// Gets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - - } - - /// - /// Represents the Flux action to handle changes in the form based editor - /// - public class HandleFormBasedEditorChange - { - - /// - /// Initialised a new action with the provided definition - /// - /// The updated - public HandleFormBasedEditorChange(V1FunctionDefinitionCollection collection) - { - this.Collection = collection ?? throw new ArgumentNullException(nameof(collection)); - } - - /// - /// Gets the updated - /// - public V1FunctionDefinitionCollection Collection { get; } - - } - - /// - /// Represents the Flux action to handle changes in the text based editor - /// - public class HandleTextBasedEditorChange - { - - /// - /// Initializes a new action with the provided definition - /// - /// The updated serialized collection - public HandleTextBasedEditorChange(string serializedCollection) - { - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the updated serialized collection - /// - public string SerializedCollection { get; } - - } - - /// - /// Represents the Flux action to update the edited function definition collection - /// - public class UpdateCollection - { - - /// - /// Initializes a new - /// - /// The updated - public UpdateCollection(V1FunctionDefinitionCollection collection) - { - this.Collection = collection ?? throw new ArgumentNullException(nameof(collection)); - } - - /// - /// Gets the updated - /// - public V1FunctionDefinitionCollection Collection { get; } - } - - /// - /// Represents the Flux action used to update the edited function definition collection - /// - public class UpdateSerializedCollection - { - /// - /// Initializes a new - /// - /// The updated serialized collection - public UpdateSerializedCollection(string serializedCollection) - { - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the updated serialized collection - /// - public string SerializedCollection { get; } - - } - - /// - /// Represents the Flux action dispatched when the editor starts to update - /// - public class StartUpdating { } - - /// - /// Represents the Flux action dispatched when the editor finished updating - /// - public class StopUpdating { } - - /// - /// Represents the Flux action used to save the specified using the Synapse API - /// - public class SaveCollection - { - - /// - /// Initializes a new - /// - /// The to save - public SaveCollection(V1FunctionDefinitionCollection collection) - { - this.Collection = collection; - } - - /// - /// Gets the to save - /// - public V1FunctionDefinitionCollection Collection { get; } - - } - - /// - /// Represents the Flux action used to notify the UI about the completion of the specified 's save - /// - public class CollectionSaved - { - - /// - /// Initializes a new - /// - /// The that has been saved - public CollectionSaved(V1FunctionDefinitionCollection collection) - { - this.Collection = collection; - } - - /// - /// Gets the that has been saved - /// - public V1FunctionDefinitionCollection Collection { get; } - - } - - /// - /// Notifies the UI about the failure of a 's save - /// - public class CollectionSaveFailed - { - - /// - /// Initializes a new - /// - /// The error that has occured while saving the specified - public CollectionSaveFailed(string error) - { - this.Error = error; - } - - /// - /// Gets the error that has occured while saving the specified - /// - public string? Error { get; } - - } - - /// - /// Represents the Flux action used to change the language of the text editor - /// - public class ChangeEditorLanguage - { - - /// - /// Initializes a new - /// - /// The language to use - /// The serialized collection - public ChangeEditorLanguage(string language, string serializedCollection) - { - this.Language = language ?? throw new ArgumentNullException(nameof(language)); - this.SerializedCollection = serializedCollection ?? throw new ArgumentNullException(nameof(serializedCollection)); - } - - /// - /// Gets the language to use - /// - public string Language { get; } - - /// - /// Gets the serialized collection - /// - public string SerializedCollection { get; } - } - - /// - /// Represents the Flux action used to toggle the state of the specified expander - /// - public class ToggleExpand - { - - /// - /// Initializes a new - /// - /// The name of the state to toggle - /// A boolean indicating whether or not the state is expanded - public ToggleExpand(string name, bool isExpanded) - { - this.Name = name; - this.IsExpanded = isExpanded; - } - - /// - /// Gets the name of the state to toggle - /// - public string Name { get; } - - /// - /// Gets a boolean indicating whether or not the state is expanded - /// - public bool IsExpanded { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorEffects.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorEffects.cs deleted file mode 100644 index bd1a2c580..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorEffects.cs +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.Components; -using Neuroglia.Data.Flux; -using Neuroglia.Serialization; -using Newtonsoft.Json; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Apis.Management; -using Synapse.Dashboard.Services; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Functions -{ - - /// - /// Defines Flux effects that apply to -related Flux actions - /// - [Effect] - public static class FunctionDefinitionCollectionEditorEffects - { - - /// - /// Handles the state initialization - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(InitializeState action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - V1FunctionDefinitionCollection collection; - if (string.IsNullOrWhiteSpace(action.CollectionId)) - collection = new() { Name = "Undefined", Version = "0.1.0", Functions = new List() }; - else - collection = (await context.Services.GetRequiredService().GetFunctionDefinitionCollectionByIdAsync(action.CollectionId)); - var serializedCollection = JsonConvert.SerializeObject(collection, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - serializedCollection = await yamlConverter.JsonToYaml(serializedCollection); - var initialState = new FunctionDefinitionCollectionEditorState() - { - Collection = collection, - SerializedCollection = serializedCollection, - Updating = false, - Saving = false, - ExpanderStates = new Dictionary() - { - { "general", true }, - { "functions", true } - } - }; - context.Dispatcher.Dispatch(new InitializeStateSuccessful(initialState, action.IfNotExists)); - } - catch(Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the form editor changes - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(HandleFormBasedEditorChange action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateCollection(action.Collection)); - var serializedCollection = JsonConvert.SerializeObject(action.Collection, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - var yamlConverter = context.Services.GetRequiredService(); - serializedCollection = await yamlConverter.JsonToYaml(serializedCollection); - } - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - context.Dispatcher.Dispatch(new StopUpdating()); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the text editor changes - /// - /// The action - /// The context - /// A new awaitable - public static async Task On(HandleTextBasedEditorChange action, IEffectContext context) - { - try - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - var jsonSerializer = context.Services.GetRequiredService(); - var serializedCollection = action.SerializedCollection; - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - serializedCollection = await yamlConverter.YamlToJson(serializedCollection); - var collection = await jsonSerializer.DeserializeAsync(serializedCollection); - context.Dispatcher.Dispatch(new UpdateCollection(collection)); - context.Dispatcher.Dispatch(new StopUpdating()); - } - catch (Exception ex) - { - context.Services.GetRequiredService>().LogError(ex.ToString()); - } - } - - /// - /// Handles the text editor language changes - /// - /// The action - /// The context - /// - /// - public static async Task On(ChangeEditorLanguage action, IEffectContext context) - { - var monacoEditorHelper = context.Services.GetRequiredService(); - var yamlConverter = context.Services.GetRequiredService(); - context.Dispatcher.Dispatch(new StartUpdating()); - try - { - var serializedCollection = action.Language == PreferedLanguage.YAML ? - await yamlConverter.JsonToYaml(action.SerializedCollection) : - await yamlConverter.YamlToJson(action.SerializedCollection); - context.Dispatcher.Dispatch(new UpdateSerializedCollection(serializedCollection)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - await monacoEditorHelper.ChangePreferedLanguage(action.Language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - } - context.Dispatcher.Dispatch(new StopUpdating()); - } - - /// - /// Saves the specified collection, - /// - /// The Flux action - /// The context - /// A new awaitable - public static async Task On(SaveCollection action, IEffectContext context) - { - try - { - var api = context.Services.GetRequiredService(); - var collection = await api.CreateFunctionDefinitionCollectionAsync(new() - { - Name = action.Collection.Name, - Version = action.Collection.Version, - Description = action.Collection.Description, - Functions = action.Collection.Functions - }); - context.Dispatcher.Dispatch(new CollectionSaved(collection)); - context.Dispatcher.Dispatch(new InitializeState(false)); - var navigationManager = context.Services.GetRequiredService(); - navigationManager.NavigateTo($"/resources"); - } - catch (Exception ex) - { - context.Dispatcher.Dispatch(new CollectionSaveFailed(ex.Message)); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorReducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorReducers.cs deleted file mode 100644 index 02c8c1c7a..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorReducers.cs +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Functions -{ - - /// - /// Defines Flux reducers that apply to -related Flux actions - /// - [Reducer] - public static class FunctionDefinitionCollectionEditorReducers - { - - /// - /// Reduces the for the specified - /// - /// The to reduce - /// The Flux action to reduce - /// The reduced - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, InitializeStateSuccessful action) - { - if (action.IfNotExists && state.Initialized) - return state; - return action.InitialState with { Initialized = true }; - } - - /// - /// Changes the updating state when editing the definition form the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, StartUpdating action) - { - return state with - { - Updating = true - }; - } - - /// - /// Changes the updating state when failed to handle the value from the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, StopUpdating action) - { - return state with - { - Updating = false - }; - } - - /// - /// Changes the definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, UpdateCollection action) - { - return state with - { - Collection = action.Collection - }; - } - - /// - /// Notifies about the definition being saved - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, SaveCollection action) - { - return state with - { - Saving = true - }; - } - - /// - /// Notifies about the completion of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, CollectionSaved action) - { - return state with - { - Collection = action.Collection, - Saving = false - }; - } - - /// - /// Notifies about the failure of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, CollectionSaveFailed action) - { - return state with - { - Saving = false - }; - } - - /// - /// Changes the JSON definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, UpdateSerializedCollection action) - { - return state with - { - SerializedCollection = action.SerializedCollection - }; - } - - /// - /// Toggles the state of the specified expander - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static FunctionDefinitionCollectionEditorState On(FunctionDefinitionCollectionEditorState state, ToggleExpand action) - { - state.ExpanderStates![action.Name] = action.IsExpanded; - return state; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorSelectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorSelectors.cs deleted file mode 100644 index 173d08c90..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorSelectors.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Functions -{ - - /// - /// Defines selectors for the - /// - public static class FunctionDefinitionCollectionEditorSelectors - { - - - /// - /// Selects the workflow definition - /// - /// - /// - public static IObservable SelectFunctionDefinitionCollection(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Collection) - .DistinctUntilChanged(); - } - - /// - /// Selects the workflow definition text - /// - /// - /// - public static IObservable SelectSerializedFunctionDefinitionCollection(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.SerializedCollection) - .DistinctUntilChanged(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorState.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorState.cs deleted file mode 100644 index 43727fb6f..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/Functions/FunctionDefinitionCollectionEditorState.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections.Functions -{ - - /// - /// Represents the object used to maintain the state of a function definition collection editor - /// - [Feature] - public record FunctionDefinitionCollectionEditorState - { - - /// - /// Initializes a new - /// - public FunctionDefinitionCollectionEditorState() - { - - } - - /// - /// Gets/sets the collection to edit - /// - public V1FunctionDefinitionCollection? Collection { get; set; } - - /// - /// Gets/sets the raw JSON/YAML of the collection to edit - /// - public string? SerializedCollection { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor has been initialized - /// - public bool Initialized { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor is saving the collection - /// - public bool Saving { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the editor is being updated - /// - public bool Updating { get; set; } - - /// - /// Gets a dictionary containing the name mappings of the editor's expanders states - /// - public Dictionary? ExpanderStates { get; set; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListActions.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListActions.cs deleted file mode 100644 index a1fb0943f..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListActions.cs +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using OData.QueryBuilder.Builders; -using OData.QueryBuilder.Conventions.AddressingEntities.Query; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections -{ - - /// - /// Represents the action used to query s - /// - public class QueryV1FunctionDefinitionCollections - { - - /// - /// Initializes a new - /// - public QueryV1FunctionDefinitionCollections() - { - - } - - /// - /// Initializes a new - /// - /// The term to search for - /// An used to setup the query to perform - public QueryV1FunctionDefinitionCollections(string? searchTerm, Action> querySetup) - { - var builder = new ODataQueryBuilder(new Uri("https://test.com")) - .For("V1FunctionDefinitionCollections") - .ByList(); - querySetup(builder); - Query = builder.ToUri(UriKind.Absolute).Query; - if (!string.IsNullOrWhiteSpace(searchTerm)) - Query = $"$search={searchTerm}&{Query}"; - } - - /// - /// Initializes a new - /// - /// The term to search for - public QueryV1FunctionDefinitionCollections(string searchTerm) - : this(searchTerm, _ => { }) - { - - } - - /// - /// Initializes a new - /// - /// An used to setup the query to perform - public QueryV1FunctionDefinitionCollections(Action> querySetup) - : this(null, querySetup) - { - - } - - /// - /// Gets the query to perform - /// - public string? Query { get; } - - } - - /// - /// Represents the action used to set the currently available s - /// - public class SetV1FunctionDefinitionCollections - { - - /// - /// Initializes a new - /// - /// A containing the currently available s - public SetV1FunctionDefinitionCollections(List collections) - { - Collections = collections; - } - - /// - /// Gets a containing the currently available s - /// - public List Collections { get; } - - } - - /// - /// Represents the action used to add a function definition to the current - /// - public class AddV1FunctionDefinitionCollection - { - - /// - /// Initializes a new - /// - /// The to add - public AddV1FunctionDefinitionCollection(V1FunctionDefinitionCollection collection) - { - Collection = collection; - } - - /// - /// Gets the to add - /// - public V1FunctionDefinitionCollection Collection { get; } - - } - - /// - /// Represents the action used to removed a function definition from the current - /// - public class RemoveV1FunctionDefinitionCollection - { - - /// - /// Initializes a new - /// - /// The id of the to remove - public RemoveV1FunctionDefinitionCollection(string collectionId) - { - CollectionId = collectionId; - } - - /// - /// Gets the id of the to remove - /// - public string CollectionId { get; } - - } - - /// - /// Represents the action used to query s - /// - public class QueryV1EventDefinitionCollections - { - - /// - /// Initializes a new - /// - public QueryV1EventDefinitionCollections() - { - - } - - /// - /// Initializes a new - /// - /// The term to search for - /// An used to setup the query to perform - public QueryV1EventDefinitionCollections(string? searchTerm, Action> querySetup) - { - var builder = new ODataQueryBuilder(new Uri("https://test.com")) - .For("V1EventDefinitionCollections") - .ByList(); - querySetup(builder); - this.Query = builder.ToUri(UriKind.Absolute).Query; - if (!string.IsNullOrWhiteSpace(searchTerm)) - this.Query = $"$search={searchTerm}&{Query}"; - } - - /// - /// Initializes a new - /// - /// The term to search for - public QueryV1EventDefinitionCollections(string searchTerm) - : this(searchTerm, _ => { }) - { - - } - - /// - /// Initializes a new - /// - /// An used to setup the query to perform - public QueryV1EventDefinitionCollections(Action> querySetup) - : this(null, querySetup) - { - - } - - /// - /// Gets the query to perform - /// - public string? Query { get; } - - } - - /// - /// Represents the action used to set the currently available s - /// - public class SetV1EventDefinitionCollections - { - - /// - /// Initializes a new - /// - /// A containing the currently available s - public SetV1EventDefinitionCollections(List collections) - { - Collections = collections; - } - - /// - /// Gets a containing the currently available s - /// - public List Collections { get; } - - } - - /// - /// Represents the action used to add a event definition to the current - /// - public class AddV1EventDefinitionCollection - { - - /// - /// Initializes a new - /// - /// The to add - public AddV1EventDefinitionCollection(V1EventDefinitionCollection collection) - { - Collection = collection; - } - - /// - /// Gets the to add - /// - public V1EventDefinitionCollection Collection { get; } - - } - - /// - /// Represents the action used to removed a event definition from the current - /// - public class RemoveV1EventDefinitionCollection - { - - /// - /// Initializes a new - /// - /// The id of the to remove - public RemoveV1EventDefinitionCollection(string collectionId) - { - CollectionId = collectionId; - } - - /// - /// Gets the id of the to remove - /// - public string CollectionId { get; } - - } - - /// - /// Represents the action used to query s - /// - public class QueryV1AuthenticationDefinitionCollections - { - - /// - /// Initializes a new - /// - public QueryV1AuthenticationDefinitionCollections() - { - - } - - /// - /// Initializes a new - /// - /// The term to search for - /// An used to setup the query to perform - public QueryV1AuthenticationDefinitionCollections(string? searchTerm, Action> querySetup) - { - var builder = new ODataQueryBuilder(new Uri("https://test.com")) - .For("V1AuthenticationDefinitionCollections") - .ByList(); - querySetup(builder); - Query = builder.ToUri(UriKind.Absolute).Query; - if (!string.IsNullOrWhiteSpace(searchTerm)) - Query = $"$search={searchTerm}&{Query}"; - } - - /// - /// Initializes a new - /// - /// The term to search for - public QueryV1AuthenticationDefinitionCollections(string searchTerm) - : this(searchTerm, _ => { }) - { - - } - - /// - /// Initializes a new - /// - /// An used to setup the query to perform - public QueryV1AuthenticationDefinitionCollections(Action> querySetup) - : this(null, querySetup) - { - - } - - /// - /// Gets the query to perform - /// - public string? Query { get; } - - } - - /// - /// Represents the action used to set the currently available s - /// - public class SetV1AuthenticationDefinitionCollections - { - - /// - /// Initializes a new - /// - /// A containing the currently available s - public SetV1AuthenticationDefinitionCollections(List collections) - { - this.Collections = collections; - } - - /// - /// Gets a containing the currently available s - /// - public List Collections { get; } - - } - - /// - /// Represents the action used to add a authentication definition to the current - /// - public class AddV1AuthenticationDefinitionCollection - { - - /// - /// Initializes a new - /// - /// The to add - public AddV1AuthenticationDefinitionCollection(V1AuthenticationDefinitionCollection collection) - { - this.Collection = collection; - } - - /// - /// Gets the to add - /// - public V1AuthenticationDefinitionCollection Collection { get; } - - } - - /// - /// Represents the action used to removed a authentication definition from the current - /// - public class RemoveV1AuthenticationDefinitionCollection - { - - /// - /// Initializes a new - /// - /// The id of the to remove - public RemoveV1AuthenticationDefinitionCollection(string collectionId) - { - this.CollectionId = collectionId; - } - - /// - /// Gets the id of the to remove - /// - public string CollectionId { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListEffects.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListEffects.cs deleted file mode 100644 index aedcba16d..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListEffects.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections -{ - - /// - /// Defines the Flux effects applying to -related actions - /// - [Effect] - public static class ResourceListEffects - { - - /// - /// Queries s - /// - /// The Flux action the effect applies to - /// The current - /// A new awaitable - public static async Task On(QueryV1FunctionDefinitionCollections action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var collections = await api.GetFunctionDefinitionCollectionsAsync(action.Query); - context.Dispatcher.Dispatch(new SetV1FunctionDefinitionCollections(collections)); - } - - /// - /// Queries s - /// - /// The Flux action the effect applies to - /// The current - /// A new awaitable - public static async Task On(QueryV1EventDefinitionCollections action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var collections = await api.GetEventDefinitionCollectionsAsync(action.Query); - context.Dispatcher.Dispatch(new SetV1EventDefinitionCollections(collections)); - } - - /// - /// Queries s - /// - /// The Flux action the effect applies to - /// The current - /// A new awaitable - public static async Task On(QueryV1AuthenticationDefinitionCollections action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var collections = await api.GetAuthenticationDefinitionCollectionsAsync(action.Query); - context.Dispatcher.Dispatch(new SetV1AuthenticationDefinitionCollections(collections)); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListReducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListReducers.cs deleted file mode 100644 index 763f2514b..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListReducers.cs +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Resources.Collections -{ - - /// - /// Defines Flux reducers for -related actions - /// - [Reducer] - public static class ResourceListReducers - { - - /// - /// Sets the current 's - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, SetV1FunctionDefinitionCollections action) - { - return state with - { - FunctionDefinitionCollections = action.Collections - }; - } - - /// - /// Adds the specified to the current state - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, AddV1FunctionDefinitionCollection action) - { - var collections = state.FunctionDefinitionCollections; - collections.Add(action.Collection); - return state with - { - FunctionDefinitionCollections = collections - }; - } - - /// - /// Removes the specified from the current state - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, RemoveV1FunctionDefinitionCollection action) - { - var collections = state.FunctionDefinitionCollections; - var collection = collections.FirstOrDefault(c => c.Id.Equals(action.CollectionId, StringComparison.InvariantCultureIgnoreCase)); - if (collection == null) - return state; - collections.Remove(collection); - return state with - { - FunctionDefinitionCollections = collections - }; - } - - /// - /// Sets the current 's - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, SetV1EventDefinitionCollections action) - { - return state with - { - EventDefinitionCollections = action.Collections - }; - } - - /// - /// Adds the specified to the current state - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, AddV1EventDefinitionCollection action) - { - var collections = state.EventDefinitionCollections; - collections.Add(action.Collection); - return state with - { - EventDefinitionCollections = collections - }; - } - - /// - /// Removes the specified from the current state - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, RemoveV1EventDefinitionCollection action) - { - var collections = state.EventDefinitionCollections; - var collection = collections.FirstOrDefault(c => c.Id.Equals(action.CollectionId, StringComparison.InvariantCultureIgnoreCase)); - if (collection == null) - return state; - collections.Remove(collection); - return state with - { - EventDefinitionCollections = collections - }; - } - - /// - /// Sets the current 's - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, SetV1AuthenticationDefinitionCollections action) - { - return state with - { - AuthenticationDefinitionCollections = action.Collections - }; - } - - /// - /// Adds the specified to the current state - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, AddV1AuthenticationDefinitionCollection action) - { - var collections = state.AuthenticationDefinitionCollections; - collections.Add(action.Collection); - return state with - { - AuthenticationDefinitionCollections = collections - }; - } - - /// - /// Removes the specified from the current state - /// - /// The to reduce - /// The Flux action used to reduce the specified - /// The reduced - public static ResourceListState On(ResourceListState state, RemoveV1AuthenticationDefinitionCollection action) - { - var collections = state.AuthenticationDefinitionCollections; - var collection = collections.FirstOrDefault(c => c.Id.Equals(action.CollectionId, StringComparison.InvariantCultureIgnoreCase)); - if (collection == null) - return state; - collections.Remove(collection); - return state with - { - AuthenticationDefinitionCollections = collections - }; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListState.cs b/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListState.cs deleted file mode 100644 index 9ee402933..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/Collections/ResourceListState.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Resources.Collections -{ - - /// - /// Represents the Flux state used to manage s - /// - [Feature] - public record ResourceListState - { - - /// - /// Initializes a new - /// - public ResourceListState() - { - - } - - /// - /// Initializes a new - /// - /// A containing the currently available s - public ResourceListState(List functionDefinitionCollections, List eventDefinitionCollections, List authenticationDefinitionCollections) - { - this.FunctionDefinitionCollections = functionDefinitionCollections; - this.EventDefinitionCollections = eventDefinitionCollections; - this.AuthenticationDefinitionCollections = authenticationDefinitionCollections; - } - - /// - /// Gets a containing the currently available s - /// - public List FunctionDefinitionCollections { get; set; } = null!; - - /// - /// Gets a containing the currently available s - /// - public List EventDefinitionCollections { get; set; } = null!; - - /// - /// Gets a containing the currently available s - /// - public List AuthenticationDefinitionCollections { get; set; } = null!; - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Resources/List.razor b/src/dashboard/Synapse.Dashboard/Pages/Resources/List.razor deleted file mode 100644 index 30bb15cef..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Resources/List.razor +++ /dev/null @@ -1,385 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/resources" -@page "/resources/collections/{definitionType}" -@using Semver -@using Synapse.Dashboard.Pages.Resources.Collections -@using Synapse.Dashboard.Pages.Resources.Collections.Functions -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbService -@inject NavigationManager NavigationManager - -Resources - - - - @switch (selectedDefinitionType) - { - case "events": - - break; - case "authentications": - - break; - default: - - break; - } - - - - - -

-
-
- -
-
- - - -
- -
- -
- - - - - - - - - - @context.Value - - - - -
- - - - - - - - - - - - @context.Value - - - - -
-
-
- - -
-
-
- -
-
- - - -
- -
- -
- - - - - - - - - - @context.Value - - - - -
- - - - - - - - - - - - @context.Value - - - - -
-
-
-
- -
-
-
- -
-
- - - -
- -
- -
- - - - - - - - - - @context.Value - - - - -
- - - - - - - - - - - - @context.Value - - - - -
-
-
-
- - -@code { - - private Dictionary> functionDefinitionCollections = null!; - private Dictionary> eventDefinitionCollections = null!; - private Dictionary> authenticationDefinitionCollections = null!; - private IDisposable subscription = null!; - private TabPage? activeTabPage; - private string selectedDefinitionType = "functions"; - - private string? definitionType; - [Parameter] - public string? DefinitionType { get; set; } - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.subscription = this.Feature - .Subscribe(state => - { - if(state.FunctionDefinitionCollections != null) - this.functionDefinitionCollections = state.FunctionDefinitionCollections - .GroupBy(c => c.Name) - .ToDictionary(g => g.Key, g => g.OrderByDescending(c => SemVersion.Parse(c.Version, SemVersionStyles.Any)).ToList())!; - if (state.EventDefinitionCollections != null) - this.eventDefinitionCollections = state.EventDefinitionCollections - .GroupBy(c => c.Name) - .ToDictionary(g => g.Key, g => g.OrderByDescending(c => SemVersion.Parse(c.Version, SemVersionStyles.Any)).ToList())!; - if (state.AuthenticationDefinitionCollections != null) - this.authenticationDefinitionCollections = state.AuthenticationDefinitionCollections - .GroupBy(c => c.Name) - .ToDictionary(g => g.Key, g => g.OrderByDescending(c => SemVersion.Parse(c.Version, SemVersionStyles.Any)).ToList())!; - this.StateHasChanged(); - }); - this.Dispatcher.Dispatch(new QueryV1FunctionDefinitionCollections()); - this.Dispatcher.Dispatch(new QueryV1EventDefinitionCollections()); - this.Dispatcher.Dispatch(new QueryV1AuthenticationDefinitionCollections()); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.definitionType != this.DefinitionType) - { - this.definitionType = this.DefinitionType; - this.selectedDefinitionType = this.definitionType?.ToLower()!; - await this.UpdateBreadcrumbAsync(); - this.StateHasChanged(); - } - } - - protected virtual async Task UpdateBreadcrumbAsync() - { - IEnumerable breadcrumb; - switch (this.selectedDefinitionType) - { - case "events": - breadcrumb = Breadcrumbs.EventDefinitionCollections; - break; - case "authentications": - breadcrumb = Breadcrumbs.AuthenticationDefinitionCollections; - break; - default: - breadcrumb = Breadcrumbs.FunctionDefinitionCollections; - break; - } - await this.BreadcrumbService.Use(breadcrumb); - } - - protected virtual async Task OnTabPageChanged(TabPage page) - { - this.activeTabPage = page; - this.selectedDefinitionType = page.Header?.ToLower()!; - await this.UpdateBreadcrumbAsync(); - this.StateHasChanged(); - } - - protected virtual void OnSearchFunctionDefinitionCollection(string searchTerm) - { - this.Dispatcher.Dispatch(new QueryV1FunctionDefinitionCollections(searchTerm)); - } - - protected virtual void OnClearFunctionDefinitionCollectionSearch() - { - this.Dispatcher.Dispatch(new QueryV1FunctionDefinitionCollections()); - } - - protected virtual void OnViewFunctionDefinitionCollection(V1FunctionDefinitionCollection collection) - { - this.NavigationManager.NavigateTo($"/resources/collections/functions/{collection.Id}"); - } - - protected virtual void OnCreateFunctionDefinitionCollection() - { - this.NavigationManager.NavigateTo("/resources/collections/functions/new"); - } - - protected virtual void OnSearchEventDefinitionCollection(string searchTerm) - { - this.Dispatcher.Dispatch(new QueryV1EventDefinitionCollections(searchTerm)); - } - - protected virtual void OnClearEventDefinitionCollectionSearch() - { - this.Dispatcher.Dispatch(new QueryV1EventDefinitionCollections()); - } - - protected virtual void OnViewEventDefinitionCollection(V1EventDefinitionCollection collection) - { - this.NavigationManager.NavigateTo($"/resources/collections/events/{collection.Id}"); - } - - protected virtual void OnCreateEventDefinitionCollection() - { - this.NavigationManager.NavigateTo("/resources/collections/events/new"); - } - - protected virtual void OnSearchAuthenticationDefinitionCollection(string searchTerm) - { - this.Dispatcher.Dispatch(new QueryV1AuthenticationDefinitionCollections(searchTerm)); - } - - protected virtual void OnClearAuthenticationDefinitionCollectionSearch() - { - this.Dispatcher.Dispatch(new QueryV1AuthenticationDefinitionCollections()); - } - - protected virtual void OnViewAuthenticationDefinitionCollection(V1AuthenticationDefinitionCollection collection) - { - this.NavigationManager.NavigateTo($"/resources/collections/authentications/{collection.Id}"); - } - - protected virtual void OnCreateAuthenticationDefinitionCollection() - { - this.NavigationManager.NavigateTo("/resources/collections/authentications/new"); - } - - protected override void Dispose(bool disposing) - { - if(disposing) - { - this.subscription?.Dispose(); - } - base.Dispose(disposing); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Actions.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Actions.cs deleted file mode 100644 index 3b1deede7..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Actions.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Commands.Schedules; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Schedules.Create -{ - - /// - /// Represents the Flux action used to create a new - /// - public class CreateSchedule - { - - /// - /// Initializes a new - /// - /// The command to execute - public CreateSchedule(V1CreateScheduleCommand command) - { - this.Command = command; - } - - /// - /// Gets the command to execute - /// - public V1CreateScheduleCommand Command { get; } - - } - - /// - /// Represents a Flux action used to handle the differed result of a action - /// - public class HandleCreateScheduleResult - { - - /// - /// Initializes a new - /// - /// The newly created - public HandleCreateScheduleResult(V1Schedule schedule) - { - this.Schedule = schedule; - } - - /// - /// Initializes a new - /// - /// The that occured during the 's creation - public HandleCreateScheduleResult(Exception exception) - { - this.Exception = exception; - } - - /// - /// Gets the newly created - /// - public V1Schedule? Schedule { get; } - - /// - /// Gets the that occured during the 's creation - /// - public Exception? Exception { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Effects.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Effects.cs deleted file mode 100644 index 695b8ff6a..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Effects.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; - -namespace Synapse.Dashboard.Pages.Schedules.Create -{ - - /// - /// Defines Flux effects applying to -related actions - /// - [Effect] - public static class Effects - { - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(CreateSchedule action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - try - { - var schedule = await api.CreateScheduleAsync(action.Command); - context.Dispatcher.Dispatch(new HandleCreateScheduleResult(schedule)); - } - catch(Exception ex) - { - context.Dispatcher.Dispatch(new HandleCreateScheduleResult(ex)); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Reducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Reducers.cs deleted file mode 100644 index 105f3a2ca..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Reducers.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Schedules.Create -{ - - /// - /// Defines Flux reducers applying to -related actions - /// - [Reducer] - public static class Reducers - { - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static CreateScheduleState On(CreateScheduleState state, HandleCreateScheduleResult action) - { - return state with - { - Error = action.Exception, - Schedule = action.Schedule - }; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Selectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Selectors.cs deleted file mode 100644 index 403e863d2..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/Selectors.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Schedules.Create -{ - - /// - /// Defines selectors for s - /// - public static class CreateScheduleStateSelectors - { - - /// - /// Selects the newly created - /// - /// The global - /// A new - public static IObservable SelectCreatedSchedule(IStore store) - { - return store.GetFeature() - .Select(state => state.Schedule) - .DistinctUntilChanged(); - } - - /// - /// Selects the error that might have occured during the creation - /// - /// The global - /// A new - public static IObservable SelectError(IStore store) - { - return store.GetFeature() - .Select(state => state.Error) - .DistinctUntilChanged(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/State.cs deleted file mode 100644 index 1f65881a4..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/State.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Schedules.Create -{ - - - /// - /// Represents the Flux state of the view used to create a new - /// - [Feature] - public record CreateScheduleState - { - - /// - /// Gets a boolean indicating whether or not the schedule is being created - /// - public bool Creating { get; init; } - - /// - /// Gets the newly created - /// - public V1Schedule? Schedule { get; init; } - - /// - /// Gets the that has occured during the 's creation - /// - public Exception? Error { get; init; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/View.razor deleted file mode 100644 index 4a5ca4634..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/Create/View.razor +++ /dev/null @@ -1,119 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/schedules/new" -@using Synapse.Integration.Commands.Schedules -@using System.Reactive.Subjects -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbService -@inject ISynapseManagementApi SynapseApi -@inject NavigationManager NavigationManager - -New schedule - - - - -
- -
-
- -@if (State.Creating) -{ -
- -
-} -else -{ -
- - - - - - - - - - - -
Definition - -
Workflow - -
-
-} - -@code -{ - - private Subject? disposeNotifier = null!; - private V1CreateScheduleCommand command = new() { ActivationType = V1ScheduleActivationType.Explicit }; - private V1WorkflowReference? workflowReference => string.IsNullOrWhiteSpace(command.WorkflowId) ? null : V1WorkflowReference.Parse(command.WorkflowId); - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.disposeNotifier = new Subject(); - CreateScheduleStateSelectors.SelectCreatedSchedule(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(schedule => - { - if (schedule != null) this.NavigationManager.NavigateTo("/schedules"); - }); - CreateScheduleStateSelectors.SelectError(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(errors => - { - - }); - - await this.BreadcrumbService.Use(Breadcrumbs.CreateSchedule); - } - - private void OnChanged(Action patch) - { - if (this.command == null) this.command = new(); - patch(this.command); - this.StateHasChanged(); - } - - private void OnCreateSchedule() - { - if (this.command == null) return; - this.Dispatcher.Dispatch(new CreateSchedule(this.command)); - } - - void OnViewScheduleList() - { - this.NavigationManager.NavigateTo("/schedules"); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - this.disposeNotifier?.OnNext(true); - this.disposeNotifier?.OnCompleted(); - this.disposeNotifier?.Dispose(); - this.disposeNotifier = null; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Actions.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Actions.cs deleted file mode 100644 index 5debfafc2..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Actions.cs +++ /dev/null @@ -1,87 +0,0 @@ -using OData.QueryBuilder.Builders; -using OData.QueryBuilder.Conventions.AddressingEntities.Query; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Schedules.List -{ - - /// - /// Represents the action used to query s - /// - public class QuerySchedules - { - - /// - /// Initializes a new - /// - public QuerySchedules() - { - - } - - /// - /// Initializes a new - /// - /// The term to search for - /// An used to setup the query to perform - public QuerySchedules(string? searchTerm, Action> querySetup) - { - var builder = new ODataQueryBuilder(new Uri("https://test.com")) - .For("V1V1Schedules") - .ByList(); - querySetup(builder); - Query = builder.ToUri(UriKind.Absolute).Query; - if (!string.IsNullOrWhiteSpace(searchTerm)) - Query = $"$search={searchTerm}&{Query}"; - } - - /// - /// Initializes a new - /// - /// The term to search for - public QuerySchedules(string searchTerm) - : this(searchTerm, _ => { }) - { - - } - - /// - /// Initializes a new - /// - /// An used to setup the query to perform - public QuerySchedules(Action> querySetup) - : this(null, querySetup) - { - - } - - /// - /// Gets the query to perform - /// - public string? Query { get; } - - } - - /// - /// Represents the action used to handle the differed results of a action - /// - public class HandleScheduleQueryResults - { - - /// - /// Initializes a new - /// - /// A that contains the differed results of the action - public HandleScheduleQueryResults(List results) - { - this.Results = results; - } - - /// - /// Gets a that contains the differed results of the action - /// - public List Results { get; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Effects.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Effects.cs deleted file mode 100644 index ffc256142..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Effects.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; - -namespace Synapse.Dashboard.Pages.Schedules.List; - -/// -/// Defines Flux effects that apply to -related actions -/// -[Effect] -public static class ScheduleCollectionStateEffects -{ - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(QuerySchedules action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var schedules = await api.GetSchedulesAsync(action.Query); - context.Dispatcher.Dispatch(new HandleScheduleQueryResults(schedules)); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Reducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Reducers.cs deleted file mode 100644 index 90f5c0d64..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Reducers.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Schedules.List; - -/// -/// Defines Flux reducers for -related actions -/// -[Reducer] -public static class ScheduleCollectionStateReducers -{ - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleCollectionState On(ScheduleCollectionState state, HandleScheduleQueryResults action) - { - return state with - { - Schedules = action.Results - }; - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Selectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Selectors.cs deleted file mode 100644 index 2bf08989d..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/Selectors.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Schedules.List -{ - - /// - /// Defines selectors for s - /// - public static class ScheduleCollectionStateSelectors - { - - /// - /// Selects all available schedules - /// - /// The global - /// A new - public static IObservable?> SelectedSchedules(IStore store) - { - return store.GetFeature() - .Select(state => state.Schedules) - .DistinctUntilChanged(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/State.cs deleted file mode 100644 index 7a63eeb0e..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/State.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Schedules.List; - -/// -/// Represents the Flux state of the 'schedules/list' page feature -/// -[Feature] -public record ScheduleCollectionState -{ - - /// - /// Gets a containing all available s - /// - public List? Schedules { get; init; } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/View.razor deleted file mode 100644 index 45fbf4dc1..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/List/View.razor +++ /dev/null @@ -1,151 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/schedules" -@using System.Reactive.Subjects -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbManager -@inject NavigationManager NavigationManager - -Schedules - - - - - - - -@if(schedules != null) -{ -
-
- -
-
- - - - - - - - - - @{ - var background = (V1ScheduleStatus)context.Value! switch - { - V1ScheduleStatus.Active => "bg-primary", - V1ScheduleStatus.Suspended => "bg-warning", - _ => "bg-secondary", - }; - } - - - - - - - - - - - - - - - - - - -
-} -else -{ - -} -@code{ - - private Subject? disposeNotifier = null!; - private List? schedules; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbManager.Use(Breadcrumbs.Schedules); - this.disposeNotifier = new Subject(); - ScheduleCollectionStateSelectors.SelectedSchedules(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(schedules => - { - this.schedules = schedules; - this.StateHasChanged(); - }); - this.Dispatcher.Dispatch(new QuerySchedules()); - } - - void OnNewSchedule() - { - this.NavigationManager.NavigateTo("/schedules/new"); - } - - void OnViewSchedule(V1Schedule schedule) - { - this.NavigationManager.NavigateTo($"/schedules/{schedule.Id}"); - } - - void OnSearchSchedules(string searchTerm) - { - this.Dispatcher.Dispatch(new QuerySchedules(searchTerm)); - } - - void OnClearScheduleSearch() - { - this.Dispatcher.Dispatch(new QuerySchedules()); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - this.disposeNotifier?.OnNext(true); - this.disposeNotifier?.OnCompleted(); - this.disposeNotifier?.Dispose(); - this.disposeNotifier = null; - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Actions.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Actions.cs deleted file mode 100644 index 526817921..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Actions.cs +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Schedules.View -{ - - /// - /// Represents the Flux action used to initialize the - /// - public class InitializeState - { - - - - } - - /// - /// Represents the Flux action used to get a by id - /// - public class GetScheduleById - { - - /// - /// Initializes a new - /// - /// The id of the to get - public GetScheduleById(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to get - /// - public string Id { get; } - - } - - /// - /// Repesents the Flux action used to handle the differed result of a Flux action - /// - public class HandleGetScheduleByIdResult - { - - /// - /// Initializes a new - /// - /// The differed result of a Flux action - public HandleGetScheduleByIdResult(V1Schedule? result) - { - this.Result = result; - } - - /// - /// Gets the differed result of a Flux action - /// - public V1Schedule? Result { get; } - - } - - /// - /// Represents the Flux action used to trigger a - /// - public class TriggerSchedule - { - - /// - /// Initializes a new - /// - /// The id of the to trigger - public TriggerSchedule(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to trigger - /// - public string Id { get; } - - } - - /// - /// Represents the Flux action used to suspend a - /// - public class SuspendSchedule - { - - /// - /// Initializes a new - /// - /// The id of the to suspend - public SuspendSchedule(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to suspend - /// - public string Id { get; } - - } - - /// - /// Represents the Flux action used to resume a - /// - public class ResumeSchedule - { - - /// - /// Initializes a new - /// - /// The id of the to resume - public ResumeSchedule(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to resume - /// - public string Id { get; } - - } - - /// - /// Represents the Flux action used to retire a - /// - public class RetireSchedule - { - - /// - /// Initializes a new - /// - /// The id of the to retire - public RetireSchedule(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to retire - /// - public string Id { get; } - - } - - /// - /// Represents the Flux action used to delete a - /// - public class DeleteSchedule - { - - /// - /// Initializes a new - /// - /// The id of the to delete - public DeleteSchedule(string id) - { - this.Id = id; - } - - /// - /// Gets the id of the to delete - /// - public string Id { get; } - - } - - /// - /// Represents the Flux action used to handle the deletion of a - /// - public class HandleScheduleDeleted - { - - - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Effects.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Effects.cs deleted file mode 100644 index f5761657c..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Effects.cs +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; - -namespace Synapse.Dashboard.Pages.Schedules.View -{ - - /// - /// Defines Flux effects for -related Flux actions - /// - [Effect] - public static class Effects - { - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(GetScheduleById action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var schedule = await api.GetScheduleByIdAsync(action.Id); - context.Dispatcher.Dispatch(new HandleGetScheduleByIdResult(schedule)); - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(TriggerSchedule action, IEffectContext context) - { - var logger = context.Services.GetRequiredService>(); - var api = context.Services.GetRequiredService(); - try - { - await api.TriggerScheduleAsync(action.Id); - context.Dispatcher.Dispatch(new GetScheduleById(action.Id)); - } - catch (Exception ex) - { - logger.LogError("An error occured while triggering a new occurence of the schedule with id '{scheduleId}': {ex}", action.Id, ex); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(SuspendSchedule action, IEffectContext context) - { - var logger = context.Services.GetRequiredService>(); - var api = context.Services.GetRequiredService(); - try - { - await api.SuspendScheduleAsync(action.Id); - context.Dispatcher.Dispatch(new GetScheduleById(action.Id)); - } - catch(Exception ex) - { - logger.LogError("An error occured while suspending the schedule with id '{scheduleId}': {ex}", action.Id, ex); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(ResumeSchedule action, IEffectContext context) - { - var logger = context.Services.GetRequiredService>(); - var api = context.Services.GetRequiredService(); - try - { - await api.ResumeScheduleAsync(action.Id); - context.Dispatcher.Dispatch(new GetScheduleById(action.Id)); - } - catch (Exception ex) - { - logger.LogError("An error occured while resuming the schedule with id '{scheduleId}': {ex}", action.Id, ex); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(RetireSchedule action, IEffectContext context) - { - var logger = context.Services.GetRequiredService>(); - var api = context.Services.GetRequiredService(); - try - { - await api.RetireScheduleAsync(action.Id); - context.Dispatcher.Dispatch(new GetScheduleById(action.Id)); - } - catch (Exception ex) - { - logger.LogError("An error occured while retiring the schedule with id '{scheduleId}': {ex}", action.Id, ex); - } - } - - /// - /// Handles the specified - /// - /// The to handle - /// The current - /// A new awaitable - public static async Task On(DeleteSchedule action, IEffectContext context) - { - var logger = context.Services.GetRequiredService>(); - var api = context.Services.GetRequiredService(); - try - { - await api.DeleteScheduleAsync(action.Id); - context.Dispatcher.Dispatch(new HandleScheduleDeleted()); - } - catch (Exception ex) - { - logger.LogError("An error occured while deleting the schedule with id '{scheduleId}': {ex}", action.Id, ex); - } - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Reducers.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Reducers.cs deleted file mode 100644 index 5da6b7b96..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Reducers.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; - -namespace Synapse.Dashboard.Pages.Schedules.View -{ - - /// - /// Defines Flux reducers for -related actions - /// - [Reducer] - public static class Reducers - { - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, InitializeState action) - { - return new(); - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, HandleGetScheduleByIdResult action) - { - return state with - { - Processing = false, - Schedule = action.Result - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, TriggerSchedule action) - { - return state with - { - Processing = true - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, SuspendSchedule action) - { - return state with - { - Processing = true - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, ResumeSchedule action) - { - return state with - { - Processing = true - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, RetireSchedule action) - { - return state with - { - Processing = true - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, DeleteSchedule action) - { - return state with - { - Processing = true - }; - } - - /// - /// Handles the specified - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static ScheduleViewState On(ScheduleViewState state, HandleScheduleDeleted action) - { - return state with - { - IsDeleted = true - }; - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Selectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Selectors.cs deleted file mode 100644 index c684d4305..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/Selectors.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Schedules.View -{ - - /// - /// Defines selectors for the - /// - public static class ScheduleViewStateSelectors - { - - /// - /// Selects the current schedule - /// - /// The global - /// A new - public static IObservable SelectCurrentSchedule(IStore store) - { - return store.GetFeature() - .Select(state => state.Schedule) - .DistinctUntilChanged(); - } - - /// - /// Selects a boolean that indicates whether or not the current has been deleted - /// - /// The global - /// A new - public static IObservable SelectIsDeleted(IStore store) - { - return store.GetFeature() - .Select(state => state.IsDeleted) - .DistinctUntilChanged(); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/State.cs deleted file mode 100644 index 1abae0ec3..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/State.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Schedules.View -{ - - /// - /// Represents the Flux state used by the schedule view - /// - [Feature] - public record ScheduleViewState - { - - /// - /// Gets a boolean indicating whether or not the application is processing - /// - public bool Processing { get; set; } - - /// - /// Gets a boolean indicating whether or not the current has been deleted - /// - public bool IsDeleted { get; init; } - - /// - /// Gets the current , if any - /// - public V1Schedule? Schedule { get; init; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/View.razor deleted file mode 100644 index 82362f5a7..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Schedules/View/View.razor +++ /dev/null @@ -1,193 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/schedules/{scheduleId}" -@using System.Reactive.Subjects -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbManager -@inject NavigationManager NavigationManager - -Schedules - -@if(schedule != null) -{ - - - - @if (schedule.Status == V1ScheduleStatus.Active) - { - - - } - @if(schedule.Status == V1ScheduleStatus.Suspended) - { - - } - @if(schedule.Status <= V1ScheduleStatus.Suspended) - { - - } - -
- -
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Id@schedule.Id
Created at@schedule.CreatedAt
Last modified@schedule.LastModified
Activation@EnumHelper.Stringify(schedule.ActivationType)
Status - @{ - var background = schedule.Status switch - { - V1ScheduleStatus.Active => "bg-primary", - V1ScheduleStatus.Suspended => "bg-warning", - _ => "bg-secondary", - }; - } - -
Workflow@schedule.WorkflowId
Type@EnumHelper.Stringify(schedule.Definition.Type)
Expression@(schedule.Definition.Type == ScheduleDefinitionType.Cron ? schedule.Definition.Cron!.Expression : schedule.Definition.Interval.ToString())
Last occurence@schedule.LastOccuredAt
Next occurence@schedule.NextOccurenceAt
Total occurences@schedule.TotalOccurences
-
- -} - -@code{ - - private Subject? disposeNotifier = null!; - private V1Schedule? schedule; - - private string scheduleId = null!; - [Parameter] public string ScheduleId { get; set; } = null!; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - this.disposeNotifier = new Subject(); - ScheduleViewStateSelectors.SelectCurrentSchedule(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(schedule => - { - this.schedule = schedule; - this.StateHasChanged(); - }); - ScheduleViewStateSelectors.SelectIsDeleted(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(isDeleted => - { - if (isDeleted) this.OnViewScheduleList(); - }); - await this.BreadcrumbManager.Use(Breadcrumbs.Schedules); - await this.BreadcrumbManager.AddItem(new BreadcrumbItem(this.ScheduleId, $"/schedules/{this.ScheduleId}")); - this.Dispatcher.Dispatch(new InitializeState()); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.ScheduleId != this.scheduleId) - { - this.scheduleId = this.ScheduleId; - this.Dispatcher.Dispatch(new GetScheduleById(this.scheduleId)); - } - } - - private void OnTriggerSchedule() - { - this.Dispatcher.Dispatch(new TriggerSchedule(this.scheduleId)); - } - - private void OnSuspendSchedule() - { - this.Dispatcher.Dispatch(new SuspendSchedule(this.scheduleId)); - } - - private void OnResumeSchedule() - { - this.Dispatcher.Dispatch(new ResumeSchedule(this.scheduleId)); - } - - private void OnRetireSchedule() - { - this.Dispatcher.Dispatch(new RetireSchedule(this.scheduleId)); - } - - private void OnDeleteSchedule() - { - this.Dispatcher.Dispatch(new DeleteSchedule(this.scheduleId)); - } - - private void OnViewScheduleList() - { - this.NavigationManager.NavigateTo("/schedules"); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) return; - this.disposeNotifier?.OnNext(true); - this.disposeNotifier?.OnCompleted(); - this.disposeNotifier?.Dispose(); - this.disposeNotifier = null; - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Users/Profile.razor b/src/dashboard/Synapse.Dashboard/Pages/Users/Profile.razor new file mode 100644 index 000000000..b8d14a349 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Users/Profile.razor @@ -0,0 +1,63 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/users/profile" +@attribute [Authorize] +@inject ApplicationAuthenticationStateProvider AuthenticationStateProvider +@inject IBreadcrumbManager BreadcrumbManager + +User Profile + +
+
+

User Profile

+ @user?.Identity?.AuthenticationType +
+ @if (user != null) + { + + + @foreach (var claim in user.Claims) + { + + + + + } + +
@claim.Type@claim.Value
+ } +
+ +@code { + + ClaimsPrincipal? user; + + protected override async Task OnInitializedAsync() + { + BreadcrumbManager.Use(Breadcrumbs.UserProfile); + user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User; + AuthenticationStateProvider.AuthenticationStateChanged += OnAuthenticationStateChanged; + await base.OnInitializedAsync(); + } + + async void OnAuthenticationStateChanged(Task authenticationStateTask) + { + user = (await authenticationStateTask).User; + this.StateHasChanged(); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/State.cs b/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/State.cs new file mode 100644 index 000000000..713aad051 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/State.cs @@ -0,0 +1,55 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.WorkflowInstances.List; + +/// +/// Represents the 's state +/// +public record WorkflowInstanceListState + : NamespacedResourceManagementComponentState +{ + + /// + /// Gets/sets a that contains all cached s + /// + public EquatableList? Workflows { get; set; } + + /// + /// Gets/sets the , if any, to filter by the instances to list + /// + public Workflow? Workflow { get; set; } + + /// + /// Gets/sets the versions of the selected , if any + /// + public EquatableList? Versions { get; set; } + + /// + /// Gets/sets the version of the to filter by the instances to list + /// + public string? Version { get; set; } + + /// + /// Gets a that contains all s + /// + public EquatableList? Operators { get; set; } + + /// + /// Gets/sets the active operator filter + /// + public string? Operator { get; set; } = null; + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/Store.cs b/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/Store.cs new file mode 100644 index 000000000..9680bdb55 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/Store.cs @@ -0,0 +1,99 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Services; +using Synapse.Dashboard.Pages.Workflows.List; +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.WorkflowInstances.List; + +/// +/// Represents the 's store +/// +/// The service used to interact with the Synapse API +/// The hub used to watch resource events +public class WorkflowInstanceListComponentStore(ISynapseApiClient apiClient, ResourceWatchEventHubClient resourceEventHub) + : NamespacedResourceManagementComponentStore(apiClient, resourceEventHub) +{ + + /// + /// Gets an used to observe s + /// + public IObservable?> Workflows => this.Select(s => s.Workflows); + + /// + /// Gets an used to observe changes + /// + public IObservable?> Operators => this.Select(s => s.Operators).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Operator => this.Select(s => s.Operator).DistinctUntilChanged(); + + /// + /// Lists all available s + /// + /// A new awaitable + public virtual async Task ListWorkflowsAsync() + { + var workflowList = new EquatableList(await (await this.ApiClient.Workflows.ListAsync(null!).ConfigureAwait(false)).OrderBy(ns => ns.GetQualifiedName()).ToListAsync().ConfigureAwait(false)); + this.Reduce(s => s with + { + Workflows = workflowList + }); + } + + /// + /// Lists all available s + /// + /// A new awaitable + public async Task ListOperatorsAsync() + { + var operatorList = new EquatableList(await (await this.ApiClient.Operators.ListAsync().ConfigureAwait(false)).OrderBy(ns => ns.GetQualifiedName()).ToListAsync().ConfigureAwait(false)); + this.Reduce(s => s with + { + Operators = operatorList + }); + } + + /// + /// Sets the + /// + /// The new value + public void SetOperator(string? operatorName) + { + this.Reduce(state => state with + { + Operator = operatorName + }); + } + + /// + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + await this.ListWorkflowsAsync().ConfigureAwait(false); + await this.ListOperatorsAsync().ConfigureAwait(false); + this.Operator.Subscribe(operatorName => { + if (string.IsNullOrWhiteSpace(operatorName)) + { + this.RemoveLabelSelector(SynapseDefaults.Resources.Labels.Operator); + } + else + { + this.AddLabelSelector(new(SynapseDefaults.Resources.Labels.Operator, LabelSelectionOperator.Equals, operatorName)); + } + }, token: this.CancellationTokenSource.Token); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/View.razor b/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/View.razor new file mode 100644 index 000000000..8acbfe54b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/WorkflowInstances/List/View.razor @@ -0,0 +1,111 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/workflow-instances/{instanceName?}" +@attribute [Authorize] +@namespace Synapse.Dashboard.Pages.WorkflowInstances.List +@using BlazorBootstrap +@inherits NamespacedResourceManagementComponent +@inject IBreadcrumbManager BreadcrumbManager +@inject NavigationManager NavigationManager + +Workflow Instances + + + + + + + + + +@code { + [Parameter] public string? InstanceName { get; set; } + + RenderFragment Title() => __builder => + { +

Workflow Instances

+ }; + + /// + /// Gets the list of available s + /// + protected EquatableList? Operators { get; set; } + + /// + /// Gets selected + /// + protected string? Operator { get; set; } + + /// + /// Gets the list of available s + /// + protected EquatableList? Workflows { get; set; } + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + BreadcrumbManager.Use(Breadcrumbs.WorkflowInstances); + this.Store.Workflows.Subscribe(value => OnStateChanged(_ => Workflows = value), token: CancellationTokenSource.Token); + this.Store.Operators.Subscribe(value => OnStateChanged(_ => Operators = value), token: CancellationTokenSource.Token); + this.Store.Operator.Subscribe(value => OnStateChanged(_ => Operator = value), token: CancellationTokenSource.Token); + this.Store.Resources.Subscribe(value => + { + if (!string.IsNullOrWhiteSpace(InstanceName) && value != null && value.Count > 0) + { + var instance = value.FirstOrDefault(r => r.GetQualifiedName() == InstanceName); + if (instance != null) OnShowInstanceDetails(instance); + } + + }, token: CancellationTokenSource.Token); + } + + /// + /// Handles changes of the workflow selector + /// + /// The name of the workflow + protected void OnWorkflowChanged(string? workflowQualifiedName) + { + if (string.IsNullOrWhiteSpace(workflowQualifiedName)) + { + this.Store.RemoveLabelSelector(SynapseDefaults.Resources.Labels.Workflow); + } + else + { + this.Store.AddLabelSelector(new(SynapseDefaults.Resources.Labels.Workflow, LabelSelectionOperator.Equals, workflowQualifiedName)); + } + } + + void OnShowInstanceDetails(WorkflowInstance instance) => NavigationManager.NavigateTo($"/workflows/details/{instance.Spec.Definition.Namespace}/{instance.Spec.Definition.Name}/{instance.Spec.Definition.Version}/{instance.GetName()}"); + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create.razor deleted file mode 100644 index 4e27f0dd8..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create.razor +++ /dev/null @@ -1,54 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/workflows/new" -@inject IBreadcrumbManager BreadcrumbService -@inject NavigationManager NavigationManager - -New workflow - - - New workflow - - -

-
-
-
-
- -
Editor
-
-
-
-
-
-
- -
Upload
-
-
-
-
-
- -@code { - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.CreateWorkflow); - } -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/State.cs new file mode 100644 index 000000000..d2603bda6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/State.cs @@ -0,0 +1,95 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using ServerlessWorkflow.Sdk.Models; +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.Workflows.Create; + +/// +/// The of the workflow editor +/// +[Feature] +public record CreateWorkflowViewState +{ + /// + /// Gets/sets the 's + /// + public string? Namespace { get; set; } + + /// + /// Gets/sets the 's name + /// + public string? Name { get; set; } + + /// + /// Gets/sets the definition of the workflow to create + /// + public WorkflowDefinition WorkflowDefinition { get; set; } = new WorkflowDefinition() + { + Document = new() + { + Dsl = "1.0.0-alpha2", + Namespace = Neuroglia.Data.Infrastructure.ResourceOriented.Namespace.DefaultNamespaceName, + Name = "new-workflow", + Version = "0.1.0" + }, + Do = [] + }; + + /// + /// Gets/sets the workflow definition text representation + /// + public string? WorkflowDefinitionText { get; set; } + + /// + /// Gets/sets a boolean indicating whether or not the state is being loaded + /// + public bool Loading { get; set; } = false; + + /// + /// Defines if the workflow definition is being updated + /// + public bool Updating { get; set; } = false; + + /// + /// Defines if the workflow definition is being saved + /// + public bool Saving { get; set; } = false; + + /// + /// Gets/sets the type that occurred when trying to save the resource, if any + /// + public Uri? ProblemType { get; set; } = null; + + /// + /// Gets/sets the title that occurred when trying to save the resource, if any + /// + public string ProblemTitle { get; set; } = string.Empty; + + /// + /// Gets/sets the details that occurred when trying to save the resource, if any + /// + public string ProblemDetail { get; set; } = string.Empty; + + /// + /// Gets/sets the status that occurred when trying to save the resource, if any + /// + public int ProblemStatus { get; set; } = 0; + + /// + /// Gets/sets the list of errors that occurred when trying to save the resource, if any + /// + public IDictionary ProblemErrors { get; set; } = new Dictionary(); + +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/Store.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/Store.cs new file mode 100644 index 000000000..ab6f0f239 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/Store.cs @@ -0,0 +1,509 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Json.Schema; +using JsonCons.Utilities; +using Neuroglia.Data; +using Semver; +using ServerlessWorkflow.Sdk.Models; +using Synapse.Api.Client.Services; +using Synapse.Dashboard.Components.ResourceEditorStateManagement; +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.Workflows.Create; + +/// +/// Represents the +/// +/// The service used to interact with the Synapse API +/// The service used to help handling Monaco editors +/// The service used to serialize/deserialize data to/from JSON +/// The service used to serialize/deserialize data to/from YAML +/// The service used for JS interop +/// The service used to provides an abstraction for querying and managing URI navigation +/// The service used to download the specification schemas +/// The service to build a bridge with the monaco interop extension +public class CreateWorkflowViewStore( + ISynapseApiClient api, + IMonacoEditorHelper monacoEditorHelper, + IJsonSerializer jsonSerializer, + IYamlSerializer yamlSerializer, + IJSRuntime jsRuntime, + NavigationManager navigationManager, + SpecificationSchemaManager specificationSchemaManager, + MonacoInterop monacoInterop +) + : ComponentStore(new()) +{ + + private TextModel? _textModel = null; + private string _textModelUri = string.Empty; + private bool _disposed; + + /// + /// Gets the service used to interact with the Synapse API + /// + protected ISynapseApiClient Api { get; } = api; + + /// + /// Gets the service used to help handling Monaco editors + /// + protected IMonacoEditorHelper MonacoEditorHelper { get; } = monacoEditorHelper; + + /// + /// Gets the service used to serialize/deserialize data to/from JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the service used to serialize/deserialize data to/from YAML + /// + protected IYamlSerializer YamlSerializer { get; } = yamlSerializer; + + /// + /// Gets the service used for JS interop + /// + protected IJSRuntime JSRuntime { get; } = jsRuntime; + + /// + /// Gets the service used to provides an abstraction for querying and managing URI navigation + /// + protected NavigationManager NavigationManager { get; } = navigationManager; + + /// + /// Gets the service used to download the specification schemas + /// + protected SpecificationSchemaManager SpecificationSchemaManager { get; } = specificationSchemaManager; + + /// + /// Gets the service to build a bridge with the monaco interop extension + /// + protected MonacoInterop MonacoInterop { get; } = monacoInterop; + + /// + /// The provider function + /// + public Func StandaloneEditorConstructionOptions = monacoEditorHelper.GetStandaloneEditorConstructionOptions(string.Empty, false, monacoEditorHelper.PreferredLanguage); + + /// + /// The reference + /// + public StandaloneCodeEditor? TextEditor { get; set; } + + #region Selectors + /// + /// Gets an used to observe changes + /// + public IObservable Namespace => this.Select(state => state.Namespace).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Name => this.Select(state => state.Name).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes to the state's property + /// + public IObservable WorkflowDefinition => this.Select(state => state.WorkflowDefinition).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes to the state's property + /// + public IObservable Loading => this.Select(state => state.Loading).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes to the state's property + /// + public IObservable Saving => this.Select(state => state.Saving).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemType => this.Select(state => state.ProblemType).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemTitle => this.Select(state => state.ProblemTitle).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemDetail => this.Select(state => state.ProblemDetail).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable ProblemStatus => this.Select(state => state.ProblemStatus).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable> ProblemErrors => this.Select(state => state.ProblemErrors).DistinctUntilChanged(); + + /// + /// Gets an used to observe computed + /// + public IObservable ProblemDetails => Observable.CombineLatest( + this.ProblemType, + this.ProblemTitle, + this.ProblemStatus, + this.ProblemDetail, + this.ProblemErrors, + (type, title, status, details, errors) => + { + if (string.IsNullOrWhiteSpace(title)) + { + return null; + } + return new ProblemDetails(type ?? new Uri("unknown://"), title, status, details, null, errors, null); + } + ); + #endregion + + #region Setters + /// + /// Sets the state's + /// + /// The new value + public void SetNamespace(string? ns) + { + this.Reduce(state => state with + { + Namespace = ns, + Loading = true + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetName(string? name) + { + this.Reduce(state => state with + { + Name = name, + Loading = true + }); + } + + /// + /// Sets the state's 's related data + /// + /// The to populate the data with + public void SetProblemDetails(ProblemDetails? problem) + { + this.Reduce(state => state with + { + ProblemType = problem?.Type, + ProblemTitle = problem?.Title ?? string.Empty, + ProblemStatus = problem?.Status ?? 0, + ProblemDetail = problem?.Detail ?? string.Empty, + ProblemErrors = problem?.Errors?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? [] + }); + } + #endregion + + #region Actions + /// + /// Gets the for the specified namespace and name + /// + /// The namespace the to create a new version of belongs to + /// The name of the to create a new version of + /// A new awaitable + public async Task GetWorkflowDefinitionAsync(string @namespace, string name) + { + ArgumentException.ThrowIfNullOrWhiteSpace(@namespace); + ArgumentException.ThrowIfNullOrWhiteSpace(name); + var workflow = await this.Api.Workflows.GetAsync(name, @namespace) ?? throw new NullReferenceException($"Failed to find the specified workflow '{name}.{@namespace}'"); + var definition = workflow.Spec.Versions.GetLatest(); + var nextVersion = SemVersion.Parse(definition.Document.Version, SemVersionStyles.Strict); + nextVersion = nextVersion.WithPatch(nextVersion.Patch + 1); + definition.Document.Version = nextVersion.ToString(); + this.Reduce(s => s with + { + WorkflowDefinition = definition, + Loading = false + }); + } + + /// + /// Handles changed of the text editor's language + /// + /// + /// + public async Task ToggleTextBasedEditorLanguageAsync(string _) + { + if (this.TextEditor == null) + { + return; + } + var language = this.MonacoEditorHelper.PreferredLanguage; + try + { + var document = await this.TextEditor.GetValue(); + if (document == null) + { + return; + } + document = language == PreferredLanguage.YAML ? + this.YamlSerializer.ConvertFromJson(document) : + this.YamlSerializer.ConvertToJson(document); + this.Reduce(state => state with + { + WorkflowDefinitionText = document + }); + await this.OnTextBasedEditorInitAsync(); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + await this.MonacoEditorHelper.ChangePreferredLanguageAsync(language == PreferredLanguage.YAML ? PreferredLanguage.JSON : PreferredLanguage.YAML); + } + } + + /// + /// Handles initialization of the text editor + /// + /// + public async Task OnTextBasedEditorInitAsync() + { + await this.SetTextBasedEditorLanguageAsync(); + await this.SetTextEditorValueAsync(); + } + + /// + /// Sets the language of the text editor + /// + /// + public async Task SetTextBasedEditorLanguageAsync() + { + try + { + var language = this.MonacoEditorHelper.PreferredLanguage; + if (this.TextEditor != null) + { + this._textModel = await Global.GetModel(this.JSRuntime, this._textModelUri); + this._textModel ??= await Global.CreateModel(this.JSRuntime, "", language, this._textModelUri); + await Global.SetModelLanguage(this.JSRuntime, this._textModel, language); + await this.TextEditor!.SetModel(this._textModel); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + } + + /// + /// Changes the value of the text editor + /// + /// + async Task SetTextEditorValueAsync() + { + var document = this.Get(state => state.WorkflowDefinitionText); + if (this.TextEditor != null && !string.IsNullOrWhiteSpace(document)) + { + await this.TextEditor.SetValue(document); + try + { + //await this.TextEditor.Trigger("", "editor.action.triggerSuggest"); + await this.TextEditor.Trigger("", "editor.action.formatDocument"); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + } + } + + /// + /// Saves the by posting it to the Synapse API + /// + /// A new awaitable + public async Task SaveWorkflowDefinitionAsync() + { + if (this.TextEditor == null) + { + this.Reduce(state => state with + { + ProblemTitle = "Text editor", + ProblemDetail = "The text editor must be initialized." + }); + return; + } + var workflowDefinitionText = await this.TextEditor.GetValue(); + if (string.IsNullOrWhiteSpace(workflowDefinitionText)) + { + this.Reduce(state => state with + { + ProblemTitle = "Invalid definition", + ProblemDetail = "The workflow definition cannot be empty." + }); + return; + } + try + { + var workflowDefinition = this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON ? + this.JsonSerializer.Deserialize(workflowDefinitionText) : + this.YamlSerializer.Deserialize(workflowDefinitionText); + var @namespace = workflowDefinition!.Document.Namespace; + var name = workflowDefinition.Document.Name; + var version = workflowDefinition.Document.Version; + this.Reduce(s => s with + { + Saving = true + }); + Workflow? workflow = null; + try + { + workflow = await this.Api.Workflows.GetAsync(name, @namespace); + } + catch + { + // Assume 404, might need actual handling + } + if (workflow == null) + { + workflow = await this.Api.Workflows.CreateAsync(new() + { + Metadata = new() + { + Namespace = workflowDefinition!.Document.Namespace, + Name = workflowDefinition.Document.Name + }, + Spec = new() + { + Versions = [workflowDefinition] + } + }); + } + else + { + var updatedResource = workflow.Clone()!; + var documentVersion = SemVersion.Parse(version, SemVersionStyles.Strict)!; + var latestVersion = SemVersion.Parse(updatedResource.Spec.Versions.GetLatest().Document.Version, SemVersionStyles.Strict)!; + if (updatedResource.Spec.Versions.Any(v => SemVersion.Parse(v.Document.Version, SemVersionStyles.Strict).CompareSortOrderTo(documentVersion) >= 0)) + { + this.Reduce(state => state with + { + ProblemTitle = "Invalid version", + ProblemDetail = $"The specified version '{documentVersion}' must be strictly superior to the latest version '{latestVersion}'." + }); + return; + } + updatedResource.Spec.Versions.Add(workflowDefinition!); + var jsonPatch = JsonPatch.FromDiff(this.JsonSerializer.SerializeToElement(workflow)!.Value, this.JsonSerializer.SerializeToElement(updatedResource)!.Value); + var patch = this.JsonSerializer.Deserialize(jsonPatch.RootElement); + if (patch != null) + { + var resourcePatch = new Patch(PatchType.JsonPatch, jsonPatch); + await this.Api.ManageNamespaced().PatchAsync(name, @namespace, resourcePatch, null, this.CancellationTokenSource.Token); + } + } + this.NavigationManager.NavigateTo($"/workflows/details/{@namespace}/{name}/{version}"); + } + catch (ProblemDetailsException ex) + { + this.SetProblemDetails(ex.Problem); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + finally + { + this.Reduce(s => s with + { + Saving = false + }); + } + } + #endregion + + /// + public override async Task InitializeAsync() + { + this.WorkflowDefinition.SubscribeAsync(async definition => { + string document = ""; + if (definition != null) + { + if (definition.Document?.Dsl != null) + { + await this.SetValidationSchema($"v{definition.Document.Dsl}"); + } + document = this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON ? + this.JsonSerializer.SerializeToText(definition) : + this.YamlSerializer.SerializeToText(definition); + } + this.Reduce(state => state with + { + WorkflowDefinitionText = document + }); + await this.OnTextBasedEditorInitAsync(); + }, cancellationToken: this.CancellationTokenSource.Token); + Observable.CombineLatest( + this.Namespace.Where(ns => !string.IsNullOrWhiteSpace(ns)), + this.Name.Where(name => !string.IsNullOrWhiteSpace(name)), + (ns, name) => (ns!, name!) + ).SubscribeAsync(async ((string ns, string name) workflow) => + { + await this.GetWorkflowDefinitionAsync(workflow.ns, workflow.name); + }, cancellationToken: this.CancellationTokenSource.Token); + await base.InitializeAsync(); + } + + /// + /// Adds validation for the specification of the specified version + /// + /// The version of the spec to add the validation for + /// An awaitable task + protected async Task SetValidationSchema(string? version = null) + { + version = version ?? await this.SpecificationSchemaManager.GetLatestVersion(); + var schema = await this.SpecificationSchemaManager.GetSchema(version); + var type = $"create_{typeof(WorkflowDefinition).Name.ToLower()}_{version}"; + await this.MonacoInterop.AddValidationSchemaAsync(schema, $"https://synapse.io/schemas/{type}.json", $"{type}*").ConfigureAwait(false); + this._textModelUri = this.MonacoEditorHelper.GetResourceUri(type); + } + + /// + /// Disposes of the store + /// + /// A boolean indicating whether or not the dispose of the store + protected override void Dispose(bool disposing) + { + if (!this._disposed) + { + if (disposing) + { + if (this._textModel != null) + { + this._textModel.DisposeModel(); + this._textModel = null; + } + if (this.TextEditor != null) + { + this.TextEditor.Dispose(); + this.TextEditor = null; + } + } + this._disposed = true; + } + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/View.razor new file mode 100644 index 000000000..3541d26db --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Create/View.razor @@ -0,0 +1,114 @@ +@* + Copyright © 2024-Present The Synapse Authors +

+ Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at +

+ http://www.apache.org/licenses/LICENSE-2.0 +

+ Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/workflows/new" +@page "/workflows/new/{namespace}/{name}" +@using ServerlessWorkflow.Sdk.Models +@using Synapse.Api.Client.Services +@inherits StatefulComponent +@inject IBreadcrumbManager BreadcrumbManager +@inject NavigationManager NavigationManager + +New workflow + +

New Workflow

+@if (loading) +{ +
+ +
+} +else +{ +
+ +
+ +
+ @if (problemDetails != null) + { + + @problemDetails.Detail + + @if (problemDetails.Errors != null && problemDetails.Errors.Any()) + { + foreach (KeyValuePair errorContainer in problemDetails.Errors) + { + +
    + @foreach (string error in errorContainer.Value) + { +
  • @error
  • + } +
+
+ } + } + } +
+ +} + +@code { + + string? ns; + string? name; + bool loading; + bool saving; + private ProblemDetails? problemDetails = null; + + [Parameter] public string? Namespace { get; set; } + [Parameter] public string? Name { get; set; } + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + BreadcrumbManager.Use(Breadcrumbs.Workflows); + BreadcrumbManager.Add(new($"New", $"/workflows/new")); + Store.Namespace.Subscribe(value => OnStateChanged(_ => ns = value), token: CancellationTokenSource.Token); + Store.Name.Subscribe(value => OnStateChanged(_ => name = value), token: CancellationTokenSource.Token); + Store.Loading.Subscribe(value => OnStateChanged(_ => loading = value), token: CancellationTokenSource.Token); + Store.Saving.Subscribe(value => OnStateChanged(_ => saving = value), token: CancellationTokenSource.Token); + Store.ProblemDetails.Subscribe(problemDetails => OnStateChanged(cmp => cmp.problemDetails = problemDetails), token: CancellationTokenSource.Token); + } + + /// + protected override void OnParametersSet() + { + if (Namespace != ns) + { + Store.SetNamespace(Namespace); + } + if (Name != name) + { + Store.SetName(Name); + } + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/State.cs new file mode 100644 index 000000000..f4f27dd40 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/State.cs @@ -0,0 +1,50 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using ServerlessWorkflow.Sdk.Models; +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.Workflows.Details; + +/// +/// Represents the 's state +/// +public record WorkflowDetailsState + : NamespacedResourceManagementComponentState +{ + /// + /// Gets/sets the displayed + /// + public Workflow? Workflow { get; set; } + + /// + /// Gets/sets the displayed 's version + /// + public string? WorkflowDefinitionVersion { get; set; } + + /// + /// Gets/sets a that contains all s variations + /// + public EquatableList? Workflows { get; set; } + + /// + /// Gets/sets the parsed + /// + public string WorkflowDefinitionJson { get; set; } = string.Empty; + + /// + /// Gets/sets the displayed id + /// + public string? WorkflowInstanceName { get; set; } + +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/Store.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/Store.cs new file mode 100644 index 000000000..e54492d06 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/Store.cs @@ -0,0 +1,473 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre.Models; +using ServerlessWorkflow.Sdk.Models; +using Synapse.Api.Client.Services; +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.Workflows.Details; + +/// +/// Represents the 's store +/// +/// The service used to interact with the Synapse API +/// The hub used to watch resource events +/// The service used for JS interop +/// The service used ease Monaco Editor interactions +/// The service used to serialize and deserialize JSON +/// The service used to serialize and deserialize YAML +/// The service used to build a bridge with the monaco interop extension +/// The service used display toast messages +public class WorkflowDetailsStore( + ISynapseApiClient apiClient, + ResourceWatchEventHubClient resourceEventHub, + IJSRuntime jsRuntime, + IMonacoEditorHelper monacoEditorHelper, + IJsonSerializer jsonSerializer, + IYamlSerializer yamlSerializer, + MonacoInterop monacoInterop, + ToastService toastService +) + : NamespacedResourceManagementComponentStore(apiClient, resourceEventHub) +{ + + private TextModel? _textModel = null; + private bool _disposed; + + /// + /// Gets the service used for JS interop + /// + protected IJSRuntime JSRuntime { get; } = jsRuntime; + + /// + /// Gets the service used ease Monaco Editor interactions + /// + protected IMonacoEditorHelper MonacoEditorHelper { get; } = monacoEditorHelper; + + /// + /// Gets the service used to serialize and deserialize JSON + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + /// Gets the service used to serialize and deserialize YAML + /// + protected IYamlSerializer YamlSerializer { get; } = yamlSerializer; + + /// + /// Gets the service used to build a bridge with the monaco interop extension + /// + protected MonacoInterop MonacoInterop { get; } = monacoInterop; + + /// + /// Gets the service used display toast messages + /// + protected ToastService ToastService { get; } = toastService; + + /// + /// Gets/sets the provider function + /// + public Func StandaloneEditorConstructionOptions = monacoEditorHelper.GetStandaloneEditorConstructionOptions(string.Empty, true, monacoEditorHelper.PreferredLanguage); + + /// + /// Gets/sets the 's reference used to display the workflow definition + /// + public StandaloneCodeEditor? TextEditor { get; set; } + + /// + /// Gets/sets the 's reference used to display the workflow instance creation form + /// + public Modal? Modal { get; set; } + + #region Selectors + /// + /// Gets an used to observe changes + /// + public IObservable Workflow => this.Select(state => state.Workflow).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable WorkflowDefinitionVersion => this.Select(state => state.WorkflowDefinitionVersion).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable WorkflowInstanceName => this.Select(state => state.WorkflowInstanceName).DistinctUntilChanged(); + + /// + /// Gets an exposing the + /// + public IObservable WorkflowDefinition => Observable.CombineLatest( + this.Workflow, + this.WorkflowDefinitionVersion, + (workflow, version) => + { + if (workflow == null) + { + return null; + } + if (string.IsNullOrWhiteSpace(version) || version.Equals("latest", StringComparison.CurrentCultureIgnoreCase)) + { + var latest = workflow.Spec.Versions.GetLatest()?.Document.Version; + if (!string.IsNullOrWhiteSpace(latest)) + { + this.SetWorkflowDefinitionVersion(latest); + } + return null; + } + return workflow.Spec.Versions.Get(version)?.Clone(); + } + ).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable?> Workflows => this.Select(state => state.Workflows).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Document => this.Select(state => state.WorkflowDefinitionJson).DistinctUntilChanged(); + + /// + /// Gets an used to observe the displayed changes + /// + public IObservable WorkflowInstance => Observable.CombineLatest( + this.Resources, + this.WorkflowInstanceName, + (instances, name) => { + if (instances == null || instances.Count == 0 || name == null) + { + return null; + } + return instances.FirstOrDefault(instance => instance.Metadata.Name == name)?.Clone(); + } + ).DistinctUntilChanged(); + #endregion + + #region Setters + /// + public override void SetActiveResourceName(string activeResourceName) + { + //base.SetActiveResourceName(activeResourceName); + this.Reduce(state => state with + { + ActiveResourceName = activeResourceName, + Workflow = null + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetWorkflowDefinitionVersion(string? workflowDefinitionVersion) + { + this.Reduce(state => state with + { + WorkflowDefinitionVersion = workflowDefinitionVersion + }); + } + + /// + /// Sets the state's + /// + /// The new value + public void SetWorkflowInstanceName(string? instanceName) + { + this.Reduce(state => state with + { + WorkflowInstanceName = instanceName + }); + } + #endregion + + #region Actions + /// + /// Changes the value of the text editor + /// + /// A awaitable task + async Task SetTextEditorValueAsync() + { + var document = this.Get(state => state.WorkflowDefinitionJson); + var language = this.MonacoEditorHelper.PreferredLanguage; + if (this.TextEditor != null && !string.IsNullOrWhiteSpace(document)) + { + try + { + if (language == PreferredLanguage.YAML) + { + document = this.YamlSerializer.ConvertFromJson(document); + } + await this.TextEditor.SetValue(document); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + await this.MonacoEditorHelper.ChangePreferredLanguageAsync(language == PreferredLanguage.YAML ? PreferredLanguage.JSON : PreferredLanguage.YAML); + } + } + } + + /// + /// Gets the workflow for the provided namespace and name + /// + /// + /// + /// A awaitable task + public async Task GetWorkflowAsync(string ns, string name) + { + try + { + var workflow = await this.ApiClient.Workflows.GetAsync(name, ns); + this.Reduce(state => state with + { + Workflow = workflow + }); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: implement proper error handling + } + } + + /// + /// Handles changed of the text editor's language + /// + /// + /// A awaitable task + public async Task ToggleTextBasedEditorLanguageAsync(string _) + { + await this.OnTextBasedEditorInitAsync(); + } + + /// + /// Handles initialization of the text editor + /// + /// A awaitable task + public async Task OnTextBasedEditorInitAsync() + { + await this.SetTextBasedEditorLanguageAsync(); + await this.SetTextEditorValueAsync(); + } + + /// + /// Sets the language of the text editor + /// + /// A awaitable task + public async Task SetTextBasedEditorLanguageAsync() + { + try + { + var language = this.MonacoEditorHelper.PreferredLanguage; + if (this.TextEditor != null) + { + if (this._textModel != null) + { + await Global.SetModelLanguage(this.JSRuntime, this._textModel, language); + } + else + { + var version = this.Get(state => state.WorkflowDefinitionVersion); + var reference = this.Get(state => state.Namespaces) + "." + this.Get(state => state.ActiveResourceName) + (!string.IsNullOrWhiteSpace(version) ? $":{version}" : ""); + var resourceUri = $"inmemory://{reference.ToLower()}"; + this._textModel = await Global.CreateModel(this.JSRuntime, "", language, resourceUri); + } + await this.TextEditor!.SetModel(this._textModel); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + } + + /// + /// Copies to content of the Monaco editor to the clipboard + /// + /// A awaitable task + public async Task OnCopyToClipboard() + { + if (this.TextEditor == null) return; + var text = await this.TextEditor.GetValue(); + if (string.IsNullOrWhiteSpace(text)) return; + try + { + await this.JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text); + this.ToastService.Notify(new(ToastType.Success, "Definition copied to the clipboard!")); + } + catch (Exception ex) + { + this.ToastService.Notify(new(ToastType.Danger, "Failed to copy the definition to the clipboard.")); + Console.WriteLine(ex.ToString()); + // todo: handle exception + } + } + + /// + /// Displays the modal used to provide the new workflow input + /// + /// A awaitable task + public async Task OnShowCreateInstanceAsync(WorkflowDefinition workflowDefinition) + { + if (this.Modal != null) + { + var parameters = new Dictionary + { + { nameof(WorkflowInstanceCreation.WorkflowDefinition), workflowDefinition }, + { nameof(WorkflowInstanceCreation.OnCreate), EventCallback.Factory.Create(this, CreateInstanceAsync) } + }; + await this.Modal.ShowAsync(title: "Start a new worklfow", parameters: parameters); + } + } + + /// + /// Creates a new instance of the workflow + /// + /// The input data, if any + /// A awaitable task + public async Task CreateInstanceAsync(string input) + { + var workflowName = this.Get(state => state.ActiveResourceName); + var workflowVersion = this.Get(state => state.WorkflowDefinitionVersion); + var ns = this.Get(state => state.Namespace); + if (string.IsNullOrWhiteSpace(workflowName) || string.IsNullOrWhiteSpace(workflowVersion) || string.IsNullOrWhiteSpace(ns)) + { + await this.Modal!.HideAsync(); + return; + } + var inputData = new EquatableDictionary { }; + if (!string.IsNullOrWhiteSpace(input)) inputData = this.MonacoEditorHelper.PreferredLanguage == PreferredLanguage.JSON ? + this.JsonSerializer.Deserialize>(input) : + this.YamlSerializer.Deserialize>(input); + try + { + var instance = await this.ApiClient.WorkflowInstances.CreateAsync(new() + { + Metadata = new() + { + Namespace =ns, + Name = $"{workflowName}-" + }, + Spec = new() + { + Definition = new() + { + Namespace = ns, + Name = workflowName, + Version = workflowVersion + }, + Input = inputData + } + }); + } + catch (Exception ex) + { + // todo: handle ex + Console.WriteLine(ex.ToString()); + } + await this.Modal!.HideAsync(); + } + + /// + /// Delete the provided + /// + /// The workflow instance to delete + /// A awaitable task + public async Task DeleteWorkflowInstanceAsync(WorkflowInstance workflowInstance) + { + await this.ApiClient.ManageNamespaced().DeleteAsync(workflowInstance.GetName(), workflowInstance.GetNamespace()!).ConfigureAwait(false); + } + + /// + /// Selects the target node in the code editor + /// + /// The source of the event + /// A awaitable task + public async Task SelectNodeInEditor(GraphEventArgs e) + { + if (e.GraphElement == null) return; + if (this.TextEditor == null) return; + var source = await this.TextEditor.GetValue(); + var pointer = e.GraphElement.Id; + var language = this.MonacoEditorHelper.PreferredLanguage; + var range = await this.MonacoInterop.GetJsonPointerRangeAsync(source, pointer, language); + await this.TextEditor.SetSelection(range, string.Empty); + await this.TextEditor.RevealRangeInCenter(range); + } + #endregion + + /// + public override async Task InitializeAsync() + { + Observable.CombineLatest( + this.Namespace.Where(ns => !string.IsNullOrWhiteSpace(ns)), + this.ActiveResourceName.Where(name => !string.IsNullOrWhiteSpace(name)), + (ns, name) => (ns!, name!) + ).SubscribeAsync(async ((string ns, string name) workflow) => + { + this.RemoveLabelSelector(SynapseDefaults.Resources.Labels.Workflow); + this.AddLabelSelector(new(SynapseDefaults.Resources.Labels.Workflow, LabelSelectionOperator.Equals, $"{workflow.name}.{workflow.ns}")); + await this.GetWorkflowAsync(workflow.ns, workflow.name); + }, cancellationToken: this.CancellationTokenSource.Token); + this.WorkflowDefinitionVersion.Where(version => version != null).Subscribe(version => + { + this.RemoveLabelSelector(SynapseDefaults.Resources.Labels.WorkflowVersion); + this.AddLabelSelector(new(SynapseDefaults.Resources.Labels.WorkflowVersion, LabelSelectionOperator.Equals, version!)); + }); + this.WorkflowDefinition.Where(definition => definition != null).SubscribeAsync(async (definition) => + { + await Task.Delay(1); + var document = this.JsonSerializer.SerializeToText(definition.Clone()); + this.Reduce(state => state with + { + WorkflowDefinitionJson = document + }); + await this.SetTextEditorValueAsync(); + if (this.MonacoEditorHelper.PreferredLanguage != PreferredLanguage.YAML) + { + await this.MonacoEditorHelper.ChangePreferredLanguageAsync(PreferredLanguage.YAML); + } + }, cancellationToken: this.CancellationTokenSource.Token); + await base.InitializeAsync(); + } + + /// + /// Disposes of the store + /// + /// A boolean indicating whether or not the dispose of the store + protected override void Dispose(bool disposing) + { + if (!this._disposed) + { + if (disposing) + { + if (this._textModel != null) + { + this._textModel.DisposeModel(); + this._textModel = null; + } + if (this.TextEditor != null) + { + this.TextEditor.Dispose(); + this.TextEditor = null; + } + } + this._disposed = true; + } + } +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/View.razor new file mode 100644 index 000000000..1477d66bb --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Details/View.razor @@ -0,0 +1,238 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + + +@page "/workflows/details/{namespace}/{name}/{version?}/{instanceName?}" +@using ServerlessWorkflow.Sdk.Models +@using Synapse.Api.Client.Services +@inherits NamespacedResourceManagementComponent +@inject IBreadcrumbManager BreadcrumbManager +@inject NavigationManager NavigationManager + +Workflow @($"{Name}.{Namespace}:{version}") + +
+ + + + + + + + + + + @if (workflowDefinition == null) + { +
+ +
+ } + else + { + + } +
+
+ + + + @if (workflowDefinition == null) + { +
+ +
+ } + else + { +
+
+
+ + +
+ +
+
+ +
+
+ + } +
+
+ @if (workflowInstance != null) + { + + + +
+ +
+
+
+ } +
+ + + + + +@code +{ + string version = null!; + string? instanceName = null!; + Workflow workflow = null!; + WorkflowDefinition workflowDefinition = null!; + WorkflowInstance? workflowInstance; + readonly IEnumerable columns = [ + "Name", + "Status", + "Start Time", + "End Time", + "Delete" + ]; + + [Parameter] public string? Version { get; set; } + [Parameter] public string? InstanceName { get; set; } + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync().ConfigureAwait(false); + UpdateBreadcrumb(); + Store.WorkflowInstanceName.Subscribe(value => OnStateChanged(_ => instanceName = value), token: CancellationTokenSource.Token); + Store.WorkflowDefinition.Where(value => value != null).Subscribe(value => OnStateChanged(_ => workflowDefinition = value!), token: CancellationTokenSource.Token); + Store.WorkflowDefinitionVersion.Where(value => !string.IsNullOrWhiteSpace(value)).Subscribe(value => + { + OnStateChanged(_ => version = value!); + UpdateBreadcrumb(); + }, token: CancellationTokenSource.Token); + Store.WorkflowInstance.Subscribe(value => + { + OnStateChanged(_ => workflowInstance = value); + UpdateBreadcrumb(); + }, token: CancellationTokenSource.Token); + Store.Workflow.Where(value => value != null).Subscribe(value => + { + OnStateChanged(_ => workflow = value!); + UpdateBreadcrumb(); + }, token: CancellationTokenSource.Token); + } + + /// + protected override void OnParametersSet() + { + if (Version != version) + { + Store.SetWorkflowDefinitionVersion(Version); + } + if (InstanceName != instanceName) + { + Store.SetWorkflowInstanceName(InstanceName); + } + } + + /// + /// Updates the breadcrumb + /// + void UpdateBreadcrumb() + { + BreadcrumbManager.Use(Breadcrumbs.Workflows); + BreadcrumbManager.Add(new($"{Name}.{Namespace}", $"/workflows/details/{Namespace}/{Name}/latest")); + if (workflow != null) + { + BreadcrumbManager.Add(new(VersionSelector())); + } + if (workflowInstance != null) + { + BreadcrumbManager.Add(new(workflowInstance.GetName(), $"/workflows/details/{Namespace}/{Name}/{Version}/{workflowInstance.GetName()}")); + } + StateHasChanged(); + } + + /// + /// Renders the workflows instances table's title + /// + /// + RenderFragment VersionSelector() => __builder => + { + + }; + + /// + /// Handles the deletion of the targeted + /// + /// The to delete + protected async Task OnDeleteWorkflowInstanceAsync(WorkflowInstance instance) + { + if (this.Dialog == null) return; + var confirmation = await this.Dialog.ShowAsync( + title: $"Are you sure you want to delete '{instance.Metadata.Name}'?", + message1: $"The workflow instance will be permanently deleted. Are you sure you want to proceed ?", + confirmDialogOptions: new ConfirmDialogOptions() + { + YesButtonColor = ButtonColor.Danger, + YesButtonText = "Delete", + NoButtonText = "Abort", + IsVerticallyCentered = true + } + ); + if (!confirmation) return; + await this.Store.DeleteWorkflowInstanceAsync(instance); + } + + void OnCreateWorkflowVersion() => this.NavigationManager.NavigateTo($"/workflows/new/{Namespace}/{Name}"); + + void OnShowInstanceDetails(WorkflowInstance instance) => NavigationManager.NavigateTo($"/workflows/details/{Namespace}/{Name}/{version}/{instance.GetName()}"); + + void OnCloseWorkflowInstance() + { + NavigationManager.NavigateTo($"/workflows/details/{Namespace}/{Name}/{version}"); + StateHasChanged(); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/Editor.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/Editor.razor deleted file mode 100644 index b64d253ed..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/Editor.razor +++ /dev/null @@ -1,259 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/workflows/editor" -@page "/workflows/editor/{workflowId}" -@using System.Reactive.Subjects -@using System.Reactive.Linq -@using Synapse.Dashboard.Pages.Workflows.Editor.Actions -@using Synapse.Dashboard.Pages.Workflows.Editor.State -@inherits StatefulComponent -@inject IBreadcrumbManager BreadcrumbService -@inject IMonacoEditorHelper MonacoEditorHelper -@inject NavigationManager NavigationManager - -Workflow editor - - - - -
- Documentation - -
-
- -@if (State.Saving) -{ -
- -
-} -else if (workflowDefinition != null) -{ -
-
- -
-
-
-
- -
-
-
- - -
-
-
- -
- @if (isDiagramVisible) { -
- -
- } -
-} -else if(!string.IsNullOrWhiteSpace(this.workflowId)) -{ - -} - - - Validation messages - - -
- @if (State.ValidationMessages != null && State.ValidationMessages.Any()) - { -
-
    - @foreach (var error in State.ValidationMessages) - { -
  • @error.ToString()
  • - } -
-
- } -
- -
- -@code{ - private string? workflowId; - [Parameter] public string? WorkflowId { get; set; } - - private IFeature workflowsFeature; - private MonacoEditor? textBasedEditor; - private Subject? editorValue; - private Subject? disposeNotifier; - private WorkflowDefinition? workflowDefinition; - private WorkflowDiagram? workflowDiagram; - private Modal? validationMessagesDialog; - private bool isDiagramVisible = false; - private string colClass = "col-6"; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.WorkflowEditor); - this.disposeNotifier = new Subject(); - this.editorValue = new Subject(); - this.editorValue - .Throttle(TimeSpan.FromMilliseconds(300)) - .DistinctUntilChanged() - .TakeUntil(this.disposeNotifier) - .Subscribe(text => this.Dispatcher.Dispatch(new HandleTextBasedEditorChange(text))); - WorkflowEditorSelectors.SelectWorkflowDefinitionText(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (text) => - { - if (text != null && this.textBasedEditor != null) - { - var currentText = await this.textBasedEditor!.GetValue(); - if (currentText != text) { - await this.textBasedEditor.SetValue(text); - } - } - }); - WorkflowEditorSelectors.SelectWorkflowDefinition(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (definition) => - { - if (this.workflowDefinition != definition) - { - this.workflowDefinition = definition; - this.StateHasChanged(); - if (this.workflowDiagram != null) { - await this.workflowDiagram!.RefreshAsync(); - } - } - }); - WorkflowEditorSelectors.SelectValidationMessages(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async (messages) => - { - if (messages != null && messages.Any()) - { - await this.validationMessagesDialog!.ShowAsync(); - this.StateHasChanged(); - } - }); - WorkflowEditorSelectors.SelectIsDiagramVisible(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe((isVisible) => - { - this.isDiagramVisible = isVisible; - this.colClass = isVisible ? "col-4" : "col-6"; - this.StateHasChanged(); - }); - if (string.IsNullOrWhiteSpace(this.workflowId)) - this.Dispatcher.Dispatch(new InitializeState()); - } - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - if (this.workflowId != this.WorkflowId) - { - this.workflowId = this.WorkflowId; - if(!string.IsNullOrWhiteSpace(this.workflowId)) - this.Dispatcher.Dispatch(new InitializeState(this.workflowId)); - } - } - - protected async Task OnWorkflowDefinitionChanged(WorkflowDefinition workflowDefinition) - { - this.Dispatcher.Dispatch(new HandleFormBasedEditorChange(workflowDefinition!)); - await Task.CompletedTask; - } - - protected async Task SetTextBasedEditorLanguageAsync() - { - var model = await this.textBasedEditor!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.JSON) - await model.jsRuntime.InvokeVoidAsync("enableJsonValidation08", model.Uri); - } - - protected virtual async Task ToggletextBasedEditorLanguage(string language) - { - await this.SetTextBasedEditorLanguageAsync(); - var text = await this.textBasedEditor!.GetValue(); - this.Dispatcher.Dispatch(new ChangeTextLanguage(language, text)); - } - - protected async Task OnTextBasedEditorInit(MonacoEditorBase editor) - { - await this.SetTextBasedEditorLanguageAsync(); - await this.textBasedEditor!.SetValue(this.State.WorkflowDefinitionText); - } - - protected async Task OnTextBasedDefinitionChanged(ModelContentChangedEvent e) - { - if (!this.State.Updating && this.editorValue != null) - { - var text = await this.textBasedEditor!.GetValue(); - this.editorValue.OnNext(text); - } - } - - protected virtual void OnResetWorkflow() - { - this.Dispatcher.Dispatch(new InitializeState(false)); - } - - protected virtual void OnSaveWorkflow() - { - if (this.workflowDefinition == null) - return; - this.Dispatcher.Dispatch(new ValidateWorkflowDefinition(this.workflowDefinition, true)); - this.StateHasChanged(); - } - - protected void OnNavigateToWorkflowList() - { - this.NavigationManager.NavigateTo("/workflows"); - } - - private bool disposed; - protected override void Dispose(bool disposing) - { - if (!this.disposed) - { - if (this.disposeNotifier != null) - { - this.disposeNotifier.OnNext(true); - this.disposeNotifier.OnCompleted(); - this.disposeNotifier.Dispose(); - this.disposeNotifier = null; - } - this.disposed = true; - } - base.Dispose(disposing); - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorActions.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorActions.cs deleted file mode 100644 index d233d1a63..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorActions.cs +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -using ServerlessWorkflow.Sdk.Models; -using Synapse.Dashboard.Pages.Workflows.Editor.State; - -/// -/// Stores the actions triggered in the -/// -namespace Synapse.Dashboard.Pages.Workflows.Editor.Actions -{ - - /// - /// Triggers state initialization - /// - public class InitializeState - { - public InitializeState(bool ifNotExists = true) - { - IfNotExists = ifNotExists; - } - - public InitializeState(string workflowId) - { - WorkflowId = workflowId; - } - - /// - /// Gets/sets the id of the workflow to initialize the state with - /// - public string WorkflowId { get; } - - /// - /// Gets/sets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - - } - - /// - /// Returns the initial state - /// - public class InitializeStateSuccessful - { - public InitializeStateSuccessful(WorkflowEditorState initialState, bool ifNotExists = true) - { - this.InitialState = initialState ?? throw new ArgumentNullException(nameof(initialState)); - this.IfNotExists = ifNotExists; - } - - /// - /// The initial state - /// - public WorkflowEditorState InitialState { get; } - - /// - /// Gets/sets a boolean indicating whether or not to initialize the state only if it does not yet exist - /// - public bool IfNotExists { get; } - } - - /// - /// The action to update the workflow definition - /// - public class UpdateDefinition - { - /// - /// Initialised a new action with the provided definition - /// - /// - public UpdateDefinition(WorkflowDefinition workflowDefinition) - { - this.WorkflowDefinition = workflowDefinition ?? throw new ArgumentNullException(nameof(workflowDefinition)); - } - - /// - /// The updated workflow definition - /// - public WorkflowDefinition WorkflowDefinition { get; } - } - - /// - /// The action to update the workflow definition text - /// - public class UpdateDefinitionText - { - /// - /// Initialised a new action with the provided definition text - /// - /// - public UpdateDefinitionText(string workflowDefinitionText) - { - this.WorkflowDefinitionText = workflowDefinitionText ?? throw new ArgumentNullException(nameof(workflowDefinitionText)); - } - - /// - /// The text of the updated workflow definition - /// - public string WorkflowDefinitionText { get; } - } - - /// - /// The action dispatched when the editor starts to update - /// - public class StartUpdating { } - - /// - /// The action dispatched when the editor finished updating - /// - public class StopUpdating { } - - /// - /// Saves the specified using the Synapse API - /// - public class SaveWorkflowDefinition - { - - public SaveWorkflowDefinition(WorkflowDefinition workflowDefinition) - { - this.WorkflowDefinition = workflowDefinition; - } - - public WorkflowDefinition WorkflowDefinition { get; } - - } - - /// - /// Notifies the UI about the completion of the specified 's save - /// - public class WorkflowDefinitionSaved - { - - public WorkflowDefinitionSaved(WorkflowDefinition workflowDefinition) - { - this.WorkflowDefinition = workflowDefinition; - } - - public WorkflowDefinition WorkflowDefinition { get; } - - } - - /// - /// Notifies the UI about the failure of a 's save - /// - public class WorkflowDefinitionSaveFailed - { - - public WorkflowDefinitionSaveFailed(string error) - { - this.Error = error; - } - - public string? Error { get; } - - } - - /// - /// The action to handle changes in the form based editor - /// - public class HandleFormBasedEditorChange - { - /// - /// Initialised a new action with the provided definition - /// - /// - public HandleFormBasedEditorChange(WorkflowDefinition workflowDefinition) - { - this.WorkflowDefinition = workflowDefinition ?? throw new ArgumentNullException(nameof(workflowDefinition)); - } - - /// - /// The updated workflow definition - /// - public WorkflowDefinition WorkflowDefinition { get; } - } - - /// - /// The action to handle changes in the text based editor - /// - public class HandleTextBasedEditorChange - { - /// - /// Initialised a new action with the provided definition - /// - /// - public HandleTextBasedEditorChange(string workflowDefinitionText) - { - this.WorkflowDefinitionText = workflowDefinitionText ?? throw new ArgumentNullException(nameof(workflowDefinitionText)); - } - - /// - /// The updated workflow definition text - /// - public string WorkflowDefinitionText { get; } - } - - /// - /// The action dispatched when the text editor language changes - /// - public class ChangeTextLanguage - { - /// - /// Initialised a new action with the provided definition - /// - /// - public ChangeTextLanguage(string language, string workflowDefinitionText) - { - this.Language = language ?? throw new ArgumentNullException(nameof(language)); - this.WorkflowDefinitionText = workflowDefinitionText ?? throw new ArgumentNullException(nameof(workflowDefinitionText)); - } - - /// - /// The new language of the text editor - /// - public string Language { get; } - - /// - /// The updated workflow definition text - /// - public string WorkflowDefinitionText { get; } - } - - /// - /// Toggles the state of the specified expander - /// - public class ToggleExpand - { - - public ToggleExpand(string name, bool isExpanded) - { - this.Name = name; - this.IsExpanded = isExpanded; - } - - public string Name { get; } - - public bool IsExpanded { get; } - - } - - /// - /// Toggles the workflow diagram visibility - /// - public class ToggleDiagramVisibility { } - - /// - /// The action dispatched to valide the workflow definition - /// - public class ValidateWorkflowDefinition - { - public ValidateWorkflowDefinition(WorkflowDefinition workflowDefinition, bool saveAfterValidation) - { - this.WorkflowDefinition = workflowDefinition; - this.SaveAfterValidation = saveAfterValidation; - } - - public WorkflowDefinition WorkflowDefinition { get; } - public bool SaveAfterValidation { get; } - } - - /// - /// The action dispatched to set the validation messages - /// - public class SetValidationMessages - { - public SetValidationMessages(List validationMessages) - { - ValidationMessages = validationMessages; - } - - public List ValidationMessages { get; } - - } - - /// - /// The action dispatched a workflow definition has been validated without messages/errors - /// - public class WorkflowDefinitionValidated - { - public WorkflowDefinitionValidated(WorkflowDefinition workflowDefinition, bool saveAfterValidation) - { - this.WorkflowDefinition = workflowDefinition; - this.SaveAfterValidation = saveAfterValidation; - } - - public WorkflowDefinition WorkflowDefinition { get; } - public bool SaveAfterValidation { get; } - } - - /// - /// The action dispatched to clear the validation messages - /// - public class ClearValidationMessages { } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorEffects.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorEffects.cs deleted file mode 100644 index f3ebc6093..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorEffects.cs +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Neuroglia.Serialization; -using Newtonsoft.Json.Serialization; -using Newtonsoft.Json; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Dashboard.Pages.Workflows.Editor.Actions; -using Synapse.Dashboard.Services; -using Synapse.Dashboard.Pages.Workflows.Editor.State; -using Synapse.Apis.Management; -using Newtonsoft.Json.Schema; -using System.Text; -using ServerlessWorkflow.Sdk.Services.Validation; -using Microsoft.AspNetCore.Components; - -namespace Synapse.Dashboard.Pages.Workflows.Editor.Effects -{ - - [Effect] - public static class WorkflowEditorEffects - { - /// - /// Handles the state initialization - /// - /// The action - /// The context - /// - /// - public static async Task OnInitiliazeState(InitializeState action, IEffectContext context) - { - var monacoEditorHelper = context.Services.GetRequiredService(); - if (monacoEditorHelper == null) - throw new NullReferenceException("Unable to resolved service 'IMonacoEditorHelper'."); - var yamlConverter = context.Services.GetRequiredService(); - if (yamlConverter == null) - throw new NullReferenceException("Unable to resolved service 'IYamlConverter'."); - WorkflowDefinition definition; - if (string.IsNullOrWhiteSpace(action.WorkflowId)) - definition = new WorkflowDefinition() { Id = "undefined", Name = "Undefined", Version = "0.1.0" }; - else - definition = (await context.Services.GetRequiredService().GetWorkflowByIdAsync(action.WorkflowId)).Definition; - var text = JsonConvert.SerializeObject(definition, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - text = await yamlConverter.JsonToYaml(text); - WorkflowEditorState initialState = new() - { - WorkflowDefinition = definition, - WorkflowDefinitionText = text, - - Updating = false, - Saving = false, - IsDiagramVisible = false, - ExpanderStates = new() - { - { "general", true }, - { "states", true }, - { "events", false }, - { "functions", false }, - { "secrets", false }, - { "authentication", false }, - { "annotations", false }, - { "metadata", false } - }, - ValidationMessages = new List() - }; - context.Dispatcher.Dispatch(new InitializeStateSuccessful(initialState, action.IfNotExists)); - } - - /// - /// Handles the form editor changes - /// - /// The action - /// The context - /// - /// - public static async Task OnFormBasedEditorChange(HandleFormBasedEditorChange action, IEffectContext context) - { - var monacoEditorHelper = context.Services.GetRequiredService(); - if (monacoEditorHelper == null) - throw new NullReferenceException("Unable to resolved service 'IJsonSerializer'."); - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateDefinition(action.WorkflowDefinition)); - try - { - var text = JsonConvert.SerializeObject(action.WorkflowDefinition, Formatting.Indented, JsonConvert.DefaultSettings!()!); - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - var yamlConverter = context.Services.GetRequiredService(); - if (yamlConverter == null) - throw new NullReferenceException("Unable to resolved service 'IYamlConverter'."); - text = await yamlConverter.JsonToYaml(text); - } - context.Dispatcher.Dispatch(new UpdateDefinitionText(text)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - context.Dispatcher.Dispatch(new StopUpdating()); - } - - /// - /// Handles the text editor changes - /// - /// The action - /// The context - /// - /// - public static async Task OnTextBasedEditorChange(HandleTextBasedEditorChange action, IEffectContext context) - { - var monacoEditorHelper = context.Services.GetRequiredService(); - if (monacoEditorHelper == null) - throw new NullReferenceException("Unable to resolved service 'IMonacoEditorHelper'."); - var yamlConverter = context.Services.GetRequiredService(); - if (yamlConverter == null) - throw new NullReferenceException("Unable to resolved service 'IYamlConverter'."); - var jsonSerializer = context.Services.GetRequiredService(); - if (jsonSerializer == null) - throw new NullReferenceException("Unable to resolved service 'IJsonSerializer'."); - var text = action.WorkflowDefinitionText; - context.Dispatcher.Dispatch(new StartUpdating()); - context.Dispatcher.Dispatch(new UpdateDefinitionText(text)); - try { - if (monacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - text = await yamlConverter.YamlToJson(text); - var definition = await jsonSerializer.DeserializeAsync(text); - context.Dispatcher.Dispatch(new UpdateDefinition(definition)); - } - catch(Exception ex) - { - Console.WriteLine(ex.ToString()); - } - context.Dispatcher.Dispatch(new StopUpdating()); - } - - /// - /// Handles the text editor language changes - /// - /// The action - /// The context - /// - /// - public static async Task OnChangeTextLanguage(ChangeTextLanguage action, IEffectContext context) - { - var monacoEditorHelper = context.Services.GetRequiredService(); - if (monacoEditorHelper == null) - throw new NullReferenceException("Unable to resolved service 'IMonacoEditorHelper'."); - var yamlConverter = context.Services.GetRequiredService(); - if (yamlConverter == null) - throw new NullReferenceException("Unable to resolved service 'IYamlConverter'."); - context.Dispatcher.Dispatch(new StartUpdating()); - try - { - var text = action.Language == PreferedLanguage.YAML ? - await yamlConverter.JsonToYaml(action.WorkflowDefinitionText) : - await yamlConverter.YamlToJson(action.WorkflowDefinitionText); - context.Dispatcher.Dispatch(new UpdateDefinitionText(text)); - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - await monacoEditorHelper.ChangePreferedLanguage(action.Language == PreferedLanguage.JSON ? PreferedLanguage.YAML : PreferedLanguage.JSON); - } - context.Dispatcher.Dispatch(new StopUpdating()); - } - - /// - /// Handles the request to save the workflow - /// - /// The action - /// The context - /// - public static async Task OnSaveWorkflowDefinition(SaveWorkflowDefinition action, IEffectContext context) - { - try - { - var api = context.Services.GetRequiredService(); - if (api == null) - throw new NullReferenceException("Unable to resolved service 'ISynapseManagementApi'."); - var workflow = await api.CreateWorkflowAsync(new() { Definition = action.WorkflowDefinition }); - context.Dispatcher.Dispatch(new WorkflowDefinitionSaved(workflow.Definition)); - context.Dispatcher.Dispatch(new InitializeState(false)); - var navigationManager = context.Services.GetRequiredService(); - if (navigationManager == null) - throw new NullReferenceException("Unable to resolved service 'NavigationManager'."); - navigationManager.NavigateTo($"/workflows"); - } - catch (Exception ex) - { - context.Dispatcher.Dispatch(new WorkflowDefinitionSaveFailed(ex.Message)); - } - } - - /// - /// Validates the workflow definition - /// - /// - /// - /// - /// - public static async Task OnValidateWorkflowDefinition(ValidateWorkflowDefinition action, IEffectContext context) - { - var workflowValidator = context.Services.GetRequiredService(); - if (workflowValidator == null) - throw new NullReferenceException("Unable to resolved service 'IWorkflowValidator'."); - var validationResult = await workflowValidator.ValidateAsync(action.WorkflowDefinition, false, true); - var validationMessages = new List(); - if (!validationResult.IsValid) - { - validationResult.SchemaValidationErrors.ToList().ForEach(error => - { - validationMessages.Add($"(Schema) ${error.Message}"); - }); - validationResult.DslValidationErrors.ToList().ForEach(error => - { - validationMessages.Add($"(DSL) ${error.ErrorMessage}"); - }); - } - if (validationMessages.Any()) - { - context.Dispatcher.Dispatch(new SetValidationMessages(validationMessages)); - } - else - { - context.Dispatcher.Dispatch(new WorkflowDefinitionValidated(action.WorkflowDefinition, action.SaveAfterValidation)); - } - } - - /// - /// Triggers save workflow if validation is successful - /// - /// - /// - /// - /// - public static async Task OnWorkflowDefinitionValidated(WorkflowDefinitionValidated action, IEffectContext context) - { - if (action.SaveAfterValidation) - context.Dispatcher.Dispatch(new SaveWorkflowDefinition(action.WorkflowDefinition)); - await Task.CompletedTask; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorReducer.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorReducer.cs deleted file mode 100644 index 2e96bfaf2..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorReducer.cs +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Dashboard.Pages.Workflows.Editor.Actions; -using Synapse.Dashboard.Pages.Workflows.Editor.State; - -namespace Synapse.Dashboard.Pages.Workflows.Editor -{ - - [Reducer] - public static class WorkflowEditorReducer - { - - /// - /// Initialize the state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, InitializeStateSuccessful action) - { - if (action.IfNotExists && state.Initialized) - return state; - return action.InitialState with { Initialized = true }; - } - - /// - /// Changes the updating state when editing the definition form the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, StartUpdating action) - { - return state with - { - Updating = true - }; - } - - /// - /// Changes the updating state when failed to handle the value from the text editor - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, StopUpdating action) - { - return state with - { - Updating = false - }; - } - - /// - /// Changes the definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, UpdateDefinition action) - { - return state with - { - WorkflowDefinition = action.WorkflowDefinition - }; - } - - /// - /// Notifies about the definition being saved - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, SaveWorkflowDefinition action) - { - return state with - { - Saving = true - }; - } - - /// - /// Notifies about the completion of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, WorkflowDefinitionSaved action) - { - return state with - { - WorkflowDefinition = action.WorkflowDefinition, - Saving = false - }; - } - - /// - /// Notifies about the failure of the definition save - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, WorkflowDefinitionSaveFailed action) - { - return state with - { - Saving = false - }; - } - - /// - /// Changes the JSON definition state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, UpdateDefinitionText action) - { - return state with - { - WorkflowDefinitionText = action.WorkflowDefinitionText - }; - } - - /// - /// Toggles the state of the specified expander - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowEditorState On(WorkflowEditorState state, ToggleExpand action) - { - state.ExpanderStates![action.Name] = action.IsExpanded; - return state; - } - - /// - /// Clears the validation messages - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowEditorState On(WorkflowEditorState state, ClearValidationMessages action) - { - return state with - { - ValidationMessages = new List() - }; - } - - /// - /// Sets the validation messages - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowEditorState On(WorkflowEditorState state, SetValidationMessages action) - { - - return state with - { - ValidationMessages = new List(action.ValidationMessages) - }; - - } - - /// - /// Toggles the diagram visibiliy - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowEditorState On(WorkflowEditorState state, ToggleDiagramVisibility action) - { - return state with - { - IsDiagramVisible = !state.IsDiagramVisible - }; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorSelectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorSelectors.cs deleted file mode 100644 index 19c8e4274..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorSelectors.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Dashboard.Pages.Workflows.Editor.State; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Workflows.Editor -{ - public static class WorkflowEditorSelectors - { - /// - /// Selects the workflow definition - /// - /// The global - /// - public static IObservable SelectWorkflowDefinition(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.WorkflowDefinition) - .DistinctUntilChanged(); - } - - /// - /// Selects the workflow definition text - /// - /// The global - /// - public static IObservable SelectWorkflowDefinitionText(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.WorkflowDefinitionText) - .DistinctUntilChanged(); - } - - /// - /// Selects the workflow definition validation messages - /// - /// The global - /// - public static IObservable?> SelectValidationMessages(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.ValidationMessages) - .DistinctUntilChanged(); - } - - /// - /// Selects the workflow definition diagram visibility state - /// - /// The global - /// - public static IObservable SelectIsDiagramVisible(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.IsDiagramVisible) - .DistinctUntilChanged(); - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorState.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorState.cs deleted file mode 100644 index a6823de8e..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Editor/WorkflowEditorState.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using ServerlessWorkflow.Sdk.Models; -using Synapse.Dashboard.Pages.Workflows.Editor.Actions; - -namespace Synapse.Dashboard.Pages.Workflows.Editor.State -{ - - /// - /// The of the workflow editor - /// - [Feature] - public record WorkflowEditorState - { - - /// - /// The workflow definition - /// - public WorkflowDefinition? WorkflowDefinition { get; set; } - - /// - /// The workflow definition text representation - /// - public string? WorkflowDefinitionText { get; set; } - - /// - /// Gets a dictionary containing the name mappings of the editor's expanders states - /// - public Dictionary? ExpanderStates { get; set; } - - /// - /// Gets/sets a boolean indicating whether or not the state has been initialized - /// - /// Used in conjunction with the property - public bool Initialized { get; set; } - - /// - /// Defines if the workflow definition is being updated - /// - public bool Updating { get; set; } - - /// - /// Defines if the workflow definition is being saved - /// - public bool Saving { get; set; } - - /// - /// Defines if the diagram should be displayed - /// - public bool IsDiagramVisible { get; set; } - - /// - /// Stores the workflow definition validation messages - /// - public ICollection? ValidationMessages { get; set; } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/List.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/List.razor deleted file mode 100644 index a86a16da2..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/List.razor +++ /dev/null @@ -1,148 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/workflows" -@using Neuroglia.Data -@using Neuroglia.Data.Flux -@using Semver -@using Synapse.Integration.Models -@using System.Reactive.Linq -@inherits StatefulComponent -@inject IServiceProvider ServiceProvider -@inject IBreadcrumbManager BreadcrumbService -@inject NavigationManager NavigationManager - -Workflows - - - - - -

-
- -
-
- - - - -
- -
- -
- - - - - - - - - - -
- - - - - - - - - - - - -
-
- -@code { - - private IDisposable? _Subscription; - private Dictionary> workflows = new(); - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.Workflows); - this._Subscription = this.Feature - .Subscribe(workflows => - { - this.workflows = workflows - .GroupBy(w => w.Definition.Id!) - .ToDictionary(g => g.Key, g => g.ToList())!; - this.StateHasChanged(); - }); - this.Dispatcher.Dispatch(new ListV1Workflows()); - } - - void OnSearchWorkflows(string term) - { - this.Dispatcher.Dispatch(new SearchV1Workflows(term)); - } - - void OnClearWorkflowSearch() - { - this.Dispatcher.Dispatch(new ListV1Workflows()); - } - - void OnNewWorkflow() - { - this.NavigationManager.NavigateTo("/workflows/new"); - } - - void OnUploadWorkflow() - { - this.NavigationManager.NavigateTo("/workflows/upload"); - } - - void OnViewWorkflow(V1Workflow workflow) - { - this.NavigationManager.NavigateTo($"/workflows/{workflow.Id}"); - - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - this._Subscription?.Dispose(); - this._Subscription = null; - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/State.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/State.cs new file mode 100644 index 000000000..29ed1dee6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/State.cs @@ -0,0 +1,33 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.Workflows.List; + +/// +/// Represents the 's state +/// +public record WorkflowListState + : NamespacedResourceManagementComponentState +{ + /// + /// Gets a that contains all s + /// + public EquatableList? Operators { get; set; } + + /// + /// Gets/sets the active operator filter + /// + public string? Operator { get; set; } = null; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/Store.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/Store.cs new file mode 100644 index 000000000..433ff2327 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/Store.cs @@ -0,0 +1,79 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Api.Client.Services; +using Synapse.Resources; + +namespace Synapse.Dashboard.Pages.Workflows.List; + +/// +/// Represents the 's store +/// +/// The service used to interact with the Synapse API +/// The hub used to watch resource events +public class WorkflowListComponentStore(ISynapseApiClient apiClient, ResourceWatchEventHubClient resourceEventHub) + : NamespacedResourceManagementComponentStore(apiClient, resourceEventHub) +{ + + /// + /// Gets an used to observe changes + /// + public IObservable?> Operators => this.Select(s => s.Operators).DistinctUntilChanged(); + + /// + /// Gets an used to observe changes + /// + public IObservable Operator => this.Select(s => s.Operator).DistinctUntilChanged(); + + /// + /// Lists all available s + /// + /// A new awaitable + public async Task ListOperatorsAsync() + { + var operatorList = new EquatableList(await (await this.ApiClient.Operators.ListAsync().ConfigureAwait(false)).OrderBy(ns => ns.GetQualifiedName()).ToListAsync().ConfigureAwait(false)); + this.Reduce(s => s with + { + Operators = operatorList + }); + } + + /// + /// Sets the + /// + /// The new value + public void SetOperator(string? operatorName) + { + this.Reduce(state => state with + { + Operator = operatorName + }); + } + + /// + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + await this.ListOperatorsAsync().ConfigureAwait(false); + this.Operator.Subscribe(operatorName => { + if (string.IsNullOrWhiteSpace(operatorName)) + { + this.RemoveLabelSelector(SynapseDefaults.Resources.Labels.Operator); + } + else + { + this.AddLabelSelector(new(SynapseDefaults.Resources.Labels.Operator, LabelSelectionOperator.Equals, operatorName)); + } + }, token: this.CancellationTokenSource.Token); + } +} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/View.razor new file mode 100644 index 000000000..25b6b8e5f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Pages/Workflows/List/View.razor @@ -0,0 +1,192 @@ +@* + Copyright © 2024-Present The Synapse Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*@ + +@page "/" +@page "/workflows" +@attribute [Authorize] +@namespace Synapse.Dashboard.Pages.Workflows.List +@using BlazorBootstrap +@inherits NamespacedResourceManagementComponent +@inject IBreadcrumbManager BreadcrumbManager +@inject NavigationManager NavigationManager + +Workflows + +
+ @if (Loading) + { + + } +
+

Workflows

+ @(Resources?.Count ?? 0) items +
+ + + + +
+
+ + + + + + + + + + + + + + + + + + + @if (Resources != null && Resources.Count > 0) + { + + + + + + + + + + + + + + + + + } + +
NameNamespaceCreation TimeLast Start TimeLast End TimeVersionsLatestInstancesScheduleOperator + +
@resource.Metadata.Name + @resource.Metadata.Namespace + + @resource.Metadata.CreationTimestamp?.DateTime.RelativeFormat()@(resource.Status?.Versions.Where(v => v.Value.LastStartedAt.HasValue).Select(v => v.Value.LastStartedAt).Order().LastOrDefault()?.DateTime.RelativeFormat() ?? "-")@(resource.Status?.Versions.Where(v => v.Value.LastEndedAt.HasValue).Select(v => v.Value.LastEndedAt).Order().LastOrDefault()?.DateTime.RelativeFormat() ?? "-")@resource.Spec.Versions.Count@resource.Spec.Versions.GetLatest().Document.Version@resource.Status?.Versions.Values.Sum(s => s.TotalInstances) + @if (resource.Spec.Versions.GetLatest().Schedule == null) + { + - + } + else + { + @GetScheduleType(resource) + } + + @if (resource.Metadata.Labels?.TryGetValue(SynapseDefaults.Resources.Labels.Operator, out var operatorName) == true && !string.IsNullOrWhiteSpace(operatorName)) + { + var segments = operatorName.Split("."); + var ns = segments.Last(); + var name = segments.Take(segments.Count() - 1).Join('.'); + @operatorName + + } + else + { + - + } + + + + +
+
+ + + + + + + + + +@code +{ + + /// + /// Gets the list of available s + /// + protected EquatableList? Operators { get; set; } + + /// + /// Gets selected + /// + protected string? Operator { get; set; } + + /// + protected override void OnInitialized() + { + base.OnInitialized(); + this.BreadcrumbManager.Use(Breadcrumbs.Workflows); + this.Store.Operators.Subscribe(value => this.OnStateChanged(_ => Operators = value), token: this.CancellationTokenSource.Token); + this.Store.Operator.Subscribe(value => this.OnStateChanged(_ => Operator = value), token: this.CancellationTokenSource.Token); + } + + string GetScheduleType(Workflow workflow) + { + var latest = workflow.Spec.Versions.GetLatest(); + if (latest.Schedule == null) return "-"; + else if (latest.Schedule.After != null) return "after"; + else if (latest.Schedule.Cron != null) return "cron"; + else if (latest.Schedule.Every != null) return "every"; + else if (latest.Schedule.On != null) return "on"; + else throw new NotSupportedException("The specified schedule type is not supported"); + } + + void OnViewWorkflow(Workflow workflow) => this.NavigationManager.NavigateTo($"workflows/details/{workflow.GetNamespace()}/{workflow.GetName()}/{workflow.Spec.Versions.GetLatest().Document.Version}"); + + void OnCreateWorkflow() => this.NavigationManager.NavigateTo("/workflows/new"); + + void OnCreateWorkflowVersion(string ns, string name) => this.NavigationManager.NavigateTo($"/workflows/new/{ns}/{name}"); + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Upload.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/Upload.razor deleted file mode 100644 index 5e0d7b9ad..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/Upload.razor +++ /dev/null @@ -1,150 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors -

- Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at -

- http://www.apache.org/licenses/LICENSE-2.0 -

- Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/workflows/upload" -@using Microsoft.AspNetCore.Http -@using Microsoft.Extensions.Primitives -@using Synapse.Apis.Management -@using Synapse.Integration.Commands.Workflows -@using System.Net.Http.Headers -@implements IDisposable -@inject IBreadcrumbManager BreadcrumbService -@inject ISynapseManagementApi SynapseManagementApi -@inject NavigationManager NavigationManager - -Upload workflow - - Upload workflow - -

-
-
- - - @if (!string.IsNullOrWhiteSpace(definitionError)) { -
@definitionError
- } -
-
- - - @if (!string.IsNullOrWhiteSpace(resourcesError)) { -
@resourcesError
- } -
-
-
-
- -
-
-
- -@code { - private UploadableFile? definition = null; - private List? resources = null; - private bool shouldRender = true; - private string definitionError = ""; - private string resourcesError = ""; - private bool uploadDisabled { - get - { - return this.definition == null; - } - } - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - await this.BreadcrumbService.Use(Breadcrumbs.UploadWorkflow); - } - - protected override bool ShouldRender() => this.shouldRender; - - private async Task OnInputDefinitionFileChange(InputFileChangeEventArgs e) - { - this.shouldRender = false; - this.definitionError = ""; - try - { - this.definition = new (e.File); - } - catch (Exception ex) - { - this.definitionError = "An error occured while processing your file."; - } - this.shouldRender = true; - await Task.CompletedTask; - } - - private async Task OnInputResourcesFilesChange(InputFileChangeEventArgs e) - { - this.shouldRender = false; - this.resourcesError = ""; - try - { - this.resources = null; - if (e.FileCount > 0) { - this.resources = new List(); - foreach (var file in e.GetMultipleFiles(25)) - { - this.resources.Add(new(file)); - } - } - } - catch (Exception ex) - { - this.resourcesError = "An error occured while processing one of your files."; - } - this.shouldRender = true; - await Task.CompletedTask; - } - - private async Task OnUploadWorkflowAsync() - { - var command = new V1UploadWorkflowCommand(); - command.DefinitionFile = this.definition; - var resourceFiles = new List(); - if (this.resources != null) - { - foreach(var data in this.resources) - { - resourceFiles.Add(data); - } - } - command.ResourceFiles = resourceFiles.AsEnumerable(); - var workflow = await this.SynapseManagementApi.UploadWorkflowAsync(command); - this.NavigationManager.NavigateTo($"/workflows"); - } - - public void Dispose() - { - if (this.definition != null) - { - this.definition?.Dispose(); - this.definition = null; - } - if (this.resources != null) - { - this.resources.ForEach(resource => - { - resource.Dispose(); - }); - this.resources = null; - } - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor deleted file mode 100644 index ded451be6..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor +++ /dev/null @@ -1,448 +0,0 @@ -@* - Copyright © 2022-Present The Synapse Authors - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*@ - -@page "/workflows/{workflowId}" -@page "/workflows/{workflowId}/instance/{workflowInstanceId}" -@using Microsoft.AspNetCore.SignalR.Client -@using Neuroglia.Data.Flux -@using Neuroglia.Serialization -@using Newtonsoft.Json -@using System.Text -@using Synapse.Dashboard.Pages.Workflows.View.Actions -@using Synapse.Dashboard.Pages.Workflows.View.State -@using Synapse.Integration.Events.WorkflowInstances -@using Newtonsoft.Json.Serialization -@using System.Reactive.Subjects -@using System.Reactive.Linq -@inherits StatefulComponent -@inject ISynapseManagementApi SynapseApi -@inject IMonacoEditorHelper MonacoEditorHelper -@inject IBreadcrumbManager BreadcrumbService -@inject IJsonSerializer Serializer -@inject IJSRuntime JS -@inject IYamlConverter YamlConverter -@inject HubConnection HubConnection -@inject NavigationManager NavigationManager - -@workflow?.Id - - @if (workflow != null && activeInstance == null) - { - - } - else if (workflow != null && activeInstance != null) - { - - } - - - @if (workflow != null && activeInstance == null) - { - - } - else if (workflow != null && activeInstance != null) - { - - } - - -@if (workflow != null) -{ -
-
- -
-
- @if (activeInstance == null) - { - - - - - - - - @(EnumHelper.Stringify((V1WorkflowInstanceActivationType)context.Value)) - - - - - @(EnumHelper.Stringify((V1WorkflowInstanceStatus)context.Value)) - - - - - - - - @if (((V1WorkflowInstance)context.Value!).Status == V1WorkflowInstanceStatus.Running) - { - - } - @if (((V1WorkflowInstance)context.Value!).Status == V1WorkflowInstanceStatus.Suspended) - { - - } - @if (((V1WorkflowInstance)context.Value!).Status <= V1WorkflowInstanceStatus.Resuming) - { - - } - - @if (((V1WorkflowInstance)context.Value!).Status == V1WorkflowInstanceStatus.Faulted - || ((V1WorkflowInstance)context.Value!).Status == V1WorkflowInstanceStatus.Cancelled - || ((V1WorkflowInstance)context.Value!).Status == V1WorkflowInstanceStatus.Completed) - { - - } - - - -
-
- - @if (workflow?.Definition != null) - { -
- -
-
- -
-
-
- } -
- - - -
- } - else if (activeInstance != null) - { - - - - - - - - - - - - } -
-
- - Confirm workflow instance deletion - -

Are you sure you want to delete the workflow instance @selectedWorkflowInstanceId?

-

Because deletion cannot be reversed, make sure to download the instance artifacts before proceeding

- -
- - -
-
-} -else -{ - -} -@code -{ - - [Parameter] public string WorkflowId { get; set; } = null!; - protected string workflowId = null!; - [Parameter] public string? WorkflowInstanceId { get; set; } = null!; - protected string? workflowInstanceId = null!; - - private Modal? deleteWorkflowDialog = null!; - private string? selectedWorkflowInstanceId; - - protected MonacoEditor? editor = null; - protected Subject? disposeNotifier; - protected WorkflowDiagram? workflowDiagram = null; - protected V1Workflow? workflow = null; - protected V1WorkflowInstance? activeInstance = null; - protected IEnumerable instances = new List(); - protected List activities = new List(); - protected WorkflowToolbar? workflowToolbar = null; - - protected override async Task OnInitializedAsync() - { - this.disposeNotifier = new Subject(); - WorkflowViewSelectors.SelectWorkflow(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async workflow => - { - this.workflow = workflow; - await this.Refresh(); - }); - WorkflowViewSelectors.SelectWorkflowInstances(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async instances => - { - this.instances = instances?.Values.AsEnumerable() ?? new List(); - await this.Refresh(); - }); - WorkflowViewSelectors.SelectActiveInstance(this.Store) - .TakeUntil(this.disposeNotifier) - .Subscribe(async instance => - { - this.activeInstance = instance; - await this.Refresh(); - }); - WorkflowViewSelectors.SelectActivities(this.Store) - .Throttle(TimeSpan.FromMilliseconds(20)) - .TakeUntil(this.disposeNotifier) - .Subscribe(async activities => - { - this.activities = activities.ToList() ?? new List(); - await this.Refresh(); - }); - this.Dispatcher.Dispatch(new InitializeState()); - await this.BreadcrumbService.Use(Breadcrumbs.Workflows); - await this.BreadcrumbService.AddItem(new BreadcrumbItem(this.WorkflowId.ToString(), $"/workflows/{this.WorkflowId}")); - } - - protected override async Task OnParametersSetAsync() - { - if (this.WorkflowId != this.workflowId) - { - this.workflowId = this.WorkflowId; - this.Dispatcher.Dispatch(new GetWorkflowById(this.workflowId)); - await this.UpdateActivities(); - } - if (this.WorkflowInstanceId != this.workflowInstanceId) - { - this.workflowInstanceId = this.WorkflowInstanceId; - if (this.WorkflowInstanceId == null) - { - this.Dispatcher.Dispatch(new SetActiveInstance(null)); - this.activeInstance = null!; - } - else - { - this.Dispatcher.Dispatch(new SetActiveInstance(this.instances.FirstOrDefault(i => i.Id == this.WorkflowInstanceId))); - if (!this.BreadcrumbService.Items.Any(i => i.Link == new Uri(this.NavigationManager.Uri).PathAndQuery)) - { - await this.BreadcrumbService.AddCurrentUri($"Instance {this.WorkflowInstanceId}"); - } - } - await this.UpdateActivities(); - } - } - - protected async Task Refresh() - { - await this.UpdateActivities(); - this.UpdateMetrics(); - this.StateHasChanged(); - } - - protected async Task UpdateActivities() - { - if (this.workflowDiagram != null) - { - if (this.activeInstance == null) - { - await this.workflowDiagram.DisplayActivityStatusFor(this.instances, this.activities); - } - else - { - await this.workflowDiagram.DisplayActivityStatusFor(this.activeInstance, this.activities, true); - } - } - } - - protected void UpdateMetrics() - { - if (this.workflow != null && this.instances != null) - { - var durations = this.instances.Where(instances => instances.Duration.HasValue).Select(instance => instance.Duration!.Value); - this.workflow.TotalInstanceCount = this.instances.Count(); - this.workflow.RunningInstanceCount = this.instances.Where(instance => instance.Status <= V1WorkflowInstanceStatus.Resuming).Count(); - this.workflow.ExecutedInstanceCount = this.instances.Where(instance => instance.Status > V1WorkflowInstanceStatus.Resuming).Count(); - this.workflow.CompletedInstanceCount = this.instances.Where(instance => instance.Status == V1WorkflowInstanceStatus.Completed).Count(); - this.workflow.FaultedInstanceCount = this.instances.Where(instance => instance.Status == V1WorkflowInstanceStatus.Faulted).Count(); - this.workflow.CancelledInstanceCount = this.instances.Where(instance => instance.Status == V1WorkflowInstanceStatus.Cancelled || instance.Status == V1WorkflowInstanceStatus.Cancelling).Count(); - this.workflow.TotalInstanceExecutionTime = TimeSpan.FromMilliseconds(this.instances.Sum(instance => instance.Duration.HasValue ? instance.Duration.Value.TotalMilliseconds : 0)); - this.workflow.ShortestInstanceDuration = durations.Any() ? durations.Min() : null; - this.workflow.LongestInstanceDuration = durations.Any() ? durations.Max() : null; - } - } - - protected async Task StartWith(Neuroglia.Serialization.Dynamic? input) - { - if (this.workflowToolbar == null || input == null) - return; - var serializedInput = await this.Serializer.SerializeAsync(input); - await this.workflowToolbar.OnShowWorkflowInputModal(serializedInput); - } - - protected string GetCssClassFor(V1WorkflowInstanceStatus status) - { - return status switch - { - V1WorkflowInstanceStatus.Pending => "bg-secondary", - V1WorkflowInstanceStatus.Scheduling => "border-primary", - V1WorkflowInstanceStatus.Scheduled => "bg-primary", - V1WorkflowInstanceStatus.Starting or V1WorkflowInstanceStatus.Resuming => "border-primary text-primary", - V1WorkflowInstanceStatus.Running => "bg-primary", - V1WorkflowInstanceStatus.Suspended => "bg-warning", - V1WorkflowInstanceStatus.Faulted => "bg-danger", - V1WorkflowInstanceStatus.Cancelling => "border-danger text-primary", - V1WorkflowInstanceStatus.Cancelled => "bg-danger", - V1WorkflowInstanceStatus.Completed => "bg-success", - _ => string.Empty - }; - } - - protected async Task OnDeleteWorkflowInstanceAsync(string workflowInstanceId) - { - this.selectedWorkflowInstanceId = workflowInstanceId; - await this.deleteWorkflowDialog!.ShowAsync(); - } - - protected async Task OnConfirmDeleteWorkflowInstanceAsync() - { - await this.deleteWorkflowDialog!.HideAsync(); - await this.SynapseApi.DeleteWorkflowInstanceAsync(this.selectedWorkflowInstanceId!); - this.Dispatcher.Dispatch(new DeleteV1WorkflowInstance(this.selectedWorkflowInstanceId!)); - } - - protected async Task ViewWorkflowInstance(V1WorkflowInstance workflowInstance) - { - var destination = $"/workflows/{this.WorkflowId}/instance/{workflowInstance.Id}"; - await this.BreadcrumbService.AddItem(new BreadcrumbItem($"Instance {workflowInstance.Id}", destination)); - this.NavigationManager.NavigateTo(destination); - } - - protected async Task HandleNodeClick(GraphEventArgs e) - { - if (e.GraphElement != null) - { - switch (e.GraphElement) - { - case SubflowRefNodeViewModel: - var subFlowNode = (SubflowRefNodeViewModel)e.GraphElement; - var subFlowId = $"{subFlowNode.Subflow.WorkflowId}:{subFlowNode.Subflow.Version ?? "latest"}"; - if (this.WorkflowInstanceId == null) - { - var destination = $"/workflows/{subFlowId}"; - await this.BreadcrumbService.AddItem(new BreadcrumbItem($"Subflow {subFlowId}", destination)); - this.NavigationManager.NavigateTo(destination); - } - else - { - var destination = $"/workflows/{subFlowId}"; - await this.BreadcrumbService.AddItem(new BreadcrumbItem($"Subflow {subFlowId}", destination)); - var stateNode = this.workflowDiagram!.Graph?.AllClusters[subFlowNode.ParentId!.Value] as StateNodeViewModel; - if (stateNode != null) - { - var activity = this.activities?.FirstOrDefault(activity => - activity.Type == V1WorkflowActivityType.Action - && activity.Metadata["state"] == stateNode.State.Name - && activity.Metadata["action"] == subFlowNode.Action.Name - ); - if (activity?.Output != null) - { - var output = activity.Output.ToObject(); - var instanceId = output?.AggregateId; - if (instanceId != null) - { - destination += $"/instance/{instanceId}"; - await this.BreadcrumbService.AddItem(new BreadcrumbItem($"Instance {instanceId}", destination)); - } - } - } - this.NavigationManager.NavigateTo(destination); - } - this.StateHasChanged(); - break; - case InjectNodeViewModel: - Console.WriteLine("inject clicked"); - break; - } - } - //await Task.CompletedTask; - } - - protected virtual async Task ToggleLanguage(string language) - { - await this.OnMonacoEditorDidInit(this.editor! as MonacoEditorBase); - this.StateHasChanged(); - } - - protected async Task OnMonacoEditorDidInit(MonacoEditorBase editor) - { - var model = await (editor as MonacoEditor)!.GetModel(); - await MonacoEditorBase.SetModelLanguage(model, this.MonacoEditorHelper.PreferedLanguage); - var text = JsonConvert.SerializeObject(this.workflow!.Definition, Formatting.Indented, new JsonSerializerSettings() { ContractResolver = new NonPublicSetterContractResolver() }); - if (this.MonacoEditorHelper.PreferedLanguage == PreferedLanguage.YAML) - { - text = await this.YamlConverter.JsonToYaml(text); - } - await (editor as MonacoEditor)!.SetValue(text); - } - - private bool disposed; - protected override void Dispose(bool disposing) - { - if (!this.disposed) - { - if (this.disposeNotifier != null) - { - this.disposeNotifier.OnNext(true); - this.disposeNotifier.OnCompleted(); - this.disposeNotifier.Dispose(); - this.disposeNotifier = null; - } - this.disposed = true; - } - base.Dispose(disposing); - } - -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.css b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.css deleted file mode 100644 index bd4613a7d..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.css +++ /dev/null @@ -1,5 +0,0 @@ -.show-definition { - position: absolute; - top: 0; - right: 0; -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.min.css b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.min.css deleted file mode 100644 index 862a255b0..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.min.css +++ /dev/null @@ -1 +0,0 @@ -.show-definition{position:absolute;top:0;right:0;} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.scss b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.scss deleted file mode 100644 index 9f0ad2cc5..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/View.razor.scss +++ /dev/null @@ -1,5 +0,0 @@ -.show-definition { - position: absolute; - top: 0; - right: 0; -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewActions.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewActions.cs deleted file mode 100644 index 9a51ce48b..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewActions.cs +++ /dev/null @@ -1,110 +0,0 @@ -using Synapse.Dashboard.Pages.Workflows.View.State; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Workflows.View.Actions -{ - /// - /// Triggers state initialization - /// - public class InitializeState {} - - /// - /// Returns the initial state - /// - public class InitializeStateSuccessful - { - public InitializeStateSuccessful(WorkflowViewState initialState) - { - this.InitialState = initialState ?? throw new ArgumentNullException(nameof(initialState)); - } - - /// - /// Gets the initial state - /// - public WorkflowViewState InitialState { get; } - } - - /// - /// The action triggered to get a workflow by id - /// - public class GetWorkflowById - { - /// - /// Creates a new - /// - /// The id of the workflow to get - /// - public GetWorkflowById(string workflowId) - { - this.WorkflowId = workflowId ?? throw new ArgumentNullException(nameof(workflowId)); - } - - /// - /// Gets the workflow id - /// - public string WorkflowId { get; } - } - - /// - /// The action to set the state's workflow - /// - public class SetWorkflow - { - /// - /// Creates a new - /// - /// The new workflow value - /// - public SetWorkflow(V1Workflow workflow) - { - this.Workflow = workflow ?? throw new ArgumentNullException(nameof(workflow)); - } - - /// - /// Gets the workflow - /// - public V1Workflow Workflow { get; } - } - - /// - /// The action to set the state's workflow instances - /// - public class SetWorkflowInstances - { - /// - /// Creates a new - /// - /// The workflow instances - /// - public SetWorkflowInstances(ICollection workflowInstances) - { - this.WorkflowInstances = workflowInstances ?? throw new ArgumentNullException(nameof(workflowInstances)); - } - - /// - /// Gets the workflow instances - /// - public ICollection WorkflowInstances { get; } - } - - /// - /// The action to set the active instance - /// - public class SetActiveInstance - { - /// - /// Creates a new - /// - /// The new workflow value - /// - public SetActiveInstance(V1WorkflowInstance? instance) - { - this.Instance = instance; - } - - /// - /// Gets the active instance - /// - public V1WorkflowInstance? Instance { get; } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewEffects.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewEffects.cs deleted file mode 100644 index f02b287cf..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewEffects.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Apis.Management; -using Synapse.Dashboard.Pages.Workflows.View.Actions; -using Synapse.Dashboard.Pages.Workflows.View.State; -using Synapse.Integration.Models; -using static Synapse.EnvironmentVariables; - - -namespace Synapse.Dashboard.Pages.Workflows.View.Effects -{ - - [Effect] - public static class WorkflowViewEffects - { - /// - /// Handles the state initialization - /// - /// The action - /// The context - public static async Task On(InitializeState action, IEffectContext context) - { - WorkflowViewState initialState = new() - { - Instances = new(), - Activities = new() - }; - context.Dispatcher.Dispatch(new InitializeStateSuccessful(initialState)); - } - - /// - /// Gets the specified workflow from the API - /// - /// The action - /// The context - /// - /// - public static async Task On(GetWorkflowById action, IEffectContext context) - { - var api = context.Services.GetRequiredService(); - var workflow = await api.GetWorkflowByIdAsync(action.WorkflowId); - context.Dispatcher.Dispatch(new SetWorkflow(workflow)); - var workflowInstances = await api.GetWorkflowInstancesAsync($"$filter={nameof(V1WorkflowInstance.WorkflowId)} eq '{action.WorkflowId}'"); - context.Dispatcher.Dispatch(new SetWorkflowInstances(workflowInstances)); - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewReducer.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewReducer.cs deleted file mode 100644 index 6ba70ff37..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewReducer.cs +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Dashboard.Pages.Workflows.View.Actions; -using Synapse.Dashboard.Pages.Workflows.View.State; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Workflows.View -{ - [Reducer] - public static class WorkflowViewReducer - { - /// - /// Initialize the state - /// - /// The state to reduce - /// The action to reduce - /// The reduced state - public static WorkflowViewState On(WorkflowViewState state, InitializeStateSuccessful action) - { - return action.InitialState; - } - - /// - /// Sets the workflow - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, SetWorkflow action) - { - return state with - { - Workflow = action.Workflow, - ActiveInstance = null - }; - } - - /// - /// Sets the active instance - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, SetActiveInstance action) - { - return state with - { - ActiveInstance = action.Instance - }; - } - - /// - /// Sets the workflow instances - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, SetWorkflowInstances action) - { - return state with - { - Instances = action.WorkflowInstances.ToDictionary(instance => instance.Id), - Activities = action.WorkflowInstances.SelectMany(instance => instance.Activities).ToDictionary(activity => activity.Id) - }; - } - - /// - /// Adds a workflow instance to the list - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, AddV1WorkflowInstance action) - { - if (action.WorkflowInstance.WorkflowId != state.Workflow?.Id) - { - return state; - } - var instances = state.Instances != null ? new Dictionary(state.Instances) : new Dictionary(); - if (instances.ContainsKey(action.WorkflowInstance.Id)) - return state; - instances.Add(action.WorkflowInstance.Id, action.WorkflowInstance); - return state with - { - Instances = instances - }; - } - - /// - /// Removes an instance - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, RemoveV1WorkflowInstance action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instances = new Dictionary(state.Instances); - instances.Remove(action.Id); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as starting - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsStarting action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.StartingAt; - instance.Status = V1WorkflowInstanceStatus.Starting; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as started - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsStarted action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.StartedAt; - instance.StartedAt = action.StartedAt; - instance.Status = V1WorkflowInstanceStatus.Running; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as suspending - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsSuspending action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.SuspendingAt; - instance.Status = V1WorkflowInstanceStatus.Suspending; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as suspended - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsSuspended action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.SuspendedAt; - instance.Status = V1WorkflowInstanceStatus.Suspended; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as resuming - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsResuming action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.ResumingAt; - instance.Status = V1WorkflowInstanceStatus.Resuming; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as cancelling - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsCancelling action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.CancellingAt; - instance.Status = V1WorkflowInstanceStatus.Cancelling; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as cancelled - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsCancelled action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.CancelledAt; - instance.Status = V1WorkflowInstanceStatus.Cancelled; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as faulted - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowInstanceAsFaulted action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.FaultedAt; - instance.ExecutedAt = action.FaultedAt; - instance.Status = V1WorkflowInstanceStatus.Faulted; - instance.Error = action.Error; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Marks an instance as completed - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowAsCompleted action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.Id)) - return state; - var instance = state.Instances[action.Id]; - instance.LastModified = action.CompletedAt; - instance.ExecutedAt = action.CompletedAt; - instance.Status = V1WorkflowInstanceStatus.Completed; - instance.Output = action.Output; - var instances = new Dictionary(state.Instances); - return state with - { - Instances = instances - }; - } - - /// - /// Adds a workflow activity - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, AddV1WorkflowActivity action) - { - if (state.Instances == null) - return state; - if (!state.Instances.ContainsKey(action.WorkflowActivity.WorkflowInstanceId)) - return state; - var activities = state.Activities != null ? new Dictionary(state.Activities) : new Dictionary(); - if (activities.ContainsKey(action.WorkflowActivity.Id)) - return state; - activities.Add(action.WorkflowActivity.Id, action.WorkflowActivity); - return state with - { - Activities = activities - }; - } - - /// - /// Marks an activity as started - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowActivityAsStarted action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activity = state.Activities[action.Id]; - activity.LastModified = action.StartedAt; - activity.StartedAt = action.StartedAt; - activity.Status = V1WorkflowActivityStatus.Running; - var activities = new Dictionary(state.Activities); - return state with - { - Activities = activities - }; - } - - /// - /// Marks an activity as suspended - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowActivityAsSuspended action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activity = state.Activities[action.Id]; - activity.LastModified = action.SuspendedAt; - activity.Status = V1WorkflowActivityStatus.Suspended; - var activities = new Dictionary(state.Activities); - return state with - { - Activities = activities - }; - } - - /// - /// Marks an activity as faulted - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowActivityAsFaulted action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activity = state.Activities[action.Id]; - activity.LastModified = action.FaultedAt; - activity.Error = action.Error; - activity.Status = V1WorkflowActivityStatus.Faulted; - var activities = new Dictionary(state.Activities); - return state with - { - Activities = activities - }; - } - - /// - /// Marks an activity as cancelled - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowActivityAsCancelled action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activity = state.Activities[action.Id]; - activity.LastModified = action.CancelledAt; - activity.ExecutedAt = action.CancelledAt; - activity.Status = V1WorkflowActivityStatus.Cancelled; - var activities = new Dictionary(state.Activities); - return state with - { - Activities = activities - }; - } - - /// - /// Marks an activity as executed - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowActivityAsExecuted action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activity = state.Activities[action.Id]; - activity.LastModified = action.ExecutedAt; - activity.ExecutedAt = action.ExecutedAt; - activity.Error = action.Error; - activity.Output = action.Output; - activity.Status = action.Status; - var activities = new Dictionary(state.Activities); - return state with - { - Activities = activities - }; - } - - /// - /// Marks an activity as compensating - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowActivityAsCompensating action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activity = state.Activities[action.Id]; - activity.LastModified = action.CompensatingAt; - var activities = new Dictionary(state.Activities); - return state with - { - Activities = activities - }; - } - - /// - /// Marks an activity as compensated - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, MarkV1WorkflowActivityAsCompensated action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activity = state.Activities[action.Id]; - activity.LastModified = action.CompensatedAt; - activity.Status = V1WorkflowActivityStatus.Compensated; - var activities = new Dictionary(state.Activities); - return state with - { - Activities = activities - }; - } - - /// - /// Removes an activity - /// - /// The state to reduce - /// The action to reduce - /// - public static WorkflowViewState On(WorkflowViewState state, RemoveV1WorkflowActivity action) - { - if (state.Activities == null) - return state; - if (!state.Activities.ContainsKey(action.Id)) - return state; - var activities = new Dictionary(state.Activities); - activities.Remove(action.Id); - return state with - { - Activities = activities - }; - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewSelectors.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewSelectors.cs deleted file mode 100644 index 794a604a0..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewSelectors.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Dashboard.Pages.Workflows.View.State; -using Synapse.Integration.Models; -using System.Reactive.Linq; - -namespace Synapse.Dashboard.Pages.Workflows.View -{ - /// - /// Holds the workflow view selectors - /// - public static class WorkflowViewSelectors - { - /// - /// Selects the workflow - /// - /// The global - /// - public static IObservable SelectWorkflow(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Workflow) - .DistinctUntilChanged(); - } - - /// - /// Selects the workflow instances - /// - /// The global - /// - public static IObservable?> SelectWorkflowInstances(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.Instances) - .DistinctUntilChanged(); - } - - /// - /// Selects the displayed - /// - /// The global - /// - public static IObservable SelectActiveInstance(IStore store) - { - return store.GetFeature() - .Select(featureState => featureState.ActiveInstance) - .DistinctUntilChanged(); - } - - /// - /// Selects the activities to be displayed - /// - /// - /// - public static IObservable> SelectActivities(IStore store) - { - return store.GetFeature() - .Select(featureState => - featureState.ActiveInstance == null ? - featureState.Activities?.Values : - featureState.Activities?.Values.Where(activity => activity.WorkflowInstanceId == featureState.ActiveInstance.Id) - ) - .Select(activities => activities?.OrderBy(activity => activity.CreatedAt).ToList() ?? new List()) - .DistinctUntilChanged(); - } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewState.cs b/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewState.cs deleted file mode 100644 index 5bedfe19d..000000000 --- a/src/dashboard/Synapse.Dashboard/Pages/Workflows/View/WorkflowViewState.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Data.Flux; -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Pages.Workflows.View.State -{ - - /// - /// The of the workflow details view - /// - [Feature] - public record WorkflowViewState - { - /// - /// The displayed - /// - public V1Workflow? Workflow { get; set; } - - /// - /// The workflow instances - /// - public Dictionary? Instances { get; set; } - - /// - /// The instances activities - /// - public Dictionary? Activities { get; set; } - - /// - /// The displayed , if any focused - /// - public V1WorkflowInstance? ActiveInstance { get; set; } - } -} diff --git a/src/dashboard/Synapse.Dashboard/Program.cs b/src/dashboard/Synapse.Dashboard/Program.cs index 7eb8db9e1..c9b4fa3ab 100644 --- a/src/dashboard/Synapse.Dashboard/Program.cs +++ b/src/dashboard/Synapse.Dashboard/Program.cs @@ -1,124 +1,58 @@ -/* - * Copyright 2022-Present The Synapse Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -using CloudNative.CloudEvents; -using CloudNative.CloudEvents.NewtonsoftJson; -using Microsoft.AspNetCore.Components.Web; -using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.AspNetCore.Components.Authorization; using Neuroglia.Blazor.Dagre; -using Neuroglia.Data; -using Neuroglia.Data.Flux; -using Neuroglia.Mapping; -using Neuroglia.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using ServerlessWorkflow.Sdk; -using Simple.OData.Client; -using Synapse; -using Synapse.Dashboard; -using Synapse.Dashboard.Services; -using Synapse.Integration.Serialization.Converters; -using System; +using System.Text.Json; var builder = WebAssemblyHostBuilder.CreateDefault(args); -var baseAddress = builder.HostEnvironment.BaseAddress; + builder.RootComponents.Add("#app"); builder.RootComponents.Add("head::after"); - -builder.Services.AddTransient(provider => new HttpClient { BaseAddress = new Uri(baseAddress) }); -builder.Services.AddSynapseRestApiClient(http => http.BaseAddress = new Uri(baseAddress)); -builder.Services.AddNewtonsoftJsonSerializer(options => +builder.Services.AddLogging(); +builder.Services.AddSerialization(); +builder.Services.Configure(options => { - options.ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }; - options.Converters = new List() { new FilteredExpandoObjectConverter() }; - options.NullValueHandling = NullValueHandling.Ignore; - options.DefaultValueHandling = DefaultValueHandling.Ignore; - options.DateFormatHandling = DateFormatHandling.IsoDateFormat; - options.DateParseHandling = DateParseHandling.DateTime; - options.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + options.WriteIndented = true; }); -builder.Services.AddSingleton(); -builder.Services.AddServerlessWorkflow(); -builder.Services.AddPluralizer(); -builder.Services.AddMapper(typeof(Program).Assembly); -builder.Services.AddSingleton(new ODataClient(new ODataClientSettings() +builder.Services.AddScoped(provider => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); +builder.Services.AddSynapseHttpApiClient(options => { - BaseUri = new($"{baseAddress}api/odata"), - PayloadFormat = ODataPayloadFormat.Json -})); -builder.Services.AddSchemaRegistry(); -builder.Services.AddScoped(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddSingleton(); -builder.Services.AddScoped(); -builder.Services.AddScoped(); + options.BaseAddress = new(builder.HostEnvironment.BaseAddress); + options.TokenFactory = provider => provider.GetRequiredService().GetTokenAsync(); +}); builder.Services.AddFlux(flux => { - if (builder.HostEnvironment.IsDevelopment()) - { - flux.ScanMarkupTypeAssembly() - //.UseReduxDevTools() // leads to unresponsive UI, manually enable it if required. - ; - } - else - { - flux.ScanMarkupTypeAssembly(); - } + flux.ScanMarkupTypeAssembly(); }); -builder.Services.AddSingleton(provider => +builder.Services.AddSingleton(); +builder.Services.AddBlazorBootstrap(); +builder.Services.AddCascadingAuthenticationState(); +builder.Services.AddAuthorizationCore(); +builder.Services.AddScoped(); +builder.Services.AddScoped(provider => provider.GetRequiredService()); +builder.Services.AddOidcAuthentication(options => { - return new HubConnectionBuilder() - .WithUrl($"{baseAddress}api/ws") - .WithAutomaticReconnect() - .AddNewtonsoftJsonProtocol(options => - { - options.PayloadSerializerSettings = new() - { - ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }, - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore, - DateFormatHandling = DateFormatHandling.IsoDateFormat, - DateParseHandling = DateParseHandling.DateTime, - DateTimeZoneHandling = DateTimeZoneHandling.Utc - }; - }) - .Build(); + builder.Configuration.Bind("Authentication:OIDC", options.ProviderOptions); }); -JsonConvert.DefaultSettings = () => -{ - return new JsonSerializerSettings() - { - ContractResolver = new NonPublicSetterContractResolver() { NamingStrategy = new CamelCaseNamingStrategy() { ProcessDictionaryKeys = false, OverrideSpecifiedNames = false, ProcessExtensionDataNames = false } }, - Converters = new List() { new FilteredExpandoObjectConverter() }, - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Ignore, - DateFormatHandling = DateFormatHandling.IsoDateFormat, - DateParseHandling = DateParseHandling.DateTime, - DateTimeZoneHandling = DateTimeZoneHandling.Utc - }; -}; +builder.Services.AddSingleton(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); -await builder.Build().RunAsync(); +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Properties/launchSettings.json b/src/dashboard/Synapse.Dashboard/Properties/launchSettings.json index 4505e3664..f6dadedb2 100644 --- a/src/dashboard/Synapse.Dashboard/Properties/launchSettings.json +++ b/src/dashboard/Synapse.Dashboard/Properties/launchSettings.json @@ -1,19 +1,20 @@ { + "$schema": "http://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:48042", + "applicationUrl": "http://localhost:41347", "sslPort": 0 } }, "profiles": { - "Synapse.Dashboard": { + "http": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:42286", + "applicationUrl": "http://localhost:5079", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/dashboard/Synapse.Dashboard/Services/ApplicationAuthenticationStateProvider.cs b/src/dashboard/Synapse.Dashboard/Services/ApplicationAuthenticationStateProvider.cs new file mode 100644 index 000000000..cade74594 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/ApplicationAuthenticationStateProvider.cs @@ -0,0 +1,71 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using IdentityModel; +using Microsoft.AspNetCore.Components.Authorization; +using Synapse.Api.Client.Services; +using System.Security.Claims; + +namespace Synapse.Dashboard.Services; + +/// +/// Represents an implementation used to determine the current authentication state using Synapse Static Bearer Tokens +/// +/// The service used to interact with the Synapse API +/// The service used to manage security tokens +/// The service used to provides an abstraction for querying and managing URI navigation. +public class ApplicationAuthenticationStateProvider(ISynapseApiClient api, ISecurityTokenManager tokenManager, NavigationManager navigationManager) + : AuthenticationStateProvider +{ + + static readonly ClaimsPrincipal Anonymous = new(); + + /// + /// Gets the service used to interact with the Synapse API + /// + protected ISynapseApiClient Api { get; } = api; + + /// + /// Gets the service used to manage security tokens + /// + protected ISecurityTokenManager TokenManager { get; } = tokenManager; + + /// + public override async Task GetAuthenticationStateAsync() + { + try + { + var token = await this.TokenManager.GetTokenAsync(); + if (string.IsNullOrWhiteSpace(token)) return new AuthenticationState(Anonymous); + var profile = await this.Api.Users.GetUserProfileAsync(); + var claims = profile.Claims?.Select(c => new Claim(c.Key, c.Value)).ToList() ?? []; + var identity = new ClaimsIdentity(claims, profile.AuthenticationType, JwtClaimTypes.Name, JwtClaimTypes.Role); + var principal = new ClaimsPrincipal(identity); + return new AuthenticationState(principal); + } + catch (Exception ex) + { + // todo: better error handling + Console.WriteLine(ex); + await this.TokenManager.SetTokenAsync(""); + navigationManager.NavigateTo("/"); + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + } + } + + /// + /// Notifies the application that its authentication state has changed + /// + public void NotifyNotifyAuthenticationStateChanged() => base.NotifyAuthenticationStateChanged(this.GetAuthenticationStateAsync()); + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Services/BreadcrumbManager.cs b/src/dashboard/Synapse.Dashboard/Services/BreadcrumbManager.cs new file mode 100644 index 000000000..91eee635b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/BreadcrumbManager.cs @@ -0,0 +1,91 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; + +namespace Synapse.Dashboard.Services; + +/// +/// Represents the default implementation of the interface +/// +/// The service used for managing navigation with the current application +public class BreadcrumbManager(NavigationManager navigationManager) + : IBreadcrumbManager +{ + + /// + public event PropertyChangedEventHandler? PropertyChanged; + + List _items = []; + + /// + /// Gets the service used for managing navigation with the current application + /// + protected NavigationManager NavigationManager { get; } = navigationManager; + + /// + public IReadOnlyCollection Items => this._items; + + /// + public virtual Components.BreadcrumbItem Add(Components.BreadcrumbItem breadcrumbItem) + { + ArgumentNullException.ThrowIfNull(breadcrumbItem); + this._items.Add(breadcrumbItem); + this.NotifyChange(); + return breadcrumbItem; + } + + /// + public virtual Components.BreadcrumbItem Add(string label, string link, string? icon = null) => this.Add(new(label, link, icon)); + + /// + public virtual void Remove(Components.BreadcrumbItem breadcrumbItem) + { + ArgumentNullException.ThrowIfNull(breadcrumbItem); + this._items.Remove(breadcrumbItem); + this.NotifyChange(); + } + + /// + public virtual void Clear() + { + this._items.Clear(); + this.NotifyChange(); + } + + /// + public virtual void Use(params Components.BreadcrumbItem[] breadcrumbs) + { + this._items = [.. breadcrumbs]; + this.NotifyChange(); + } + + /// + public virtual void NavigateTo(Components.BreadcrumbItem breadcrumbItem) + { + ArgumentNullException.ThrowIfNull(breadcrumbItem); + var itemIndex = this._items.IndexOf(breadcrumbItem); + var breadcrumbItems = this._items.Take(itemIndex + 1).ToArray(); + this.Use(breadcrumbItems); + if (breadcrumbItem.Link != null) + { + this.NavigationManager.NavigateTo(breadcrumbItem.Link); + } + } + + /// + /// Notifies listeners that the breadcrumbs have changed + /// + protected void NotifyChange() => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Items))); + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Services/Cloner.cs b/src/dashboard/Synapse.Dashboard/Services/Cloner.cs deleted file mode 100644 index 07c7173f3..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/Cloner.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Neuroglia.Serialization; - -namespace Synapse.Dashboard.Services -{ - - ///

- /// Represents the default, serialization-based implementation - /// - public class Cloner - : ICloner - { - - /// - /// Initializes a new - /// - /// The used to clone objects - public Cloner(IJsonSerializer serializer) - { - this.Serializer = serializer; - } - - /// - /// Gets the used to clone objects - /// - protected IJsonSerializer Serializer { get; set; } - - /// - public T Clone(T obj) => this.Serializer.Deserialize(this.Serializer.Serialize(obj))!; - - /// - public async Task CloneAsync(T obj, CancellationToken cancellationToken) => await this.Serializer.DeserializeAsync(await this.Serializer.SerializeAsync(obj))!; - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Services/ExpressionParameterReplacer.cs b/src/dashboard/Synapse.Dashboard/Services/ExpressionParameterReplacer.cs deleted file mode 100644 index 93f99b46b..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/ExpressionParameterReplacer.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using System.Linq.Expressions; - -namespace Synapse.Dashboard.Services -{ - public class ExpressionParameterReplacer - : ExpressionVisitor - { - - public ExpressionParameterReplacer(ParameterExpression parameterExpression) - { - this.ParameterExpression = parameterExpression; - } - - protected ParameterExpression ParameterExpression { get; } - - /// - protected override Expression VisitParameter(ParameterExpression node) - { - return base.VisitParameter(this.ParameterExpression); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Services/ICloner.cs b/src/dashboard/Synapse.Dashboard/Services/ICloner.cs deleted file mode 100644 index 10f338937..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/ICloner.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard.Services -{ - - ///

- /// Defines the fundamentals of a service used to clone objects - /// - public interface ICloner - { - - /// - /// Clones the specified object - /// - /// The type of the object to clone - /// The object to clone - /// The cloned object - T Clone(T obj); - - /// - /// Clones the specified object - /// - /// The type of the object to clone - /// The object to clone - /// A - /// The cloned object - Task CloneAsync(T obj, CancellationToken cancellationToken = default); - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Services/IIntegrationEventStream.cs b/src/dashboard/Synapse.Dashboard/Services/IIntegrationEventStream.cs deleted file mode 100644 index b7262effe..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/IIntegrationEventStream.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Synapse.Integration.Models; - -namespace Synapse.Dashboard.Services -{ - - ///

- /// Defines the fundamentals of an stream - /// - public interface IIntegrationEventStream - : IObservable - { - - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Services/IStyleManager.cs b/src/dashboard/Synapse.Dashboard/Services/IStyleManager.cs deleted file mode 100644 index 5ffc10e7d..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/IStyleManager.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -namespace Synapse.Dashboard.Services -{ - ///

- /// Defines the fundamentals of a service used - /// - public interface IStyleManager - { - - /// - /// Gets the value of the variable with the specified name - /// - /// The variable to get the value of - /// A - /// The value of the variable with the specified name - Task GetVariableValueAsync(string variableName, CancellationToken cancellationToken = default); - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Services/IYamlConverter.cs b/src/dashboard/Synapse.Dashboard/Services/IYamlConverter.cs deleted file mode 100644 index 2ec4738a0..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/IYamlConverter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.JSInterop; - -namespace Synapse.Dashboard.Services -{ - /// - /// A service used to manipulate YAML - /// - public interface IYamlConverter - { - /// - /// Converts the provided JSON string to YAML - /// - /// - /// - Task YamlToJson(string json); - - /// - /// Converts the provided YAML string to JSON - /// - /// - /// - Task JsonToYaml(string yaml); - - } -} diff --git a/src/dashboard/Synapse.Dashboard/Services/IntegrationEventStream.cs b/src/dashboard/Synapse.Dashboard/Services/IntegrationEventStream.cs deleted file mode 100644 index 0870ecae9..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/IntegrationEventStream.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.SignalR.Client; -using Synapse.Apis.Monitoring; -using Synapse.Integration.Models; -using System.Reactive.Subjects; - -namespace Synapse.Dashboard.Services -{ - - ///

- /// Represents the default implementation of the interface - /// - public class IntegrationEventStream - : IIntegrationEventStream, IDisposable - { - - private bool _Disposed; - - /// - /// Initializes a new - /// - /// The current - public IntegrationEventStream(ILogger logger, HubConnection hubConnection) - { - this.Logger = logger; - this.HubConnection = hubConnection; - this.Subscription = this.HubConnection.On(nameof(ISynapseMonitoringApiClient.PublishIntegrationEvent), this.OnEvent); - } - - protected ILogger Logger { get; } - - /// - /// Gets the current - /// - protected HubConnection HubConnection { get; } - - /// - /// Gets the 's subscription - /// - protected IDisposable? Subscription { get; private set; } - - /// - /// Gets the used to observe s consumed by the - /// - protected Subject Stream { get; } = new(); - - /// - public virtual IDisposable Subscribe(IObserver observer) - { - return this.Stream.Subscribe(observer); - } - - /// - /// Handles the specified - /// - /// The to handle - protected virtual void OnEvent(V1Event e) - { - this.Stream.OnNext(e); - } - - protected virtual void Dispose(bool disposing) - { - if (!this._Disposed) - { - if (disposing) - { - this.Subscription?.Dispose(); - this.Subscription = null; - } - this._Disposed = true; - } - } - - /// - public void Dispose() - { - this.Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Services/Interfaces/IBreadcrumbManager.cs b/src/dashboard/Synapse.Dashboard/Services/Interfaces/IBreadcrumbManager.cs new file mode 100644 index 000000000..1c67e5ef4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/Interfaces/IBreadcrumbManager.cs @@ -0,0 +1,73 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; + +namespace Synapse.Dashboard.Services; + +/// +/// The service used to manage the breadcrumb +/// +public interface IBreadcrumbManager +{ + + /// + /// The event fired whenever the breadcrumb collection has changed + /// + event PropertyChangedEventHandler? PropertyChanged; + + /// + /// Gets a list containing all s + /// + IReadOnlyCollection Items { get; } + + /// + /// Adds the specified to the list + /// + /// The breadcrumb item to add + /// The added + Components.BreadcrumbItem Add(Components.BreadcrumbItem breadcrumbItem); + + /// + /// Creates a new with the specified label and icon for the active route and adds it to the list + /// + /// The label of the breadcrumb item to add + /// The link associated to the breadcrumb item to add + /// The icon, if any, of the breadcrumb item to add + /// The added + Components.BreadcrumbItem Add(string label, string link, string? icon = null); + + /// + /// Removes the specified from the list + /// + /// The to remove + void Remove(Components.BreadcrumbItem breadcrumbItem); + + /// + /// Clears all s + /// + void Clear(); + + /// + /// Replaces the current s list with the provided one + /// + /// The s to use + void Use(params Components.BreadcrumbItem[] breadcrumbs); + + /// + /// Navigate to the provided item and set the breadcrumb state accordingly + /// + /// The to navigate to + void NavigateTo(Components.BreadcrumbItem breadcrumbItem); + +} diff --git a/src/dashboard/Synapse.Dashboard/Services/Interfaces/ISecurityTokenManager.cs b/src/dashboard/Synapse.Dashboard/Services/Interfaces/ISecurityTokenManager.cs new file mode 100644 index 000000000..bd4209029 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/Interfaces/ISecurityTokenManager.cs @@ -0,0 +1,37 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Services; + +/// +/// Defines the fundamentals of a service used to manage security tokens +/// +public interface ISecurityTokenManager +{ + + /// + /// Gets the current token, if any + /// + /// A + /// The encoded token + Task GetTokenAsync(CancellationToken cancellationToken = default); + + /// + /// Sets the current token + /// + /// The encoded security token + /// A + /// A new awaitable + Task SetTokenAsync(string token, CancellationToken cancellationToken = default); + +} diff --git a/src/dashboard/Synapse.Dashboard/Services/Interfaces/IWorkflowGraphBuilder.cs b/src/dashboard/Synapse.Dashboard/Services/Interfaces/IWorkflowGraphBuilder.cs new file mode 100644 index 000000000..cee013aad --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/Interfaces/IWorkflowGraphBuilder.cs @@ -0,0 +1,32 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre.Models; +using ServerlessWorkflow.Sdk.Models; + +namespace Synapse.Dashboard.Services; + +/// +/// Defines the fundamentals of a service used to build workflow graphs +/// +public interface IWorkflowGraphBuilder +{ + + /// + /// Builds a new workflow + /// + /// The to build a new for + /// A new + IGraphViewModel Build(WorkflowDefinition workflow); + +} diff --git a/src/dashboard/Synapse.Dashboard/Services/JsInterop.cs b/src/dashboard/Synapse.Dashboard/Services/JsInterop.cs new file mode 100644 index 000000000..2534e3a86 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/JsInterop.cs @@ -0,0 +1,69 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Synapse.Dashboard.StateManagement; + +namespace Synapse.Dashboard.Services; + +/// +/// The service used to build a bridge with JS interop +/// +/// +/// Constructs a new +/// +/// The service used to interop with JS +public class JSInterop(IJSRuntime jsRuntime) + : IAsyncDisposable +{ + + /// + /// A reference to the js interop module + /// + readonly Lazy> moduleTask = new(() => jsRuntime.InvokeAsync("import", "./js/js-interop.js").AsTask()); + + /// + /// Sets a checkbox tri-state + /// + /// The of the checkbox + /// The to set + /// A + public async ValueTask SetCheckboxStateAsync(ElementReference checkbox, CheckboxState state) + { + var module = await moduleTask.Value; + await module.InvokeVoidAsync("setCheckboxState", checkbox, state); + } + + /// + /// Scrolls down the provided element + /// + /// The to scorll + /// The height to scroll to, down to the end if not provided + /// + public async ValueTask ScrollDownAsync(ElementReference element, int? height = null) + { + var module = await moduleTask.Value; + await module.InvokeVoidAsync("scrollDown", element, height); + } + + /// + public async ValueTask DisposeAsync() + { + if (moduleTask.IsValueCreated) + { + var module = await moduleTask.Value; + await module.DisposeAsync(); + } + GC.SuppressFinalize(this); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Services/MonacoInterop.cs b/src/dashboard/Synapse.Dashboard/Services/MonacoInterop.cs new file mode 100644 index 000000000..315fb4b3f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/MonacoInterop.cs @@ -0,0 +1,69 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard.Services; + +/// +/// The service used to build a bridge with the monaco interop extension +/// +/// +/// Constructs a new +/// +/// The service used to interop with JS +public class MonacoInterop(IJSRuntime jsRuntime) + : IAsyncDisposable +{ + + /// + /// A reference to the js interop module + /// + readonly Lazy> moduleTask = new(() => jsRuntime.InvokeAsync("import", "./js/monaco-editor-interop-extension.js?v=2").AsTask()); + + /// + /// Adds the provided schema to monaco editor's diagnostics options + /// + /// The JSON Schema used for validation + /// The schema identifier URI + /// The schema type, used to match the "file"/model URI + /// A + public async ValueTask AddValidationSchemaAsync(string schema, string schemaUri, string schemaType) + { + var module = await moduleTask.Value; + await module.InvokeVoidAsync("addValidationSchema", schema, schemaUri, schemaType); + } + + /// + /// Finds the range in a JSON/YAML text corresponding to a provided JSON Pointer + /// + /// The source JSON/YAML text + /// The JSON pointer to find the range for + /// The language of the source, JSON or YAML + /// The corresponding + public async ValueTask GetJsonPointerRangeAsync(string source, string jsonPointer, string language) + { + var module = await moduleTask.Value; + return await module.InvokeAsync("getJsonPointerRange", source, jsonPointer, language); + } + + /// + public async ValueTask DisposeAsync() + { + if (moduleTask.IsValueCreated) + { + var module = await moduleTask.Value; + await module.DisposeAsync(); + } + GC.SuppressFinalize(this); + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Services/SecurityTokenManager.cs b/src/dashboard/Synapse.Dashboard/Services/SecurityTokenManager.cs new file mode 100644 index 000000000..4458f13ec --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/SecurityTokenManager.cs @@ -0,0 +1,53 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Extensions.Hosting; + +namespace Synapse.Dashboard.Services; + +/// +/// Represents a service used to manage security tokens +/// +/// An instance of a JavaScript runtime to which calls may be dispatched. +/// The service used to provide information about the hosting environment an application is running in. +public class SecurityTokenManager(IJSRuntime jsRuntime, IWebAssemblyHostEnvironment hostEnvironment) + : ISecurityTokenManager +{ + + private readonly string _tokenStorageKey = "synapse-static-token"; + private readonly string _tokenStorageProvider = hostEnvironment.IsDevelopment() ? "localStorage" : "sessionStorage"; + + /// + /// Gets the current static bearer token + /// + protected string? Token { get; private set; } + + /// + public async Task GetTokenAsync(CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(this.Token)) + { + this.Token = await jsRuntime.InvokeAsync($"window.{_tokenStorageProvider}.getItem", _tokenStorageKey); + } + return this.Token; + } + + + /// + public async Task SetTokenAsync(string token, CancellationToken cancellationToken = default) + { + await jsRuntime.InvokeAsync($"window.{_tokenStorageProvider}.setItem", _tokenStorageKey, token); + this.Token = token; + } + +} diff --git a/src/dashboard/Synapse.Dashboard/Services/SpecificationSchemaManager.cs b/src/dashboard/Synapse.Dashboard/Services/SpecificationSchemaManager.cs new file mode 100644 index 000000000..7e06fd26d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/SpecificationSchemaManager.cs @@ -0,0 +1,93 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Net.Http.Json; + +namespace Synapse.Dashboard.Services; + +/// +/// The service used to download the specification schemas +/// +/// The service used to serialize/deserialize data to/from YAML +/// The service used to send HTTP requests and receive HTTP responses from a resource identified by a URI. +public class SpecificationSchemaManager(IYamlSerializer yamlSerializer, HttpClient httpClient) +{ + + Dictionary _knownSchemas = new Dictionary(); + + string? _latestVersion = null; + + /// + /// Gets the service used to serialize/deserialize data to/from YAML + /// + IYamlSerializer YamlSerializer { get; } = yamlSerializer; + + /// + /// Gets the service used to send HTTP requests and receive HTTP responses from a resource identified by a URI. + /// + HttpClient HttpClient { get; } = httpClient; + + /// + /// Gets the latest version of the specification + /// + /// A awaitable task + public async Task GetLatestVersion() + { + if (!string.IsNullOrEmpty(this._latestVersion)) return this._latestVersion; + var tags = await this.HttpClient.GetFromJsonAsync>("https://api.github.com/repos/serverlessworkflow/specification/tags"); + this._latestVersion = tags?.FirstOrDefault()?.Name; + return this._latestVersion ?? string.Empty; + } + + /// + /// Gets the specification's JSON schema for the specificed version + /// + /// The version to get the schema for + /// A awaitable task + public async Task GetSchema(string version) + { + if (this._knownSchemas.ContainsKey(version)) return this._knownSchemas[version]; + var address = $"https://raw.githubusercontent.com/serverlessworkflow/specification/{version}/schema/workflow.yaml"; + var yamlSchema = await this.HttpClient.GetStringAsync(address); + this._knownSchemas.Add(version, this.YamlSerializer.ConvertToJson(yamlSchema)); + return this._knownSchemas[version]; + } +} + + +/// +/// Represents a GitHub tag +/// +public class GitHubTag +{ + /// + /// Gets/sets the tag name + /// + public string Name { get; set; } = string.Empty; + /// + /// Gets/sets the zip url + /// + public string ZipballUrl { get; set; } = string.Empty; + /// + /// Gets/sets the tar url + /// + public string TarballUrl { get; set; } = string.Empty; + /// + /// Gets/sets the commit info + /// + public object Commit { get; set; } = string.Empty; + /// + /// Gets/sets the node id + /// + public string NodeId { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Services/StyleManager.cs b/src/dashboard/Synapse.Dashboard/Services/StyleManager.cs deleted file mode 100644 index e16ba276d..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/StyleManager.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright © 2022-Present The Synapse Authors - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -using Microsoft.AspNetCore.SignalR.Client; -using Microsoft.JSInterop; -using System.Collections.Concurrent; - -namespace Synapse.Dashboard.Services -{ - ///

- /// Represents the default implementation of the interface - /// - public class StyleManager - : IStyleManager - { - - private const string GetComputedStylePropertyJSMethodName = "getComputedStyleProperty"; - - /// - /// Initializes a new - /// - /// The service used to interact with JS - public StyleManager(IJSRuntime jSRuntime) - { - this.JSRuntime = jSRuntime; - } - - /// - /// Gets the service used to interact with JS - /// - protected IJSRuntime JSRuntime { get; } - - /// - /// Gets the key/value mappings of cached variables - /// - protected ConcurrentDictionary Variables { get; } = new(); - - /// - public virtual async Task GetVariableValueAsync(string variableName, CancellationToken cancellationToken = default) - { - if(!this.Variables.TryGetValue(variableName, out var value)) - { - value = await this.JSRuntime.InvokeAsync(GetComputedStylePropertyJSMethodName, variableName); - this.Variables.TryAdd(variableName, value); - } - return value; - } - - } - -} diff --git a/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs b/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs new file mode 100644 index 000000000..f9348456a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Services/WorkflowGraphBuilder.cs @@ -0,0 +1,692 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neuroglia.Blazor.Dagre; +using Neuroglia.Blazor.Dagre.Models; +using ServerlessWorkflow.Sdk; +using ServerlessWorkflow.Sdk.Models; +using ServerlessWorkflow.Sdk.Models.Calls; +using ServerlessWorkflow.Sdk.Models.Tasks; +using System.Diagnostics; + +namespace Synapse.Dashboard.Services; + +/// +/// Represents the default implementation of the interface +/// +/// The service to serialize and deserialize YAML +/// The service to serialize and deserialize YAML +public class WorkflowGraphBuilder(IYamlSerializer yamlSerializer, IJsonSerializer jsonSerializer) + : IWorkflowGraphBuilder +{ + + const string _portSuffix = "-port"; + const string _trySuffix = "-try"; + const string _catchSuffix = "-catch"; + const double characterSize = 8d; + + /// + /// Gets the default radius for start and end nodes + /// + public const int StartEndNodeRadius = 50; + + /// + /// Gets the service used to serialize and deserialize YAML + /// + protected IYamlSerializer YamlSerializer { get; } = yamlSerializer; + + /// + /// Gets the service used to serialize and deserialize YAML + /// + protected IJsonSerializer JsonSerializer { get; } = jsonSerializer; + + /// + public IGraphViewModel Build(WorkflowDefinition workflow) + { + ArgumentNullException.ThrowIfNull(workflow); + Stopwatch sw = Stopwatch.StartNew(); + var isEmpty = workflow.Do.Count < 1; + var graph = new GraphViewModel(); + //graph.EnableProfiling = true; + var startNode = this.BuildStartNode(!isEmpty); + var endNode = this.BuildEndNode(); + graph.AddNode(startNode); + graph.AddNode(endNode); + var nextNode = endNode; + if (!isEmpty) + { + nextNode = this.BuildTaskNode(new(workflow, graph, 0, workflow.Do.First().Key, workflow.Do.First().Value, null, "/do", null, endNode, startNode)); + if (nextNode is ClusterViewModel clusterViewModel) + { + nextNode = (NodeViewModel)clusterViewModel.AllNodes.Values.First(); + } + } + this.BuildEdge(graph, startNode, nextNode); + sw.Stop(); + Console.WriteLine($"WorkflowGraphBuilder.Build took {sw.ElapsedMilliseconds} ms"); + return graph; + } + + /// + /// Builds a new start + /// + /// A boolean indicating whether or not the node has successor + /// A new + protected virtual NodeViewModel BuildStartNode(bool hasSuccessor = false) => new StartNodeViewModel(hasSuccessor); + + /// + /// Returns the name, index and reference of the next node + /// + /// The rendering context for the task nodes + /// The current task node + /// A transition, if different from the context task definition's + /// The next task + protected TaskIdentity? GetNextTaskIdentity(TaskNodeRenderingContext context, NodeViewModel currentNode, string? transition = null) + { + transition = !string.IsNullOrWhiteSpace(transition) ? transition : context.TaskDefinition.Then; + if (transition == FlowDirective.End) return null; + if (transition == FlowDirective.Exit) + { + if (context.ParentContext == null) + { + return null; + } + return this.GetNextTaskIdentity(context.ParentContext, currentNode); + } + var nextTaskName = string.IsNullOrWhiteSpace(transition) || transition == FlowDirective.Continue + ? context.Workflow.GetTaskAfter(new(context.TaskName, context.TaskDefinition), context.ParentReference)?.Key + : transition; + if (string.IsNullOrWhiteSpace(nextTaskName)) + { + if (context.ParentContext == null) + { + return null; + } + return this.GetNextTaskIdentity(context.ParentContext, currentNode); + } + var nextTaskIndex = context.Workflow.IndexOf(nextTaskName, context.ParentReference); + var nextTaskReference = $"{context.ParentReference}/{nextTaskIndex}/{nextTaskName}"; + return new(nextTaskName, nextTaskIndex, nextTaskReference, context); + } + + /// + /// Gets the next in the graph + /// + /// The rendering context for the task nodes + /// The current task node + /// A transition, if different from the context task definition's + /// The next task + /// + protected NodeViewModel GetNextNode(TaskNodeRenderingContext context, NodeViewModel currentNode, string? transition = null) + { + var nextTaskIdentity = this.GetNextTaskIdentity(context, currentNode, transition); + if (nextTaskIdentity == null) + { + return context.EndNode; + } + var nextTask = context.Workflow.GetComponent(nextTaskIdentity.Reference) ?? throw new Exception($"Failed to find the task at '{nextTaskIdentity.Reference}' in workflow '{context.Workflow.Document.Name}.{context.Workflow.Document.Namespace}:{context.Workflow.Document.Version}'"); + if (!context.Graph.AllNodes.ContainsKey(nextTaskIdentity.Reference) && !context.Graph.AllClusters.ContainsKey(nextTaskIdentity.Reference)) + { + this.BuildTaskNode(new(nextTaskIdentity.Context.Workflow, nextTaskIdentity.Context.Graph, nextTaskIdentity.Index, nextTaskIdentity.Name, nextTask, nextTaskIdentity.Context.TaskGroup, nextTaskIdentity.Context.ParentReference, nextTaskIdentity.Context.ParentContext, nextTaskIdentity.Context.EndNode, currentNode)); + } + if (context.Graph.AllClusters.ContainsKey(nextTaskIdentity.Reference)) + { + return (NodeViewModel)context.Graph.AllClusters[nextTaskIdentity.Reference].AllNodes.First().Value; + } + return (NodeViewModel)context.Graph.AllNodes[nextTaskIdentity.Reference]; + } + + /// + /// Builds a new for the specified task + /// + /// The rendering context for the task node + /// A new + protected NodeViewModel BuildTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + return context.TaskDefinition switch + { + CallTaskDefinition => this.BuildCallTaskNode(context.OfType()), + DoTaskDefinition => this.BuildDoTaskNode(context.OfType()), + EmitTaskDefinition => this.BuildEmitTaskNode(context.OfType()), + ExtensionTaskDefinition => this.BuildExtensionTaskNode(context.OfType()), + ForTaskDefinition => this.BuildForTaskNode(context.OfType()), + ForkTaskDefinition => this.BuildForkTaskNode(context.OfType()), + ListenTaskDefinition => this.BuildListenTaskNode(context.OfType()), + RaiseTaskDefinition => this.BuildRaiseTaskNode(context.OfType()), + RunTaskDefinition => this.BuildRunTaskNode(context.OfType()), + SetTaskDefinition => this.BuildSetTaskNode(context.OfType()), + SwitchTaskDefinition => this.BuildSwitchTaskNode(context.OfType()), + TryTaskDefinition => this.BuildTryTaskNode(context.OfType()), + WaitTaskDefinition => this.BuildWaitTaskNode(context.OfType()), + _ => throw new NotSupportedException($"The specified task type '{context.TaskDefinition.GetType()}' is not supported") + } ?? throw new Exception($"Unable to define a last node for task '{context.TaskName}'"); + } + + /// + /// Builds a new for the specified call task + /// + /// The rendering context for the call task node + /// A new + protected virtual NodeViewModel BuildCallTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var content = string.Empty; + string callType; + switch (context.TaskDefinition.Call.ToLower()) + { + case "asyncapi": + { + var definition = (AsyncApiCallDefinition)this.JsonSerializer.Convert(context.TaskDefinition.With, typeof(AsyncApiCallDefinition))!; + callType = context.TaskDefinition.Call.ToLower(); + content = definition.OperationRef; + break; + } + case "grpc": + { + var definition = (GrpcCallDefinition)this.JsonSerializer.Convert(context.TaskDefinition.With, typeof(GrpcCallDefinition))!; + callType = context.TaskDefinition.Call.ToLower(); + content = definition.Service.Name; + break; + } + case "http": + { + // todo + //var definition = (HttpCallDefinition)this.JsonSerializer.Convert(context.TaskDefinition.With, typeof(HttpCallDefinition))!; + callType = context.TaskDefinition.Call.ToLower(); + //content = definition.Endpoint.Uri.ToString(); + break; + } + case "openapi": + { + var definition = (OpenApiCallDefinition)this.JsonSerializer.Convert(context.TaskDefinition.With, typeof(OpenApiCallDefinition))!; + callType = context.TaskDefinition.Call.ToLower(); + content = definition.OperationId; + break; + } + default: + callType = string.Empty; + break; + } + var node = new CallTaskNodeViewModel(context.TaskReference, context.TaskName, content, callType); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new for the specified do task + /// + /// The rendering context for the do task node + /// A new + protected virtual NodeViewModel BuildDoTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var taskCount = context.TaskDefinition.Do.Count; + var cluster = new DoTaskNodeViewModel(context.TaskReference, context.TaskName, $"{taskCount} task{(taskCount > 1 ? "s" : "")}"); + var port = new PortNodeViewModel(context.TaskReference + _portSuffix); + cluster.AddChild(port); + if (context.TaskGroup == null) context.Graph.AddCluster(cluster); + else context.TaskGroup.AddChild(cluster); + this.BuildTaskNode(new(context.Workflow, context.Graph, 0, context.TaskDefinition.Do.First().Key, context.TaskDefinition.Do.First().Value, cluster, context.TaskReference + "/do", context, context.EndNode, context.PreviousNode)); + if (taskCount > 0) + { + this.BuildEdge(context.Graph, port, (NodeViewModel)cluster.AllNodes.Skip(1).First().Value); + } + else + { + this.BuildEdge(context.Graph, port, this.GetNextNode(context, cluster)); + } + return cluster; + } + + /// + /// Builds a new for the specified emit task + /// + /// The rendering context for the emit task node + /// A new + protected virtual NodeViewModel BuildEmitTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var node = new EmitTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Emit.Event.With)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new for the specified extension task + /// + /// The rendering context for the extension task node + /// A new + protected virtual NodeViewModel BuildExtensionTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var node = new ExtensionTaskNodeViewModel(context.TaskReference, context.TaskName); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new for the specified for task + /// + /// The rendering context for the for task node + /// A new + protected virtual NodeViewModel BuildForTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var cluster = new ForTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.For)); + var port = new PortNodeViewModel(context.TaskReference + _portSuffix); + cluster.AddChild(port); + if (context.TaskGroup == null) context.Graph.AddCluster(cluster); + else context.TaskGroup.AddChild(cluster); + this.BuildTaskNode(new(context.Workflow, context.Graph, 0, context.TaskDefinition.Do.First().Key, context.TaskDefinition.Do.First().Value, cluster, context.TaskReference + "/do", context, context.EndNode, context.PreviousNode)); + if (context.TaskDefinition.Do.Count > 0) + { + this.BuildEdge(context.Graph, port, (NodeViewModel)cluster.AllNodes.Skip(1).First().Value); + } + else + { + this.BuildEdge(context.Graph, port, this.GetNextNode(context, cluster)); + } + return cluster; + } + + /// + /// Builds a new for the specified fork task + /// + /// The rendering context for the fork task node + /// A new + protected virtual NodeViewModel BuildForkTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var cluster = new ForkTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Fork)); + var entryPort = new PortNodeViewModel(context.TaskReference + _portSuffix); + var exitPort = new PortNodeViewModel(context.TaskReference + "-exit" + _portSuffix); + cluster.AddChild(entryPort); + if (context.TaskGroup == null) context.Graph.AddCluster(cluster); + else context.TaskGroup.AddChild(cluster); + for (int i = 0, c = context.TaskDefinition.Fork.Branches.Count; i + /// Builds a new for the specified listen task + /// + /// The rendering context for the listen task node + /// A new + protected virtual NodeViewModel BuildListenTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var node = new ListenTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Listen)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new for the specified raise task + /// + /// The rendering context for the raise task node + /// A new + protected virtual NodeViewModel BuildRaiseTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var node = new RaiseTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Raise.Error)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new for the specified run task + /// + /// The rendering context for the run task node + /// A new + protected virtual NodeViewModel BuildRunTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + string content = string.Empty; + string runType; + switch (context.TaskDefinition.Run.ProcessType) + { + case ProcessType.Container: + { + runType = ProcessType.Container; + content = context.TaskDefinition.Run.Container!.Image; + break; + } + case ProcessType.Shell: + { + runType = ProcessType.Shell; + content = context.TaskDefinition.Run.Shell!.Command; + break; + } + case ProcessType.Script: + { + runType = ProcessType.Script; + content = context.TaskDefinition.Run.Script!.Code ?? string.Empty; + break; + } + case ProcessType.Workflow: + { + runType = ProcessType.Workflow; + content = context.TaskDefinition.Run.Workflow!.Name; + break; + } + default: + runType = string.Empty; + break; + } + var node = new RunTaskNodeViewModel(context.TaskReference, context.TaskName, content, runType); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new for the specified set task + /// + /// The rendering context for the set task node + /// A new + protected virtual NodeViewModel BuildSetTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var node = new SetTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Set)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new for the specified switch task + /// + /// The rendering context for the switch task node + /// A new + protected virtual NodeViewModel BuildSwitchTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var node = new SwitchTaskNodeViewModel(context.TaskReference, context.TaskName, this.YamlSerializer.SerializeToText(context.TaskDefinition.Switch)); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + foreach (var switchCase in context.TaskDefinition.Switch) + { + var switchCaseNode = this.GetNextNode(context, node, switchCase.Value.Then); + this.BuildEdge(context.Graph, node, switchCaseNode, switchCase.Key); + } + if (!context.TaskDefinition.Switch.Any(switchCase => string.IsNullOrEmpty(switchCase.Value.When))) + { + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + } + return node; + } + + /// + /// Builds a new for the specified try task + /// + /// The rendering context for the try task node + /// A new + protected virtual NodeViewModel BuildTryTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var taskCount = context.TaskDefinition.Try.Count; + var containerCluster = new TryTaskNodeViewModel(context.TaskReference, context.TaskName, $"{taskCount} task{(taskCount > 1 ? "s" : "")}"); + var containerPort = new PortNodeViewModel(context.TaskReference + _portSuffix); + containerCluster.AddChild(containerPort); + if (context.TaskGroup == null) context.Graph.AddCluster(containerCluster); + else context.TaskGroup.AddChild(containerCluster); + + var tryCluster = new TryNodeViewModel(context.TaskReference + _trySuffix, context.TaskName, string.Empty); + var tryPort = new PortNodeViewModel(context.TaskReference + _trySuffix + _portSuffix); + tryCluster.AddChild(tryPort); + containerCluster.AddChild(tryCluster); + this.BuildEdge(context.Graph, containerPort, tryPort); + this.BuildTaskNode(new(context.Workflow, context.Graph, 0, context.TaskDefinition.Try.First().Key, context.TaskDefinition.Try.First().Value, tryCluster, context.TaskReference + "/try", context, context.EndNode, context.PreviousNode)); + if (taskCount > 0) + { + this.BuildEdge(context.Graph, tryPort, tryCluster.AllNodes.Values.Skip(1).First()); + } + var catchContent = this.YamlSerializer.SerializeToText(context.TaskDefinition.Catch); + if (context.TaskDefinition.Catch.Do == null || context.TaskDefinition.Catch.Do.Count == 0) + { + var catchNode = new CatchNodeViewModel(context.TaskReference + _catchSuffix, context.TaskName, catchContent); + containerCluster.AddChild(catchNode); + this.BuildEdge(context.Graph, tryCluster.AllNodes.Values.Last(), catchNode); + this.BuildEdge(context.Graph, catchNode, this.GetNextNode(context, containerCluster)); + } + else + { + var catchCluster = new CatchDoNodeViewModel(context.TaskReference + _catchSuffix, context.TaskName, catchContent); + var catchPort = new PortNodeViewModel(context.TaskReference + _catchSuffix + _portSuffix); + catchCluster.AddChild(catchPort); + containerCluster.AddChild(catchCluster); + this.BuildEdge(context.Graph, tryCluster.AllNodes.Values.Last(), catchPort); + this.BuildTaskNode(new(context.Workflow, context.Graph, 0, context.TaskDefinition.Catch.Do.First().Key, context.TaskDefinition.Catch.Do.First().Value, catchCluster, context.TaskReference + "/catch/do", context, context.EndNode, context.PreviousNode)); + this.BuildEdge(context.Graph, catchPort, catchCluster.AllNodes.Values.Skip(1).First()); + this.BuildEdge(context.Graph, catchCluster.AllNodes.Values.Last(), this.GetNextNode(context, containerCluster)); + } + return containerCluster; + } + + /// + /// Builds a new for the specified wait task + /// + /// The rendering context for the wait task node + /// A new + protected virtual NodeViewModel BuildWaitTaskNode(TaskNodeRenderingContext context) + { + ArgumentNullException.ThrowIfNull(context); + var node = new WaitTaskNodeViewModel(context.TaskReference, context.TaskName, context.TaskDefinition.Wait.ToTimeSpan().ToString("hh\\:mm\\:ss\\.fff")); + if (context.TaskGroup == null) context.Graph.AddNode(node); + else context.TaskGroup.AddChild(node); + this.BuildEdge(context.Graph, node, this.GetNextNode(context, node)); + return node; + } + + /// + /// Builds a new end + /// + /// A new + protected virtual NodeViewModel BuildEndNode() => new EndNodeViewModel(); + + /// + /// Builds an edge between two nodes + /// + /// The current + /// The node to draw the edge from + /// The node to draw the edge to + /// The edge label, if any + /// A new awaitable + protected virtual IEdgeViewModel BuildEdge(IGraphViewModel graph, INodeViewModel source, INodeViewModel target, string? label = null) + { + var edge = graph.Edges.Select(keyValuePair => keyValuePair.Value).FirstOrDefault(edge => edge.SourceId == source.Id && edge.TargetId == target.Id); + if (edge != null) + { + if (!string.IsNullOrEmpty(label)) { + edge.Label = edge.Label + " / " + label; + edge.Width = edge.Label.Length * characterSize; + } + return edge; + } + edge = new EdgeViewModel(source.Id, target.Id, label); + if (!string.IsNullOrEmpty(edge.Label)) + { + edge.LabelPosition = EdgeLabelPosition.Center; + edge.Width = edge.Label.Length * characterSize; + } + if (target.Id.EndsWith(_portSuffix)) + { + edge.EndMarkerId = null; + } + return graph.AddEdge(edge); + } + + /// + /// Represents the context for rendering a task node within a workflow. + /// + /// The workflow definition. + /// The graph view model. + /// The index of the task. + /// The name of the task. + /// The definition of the task. + /// The optional task group. + /// The reference to the parent task node. + /// The parent rendering context of the task node. + /// The end node view model. + /// The previous node view model. + protected class TaskNodeRenderingContext(WorkflowDefinition workflow, GraphViewModel graph, int taskIndex, string taskName, TaskDefinition taskDefinition, WorkflowClusterViewModel? taskGroup, string parentReference, TaskNodeRenderingContext? parentContext, NodeViewModel endNode, NodeViewModel previousNode) + { + + /// + /// Gets the workflow definition. + /// + public virtual WorkflowDefinition Workflow { get; } = workflow; + + /// + /// Gets the graph view model. + /// + public virtual GraphViewModel Graph { get; } = graph; + + /// + /// Gets the index of the task. + /// + public virtual int TaskIndex { get; } = taskIndex; + + /// + /// Gets the name of the task. + /// + public virtual string TaskName { get; } = taskName; + + /// + /// Gets the definition of the task. + /// + public virtual TaskDefinition TaskDefinition { get; } = taskDefinition; + + /// + /// Gets the optional task group. + /// + public virtual WorkflowClusterViewModel? TaskGroup { get; } = taskGroup; + + /// + /// Gets the reference of the task node in the context of the parent task node. + /// + public virtual string TaskReference => $"{this.ParentReference}/{this.TaskIndex}/{this.TaskName}"; + + /// + /// Gets the reference to the parent task node. + /// + public virtual string ParentReference { get; } = parentReference; + + /// + /// Gets the rendering context of the parent task node. + /// + public virtual TaskNodeRenderingContext? ParentContext { get; } = parentContext; + + /// + /// Gets the end node view model. + /// + public virtual NodeViewModel EndNode { get; } = endNode; + + /// + /// Gets the previous node view model. + /// + public virtual NodeViewModel PreviousNode { get; } = previousNode; + + /// + /// Creates a new instance of with the specified task definition type. + /// + /// The type of the task definition. + /// A new instance of . + public virtual TaskNodeRenderingContext OfType() where TDefinition : TaskDefinition => new(this.Workflow, this.Graph, this.TaskIndex, this.TaskName, this.TaskDefinition, this.TaskGroup, this.ParentReference, this.ParentContext, this.EndNode, this.PreviousNode); + + } + + /// + /// Represents the context for rendering a task node within a workflow, with a specific task definition type. + /// + /// The type of the task definition. + /// The workflow definition. + /// The graph view model. + /// The index of the task. + /// The name of the task. + /// The definition of the task. + /// The optional task group. + /// The reference to the parent task node. + /// The parent rendering context of the task node. + /// The end node view model. + /// The previous node view model. + protected class TaskNodeRenderingContext(WorkflowDefinition workflow, GraphViewModel graph, int taskIndex, string taskName, TaskDefinition taskDefinition, WorkflowClusterViewModel? taskGroup, string parentReference, TaskNodeRenderingContext? parentContext, NodeViewModel endNode, NodeViewModel previousNode) + : TaskNodeRenderingContext(workflow, graph, taskIndex, taskName, taskDefinition, taskGroup, parentReference, parentContext, endNode, previousNode) + where TDefinition : TaskDefinition + { + /// + /// Gets the task definition of type . + /// + public new virtual TDefinition TaskDefinition => (TDefinition)base.TaskDefinition; + + } + + /// + /// Represents the identity of a task + /// + /// The task name + /// The task index + /// The task reference + /// The task rendering context + protected class TaskIdentity(string name, int index, string reference, TaskNodeRenderingContext context) + { + /// + /// Gets the task name + /// + public string Name { get; } = name; + + /// + /// Gets the task index + /// + public int Index { get; } = index; + + /// + /// Gets the task reference + /// + public string Reference { get; } = reference; + + /// + /// Get the task rendering context + /// + public TaskNodeRenderingContext Context { get; } = context; + } +} diff --git a/src/dashboard/Synapse.Dashboard/Services/YamlConverter.cs b/src/dashboard/Synapse.Dashboard/Services/YamlConverter.cs deleted file mode 100644 index 200b7b641..000000000 --- a/src/dashboard/Synapse.Dashboard/Services/YamlConverter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.JSInterop; - -namespace Synapse.Dashboard.Services -{ - /// - public class YamlConverter - : IYamlConverter - { - protected readonly IJSRuntime jsRuntime; - protected IJSInProcessObjectReference? yamlModule = null; - - public YamlConverter(IJSRuntime jSRuntime) - { - this.jsRuntime = jSRuntime; - } - - /// - public async Task YamlToJson(string json) - { - if (this.yamlModule == null) - { - this.yamlModule = await this.jsRuntime.InvokeAsync("import", "./js/yaml-conversion.js"); - } - return await this.yamlModule.InvokeAsync("yamlToJson", json); - } - - /// - public async Task JsonToYaml(string yaml) - { - if (this.yamlModule == null) - { - this.yamlModule = await this.jsRuntime.InvokeAsync("import", "./js/yaml-conversion.js"); - } - return await this.yamlModule.InvokeAsync("jsonToYaml", yaml); - } - - } -} diff --git a/src/dashboard/Synapse.Dashboard/StatefulComponent.cs b/src/dashboard/Synapse.Dashboard/StatefulComponent.cs new file mode 100644 index 000000000..25d051d1c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/StatefulComponent.cs @@ -0,0 +1,105 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard; + +/// +/// Represents the base class for all stateful implementations +/// +/// The type of component inheriting the +/// The type of the store used to manage the component's state +/// The type of the component's state +public abstract class StatefulComponent + : ComponentBase, IDisposable + where TComponent: StatefulComponent + where TStore : ComponentStore +{ + + /// + /// Gets the current + /// + [Inject] + protected IServiceProvider ServiceProvider { get; set; } = null!; + + /// + /// Gets the 's + /// + protected CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource(); + + private TStore _store = null!; + /// + /// Gets the component's store + /// + protected TStore Store + { + get + { + if (this._store != null) return this._store; + this._store = ActivatorUtilities.CreateInstance(this.ServiceProvider); + return this._store; + } + } + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + await this.Store.InitializeAsync(); + } + + /// + /// Patches the component fields after a change + /// + /// The patch to apply + protected void OnStateChanged(Action patch) + { + patch((TComponent)this); + this.shouldRender = true; + this.StateHasChanged(); + } + + bool shouldRender = true; + /// + protected override bool ShouldRender() + { + if (!this.shouldRender) return false; + this.shouldRender = false; + return true; + } + + private bool _Disposed; + /// + /// Disposes of the component + /// + /// A boolean indicating whether or not the dispose of the component + protected virtual void Dispose(bool disposing) + { + if (!this._Disposed) + { + if (disposing) + { + this._store.Dispose(); + this.CancellationTokenSource.Dispose(); + } + this._Disposed = true; + } + } + + /// + public void Dispose() + { + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj b/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj index 6f6c9ae52..dc6388d72 100644 --- a/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj +++ b/src/dashboard/Synapse.Dashboard/Synapse.Dashboard.csproj @@ -1,48 +1,51 @@ - + + - net6.0 + net8.0 enable enable - service-worker-assets.js - embedded - false - False + en + True + Default + - - - - + + + - - - - - - - - - - - - - - - - - + <_ContentIncludedByDefault Remove="wwwroot\css\horizontal-collapse.scss" /> + <_ContentIncludedByDefault Remove="wwwroot\css\workflow-instance-details.scss" /> + - + + + + + - - + + + + + + + + + - + + + + + + true - \ No newline at end of file + + diff --git a/src/dashboard/Synapse.Dashboard/Usings.cs b/src/dashboard/Synapse.Dashboard/Usings.cs new file mode 100644 index 000000000..6dabb1c05 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/Usings.cs @@ -0,0 +1,31 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +global using BlazorBootstrap; +global using BlazorMonaco.Editor; +global using Microsoft.AspNetCore.Components; +global using Microsoft.AspNetCore.Components.Web; +global using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +global using Microsoft.JSInterop; +global using Neuroglia; +global using Neuroglia.Data.Infrastructure.ResourceOriented; +global using Neuroglia.Reactive; +global using Neuroglia.Serialization; +global using Synapse.Api.Client; +global using Synapse.Dashboard; +global using Synapse.Dashboard.Components; +global using Synapse.Dashboard.Components.ResourceManagement; +global using Synapse.Dashboard.Layout; +global using Synapse.Dashboard.Services; +global using Synapse.Dashboard.StateManagement; +global using System.Reactive.Linq; diff --git a/src/dashboard/Synapse.Dashboard/_Imports.razor b/src/dashboard/Synapse.Dashboard/_Imports.razor index f178baf08..3e6ac9bdd 100644 --- a/src/dashboard/Synapse.Dashboard/_Imports.razor +++ b/src/dashboard/Synapse.Dashboard/_Imports.razor @@ -1,12 +1,12 @@ -@* - Copyright © 2022-Present The Synapse Authors -

+@* + Copyright © 2024-Present The Synapse Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -

+ http://www.apache.org/licenses/LICENSE-2.0 -

+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -14,26 +14,35 @@ limitations under the License. *@ -@using System.ComponentModel -@using System.Net.Http -@using System.Net.Http.Json +@using BlazorBootstrap +@using BlazorMonaco; +@using BlazorMonaco.Editor; +@using IdentityModel +@using moment.net +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication @using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.Extensions.Options @using Microsoft.JSInterop - -@using BlazorMonaco -@using Neuroglia @using Neuroglia.Blazor.Dagre @using Neuroglia.Blazor.Dagre.Models @using Neuroglia.Blazor.Dagre.Templates -@using Neuroglia.Data.Flux -@using ServerlessWorkflow.Sdk -@using ServerlessWorkflow.Sdk.Models -@using Synapse.Apis.Management -@using Synapse.Apis.Monitoring @using Synapse.Dashboard +@using Synapse.Dashboard.Components +@using Synapse.Dashboard.Configuration +@using Synapse.Dashboard.Extensions +@using Synapse.Dashboard.Layout @using Synapse.Dashboard.Services -@using Synapse.Integration.Models \ No newline at end of file +@using Synapse.Dashboard.StateManagement +@using Synapse.Resources +@using System.ComponentModel +@using System.Net.Http +@using System.Net.Http.Json +@using System.Reactive.Linq; +@using System.Reactive.Subjects +@using System.Security.Claims \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/compilerconfig.json b/src/dashboard/Synapse.Dashboard/compilerconfig.json index daabdf0bb..6a111235d 100644 --- a/src/dashboard/Synapse.Dashboard/compilerconfig.json +++ b/src/dashboard/Synapse.Dashboard/compilerconfig.json @@ -4,47 +4,15 @@ "inputFile": "wwwroot/css/app.scss" }, { - "outputFile": "Features/Shared/Breadcrumb/Breadcrumb.razor.css", - "inputFile": "Features/Shared/Breadcrumb/Breadcrumb.razor.scss" + "outputFile": "Components/Layout/MainLayout.razor.css", + "inputFile": "Components/Layout/MainLayout.razor.scss" }, { - "outputFile": "Features/Shared/Layout/MainLayout.razor.css", - "inputFile": "Features/Shared/Layout/MainLayout.razor.scss" + "outputFile": "Components/ResourceEditor/ResourceEditor.razor.css", + "inputFile": "Components/ResourceEditor/ResourceEditor.razor.scss" }, { - "outputFile": "Features/Shared/Layout/NavMenu.razor.css", - "inputFile": "Features/Shared/Layout/NavMenu.razor.scss" - }, - { - "outputFile": "Features/Shared/Loader/Loader.razor.css", - "inputFile": "Features/Shared/Loader/Loader.razor.scss" - }, - { - "outputFile": "Features/Shared/Toast/Toaster.razor.css", - "inputFile": "Features/Shared/Toast/Toaster.razor.scss" - }, - { - "outputFile": "Pages/Workflows/View/View.razor.css", - "inputFile": "Pages/Workflows/View/View.razor.scss" - }, - { - "outputFile": "Features/Shared/Layout/Header.razor.css", - "inputFile": "Features/Shared/Layout/Header.razor.scss" - }, - { - "outputFile": "Features/Shared/Toolbar/Toolbar.razor.css", - "inputFile": "Features/Shared/Toolbar/Toolbar.razor.scss" - }, - { - "outputFile": "Features/Shared/SearchBox/SearchBox.razor.css", - "inputFile": "Features/Shared/SearchBox/SearchBox.razor.scss" - }, - { - "outputFile": "Pages/Correlations/List.css", - "inputFile": "Pages/Correlations/List.scss" - }, - { - "outputFile": "Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.css", - "inputFile": "Features/Workflows/WorkflowEditor/JqExpressionEditor.razor.scss" + "outputFile": "Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.css", + "inputFile": "Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.scss" } ] \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/compilerconfig.json.defaults b/src/dashboard/Synapse.Dashboard/compilerconfig.json.defaults index 25d4abc06..41870cc54 100644 --- a/src/dashboard/Synapse.Dashboard/compilerconfig.json.defaults +++ b/src/dashboard/Synapse.Dashboard/compilerconfig.json.defaults @@ -1,12 +1,59 @@ { "compilers": { + "less": { + "autoPrefix": "", + "cssComb": "none", + "ieCompat": true, + "math": null, + "strictMath": false, + "strictUnits": false, + "relativeUrls": true, + "rootPath": "", + "sourceMapRoot": "", + "sourceMapBasePath": "", + "sourceMap": false + }, "sass": { "autoPrefix": "", "loadPaths": "", "style": "expanded", - "sourceMapUrls": "relative", "relativeUrls": true, "sourceMap": false + }, + "nodesass": { + "autoPrefix": "", + "includePath": "", + "indentType": "space", + "indentWidth": 2, + "outputStyle": "nested", + "precision": 5, + "relativeUrls": true, + "sourceMapRoot": "", + "lineFeed": "", + "sourceMap": false + }, + "stylus": { + "sourceMap": false + }, + "babel": { + "sourceMap": false + }, + "coffeescript": { + "bare": false, + "runtimeMode": "node", + "sourceMap": false + }, + "handlebars": { + "root": "", + "noBOM": false, + "name": "", + "namespace": "", + "knownHelpersOnly": false, + "forcePartial": false, + "knownHelpers": [], + "commonjs": "", + "amd": false, + "sourceMap": false } }, "minifiers": { @@ -14,6 +61,11 @@ "enabled": true, "termSemicolons": true, "gzip": false + }, + "javascript": { + "enabled": true, + "termSemicolons": true, + "gzip": false } } } \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/enum.cs b/src/dashboard/Synapse.Dashboard/enum.cs new file mode 100644 index 000000000..e52770f60 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/enum.cs @@ -0,0 +1,33 @@ +// Copyright © 2024-Present The Synapse Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"), +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Synapse.Dashboard; + +///

+/// Represents the states of a tri-state checkbox +/// +public enum CheckboxState +{ + /// + /// The indeterminate state + /// + Indeterminate = -1, + /// + /// The unchecked state + /// + Unchecked = 0, + /// + /// The checked state + /// + Checked = 1, +} diff --git a/src/dashboard/Synapse.Dashboard/libman.json b/src/dashboard/Synapse.Dashboard/libman.json index 7975e5e18..90fce772c 100644 --- a/src/dashboard/Synapse.Dashboard/libman.json +++ b/src/dashboard/Synapse.Dashboard/libman.json @@ -3,50 +3,10 @@ "defaultProvider": "cdnjs", "libraries": [ { - "provider": "unpkg", - "library": "bootstrap@5.1.3", + "provider": "jsdelivr", + "library": "bootstrap@5.3.3", "destination": "wwwroot/lib/bootstrap/", "files": [ - "scss/bootstrap-grid.scss", - "scss/bootstrap-reboot.scss", - "scss/bootstrap-utilities.scss", - "scss/bootstrap.scss", - "scss/_accordion.scss", - "scss/_alert.scss", - "scss/_badge.scss", - "scss/_breadcrumb.scss", - "scss/_button-group.scss", - "scss/_buttons.scss", - "scss/_card.scss", - "scss/_carousel.scss", - "scss/_close.scss", - "scss/_containers.scss", - "scss/_dropdown.scss", - "scss/_forms.scss", - "scss/_functions.scss", - "scss/_grid.scss", - "scss/_helpers.scss", - "scss/_images.scss", - "scss/_list-group.scss", - "scss/_mixins.scss", - "scss/_modal.scss", - "scss/_nav.scss", - "scss/_navbar.scss", - "scss/_offcanvas.scss", - "scss/_pagination.scss", - "scss/_placeholders.scss", - "scss/_popover.scss", - "scss/_progress.scss", - "scss/_reboot.scss", - "scss/_root.scss", - "scss/_spinners.scss", - "scss/_tables.scss", - "scss/_toasts.scss", - "scss/_tooltip.scss", - "scss/_transitions.scss", - "scss/_type.scss", - "scss/_utilities.scss", - "scss/_variables.scss", "scss/forms/_floating-labels.scss", "scss/forms/_form-check.scss", "scss/forms/_form-control.scss", @@ -57,7 +17,10 @@ "scss/forms/_labels.scss", "scss/forms/_validation.scss", "scss/helpers/_clearfix.scss", + "scss/helpers/_color-bg.scss", "scss/helpers/_colored-links.scss", + "scss/helpers/_focus-ring.scss", + "scss/helpers/_icon-link.scss", "scss/helpers/_position.scss", "scss/helpers/_ratio.scss", "scss/helpers/_stacks.scss", @@ -67,12 +30,14 @@ "scss/helpers/_vr.scss", "scss/mixins/_alert.scss", "scss/mixins/_backdrop.scss", + "scss/mixins/_banner.scss", "scss/mixins/_border-radius.scss", "scss/mixins/_box-shadow.scss", "scss/mixins/_breakpoints.scss", "scss/mixins/_buttons.scss", "scss/mixins/_caret.scss", "scss/mixins/_clearfix.scss", + "scss/mixins/_color-mode.scss", "scss/mixins/_color-scheme.scss", "scss/mixins/_container.scss", "scss/mixins/_deprecate.scss", @@ -92,66 +57,97 @@ "scss/mixins/_visually-hidden.scss", "scss/utilities/_api.scss", "scss/vendor/_rfs.scss", - "LICENSE" + "scss/bootstrap-grid.scss", + "scss/bootstrap-reboot.scss", + "scss/bootstrap-utilities.scss", + "scss/bootstrap.scss", + "scss/_accordion.scss", + "scss/_alert.scss", + "scss/_badge.scss", + "scss/_breadcrumb.scss", + "scss/_button-group.scss", + "scss/_buttons.scss", + "scss/_card.scss", + "scss/_carousel.scss", + "scss/_close.scss", + "scss/_containers.scss", + "scss/_dropdown.scss", + "scss/_forms.scss", + "scss/_functions.scss", + "scss/_grid.scss", + "scss/_helpers.scss", + "scss/_images.scss", + "scss/_list-group.scss", + "scss/_maps.scss", + "scss/_mixins.scss", + "scss/_modal.scss", + "scss/_nav.scss", + "scss/_navbar.scss", + "scss/_offcanvas.scss", + "scss/_pagination.scss", + "scss/_placeholders.scss", + "scss/_popover.scss", + "scss/_progress.scss", + "scss/_reboot.scss", + "scss/_root.scss", + "scss/_spinners.scss", + "scss/_tables.scss", + "scss/_toasts.scss", + "scss/_tooltip.scss", + "scss/_transitions.scss", + "scss/_type.scss", + "scss/_utilities.scss", + "scss/_variables-dark.scss", + "scss/_variables.scss", + "LICENSE", + "package.json", + "README.md", + "dist/js/bootstrap.bundle.js", + "dist/js/bootstrap.bundle.js.map", + "dist/js/bootstrap.bundle.min.js", + "dist/js/bootstrap.bundle.min.js.map", + "dist/js/bootstrap.esm.js", + "dist/js/bootstrap.esm.js.map", + "dist/js/bootstrap.esm.min.js", + "dist/js/bootstrap.esm.min.js.map", + "dist/js/bootstrap.js", + "dist/js/bootstrap.js.map", + "dist/js/bootstrap.min.js", + "dist/js/bootstrap.min.js.map" ] }, { - "provider": "unpkg", - "library": "open-iconic@1.1.1", - "destination": "wwwroot/lib/open-iconic/", - "files": [ - "font/css/open-iconic-bootstrap.css", - "font/css/open-iconic-bootstrap.less", - "font/css/open-iconic-bootstrap.min.css", - "font/css/open-iconic-bootstrap.scss", - "font/css/open-iconic-bootstrap.styl", - "font/css/open-iconic-foundation.css", - "font/css/open-iconic-foundation.less", - "font/css/open-iconic-foundation.min.css", - "font/css/open-iconic-foundation.scss", - "font/css/open-iconic-foundation.styl", - "font/css/open-iconic.css", - "font/css/open-iconic.less", - "font/css/open-iconic.min.css", - "font/css/open-iconic.scss", - "font/css/open-iconic.styl", - "font/fonts/open-iconic.eot", - "font/fonts/open-iconic.otf", - "font/fonts/open-iconic.svg", - "font/fonts/open-iconic.ttf", - "font/fonts/open-iconic.woff", - "FONT-LICENSE", - "ICON-LICENSE" - ] + "provider": "jsdelivr", + "library": "bootstrap-icons@1.11.3", + "destination": "wwwroot/lib/bootstrap-icons/" }, { - "provider": "unpkg", - "library": "jq-web@0.5.1", - "destination": "wwwroot/lib/jq-web/", - "files": [ - "LICENSE", - "jq.wasm.js", - "jq.wasm.wasm" - ] + "provider": "jsdelivr", + "library": "monaco-yaml-prebuilt@1.0.0", + "destination": "wwwroot/lib/monaco-yaml-prebuilt/" }, { - "provider": "unpkg", - "library": "chart.js@3.7.1", - "destination": "wwwroot/lib/chart.js/", + "provider": "jsdelivr", + "library": "@neuroglia/event-drops@1.4.0", + "destination": "wwwroot/lib/neuroglia/event-drops/", "files": [ - "dist/chart.js", - "dist/chart.min.js" + "LICENSE", + "dist/index.js", + "dist/index.js.map", + "dist/index.min.js", + "dist/style.css", + "dist/style.min.css" ] }, - { - "provider": "unpkg", - "library": "js-yaml@4.1.0", - "destination": "wwwroot/lib/js-yaml/" - }, { "provider": "jsdelivr", - "library": "bootstrap-icons@1.9.1", - "destination": "wwwroot/lib/bootstrap-icons/" + "library": "d3@7.8.2", + "destination": "wwwroot/lib/d3/", + "files": [ + "dist/d3.js", + "dist/d3.min.js", + "LICENSE" + ] } ] } \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/appsettings.json b/src/dashboard/Synapse.Dashboard/wwwroot/appsettings.json new file mode 100644 index 000000000..e3137fa11 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/appsettings.json @@ -0,0 +1,8 @@ +{ + "Authentication": { + "Oidc": { + "Authority": "https://google.com", + "ClientId": "123456789" + } + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/app.css b/src/dashboard/Synapse.Dashboard/wwwroot/css/app.css index ce90e7325..7eb2a1cff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/css/app.css +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/app.css @@ -1,9 +1,15 @@ @charset "UTF-8"; -@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;1,400&display=swap"); +/*! + * Bootstrap Icons v1.11.3 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ +@import url("https://fonts.googleapis.com/css2?family=Inconsolata:wght@200..900&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;400;500;600;700&display=swap"); @font-face { font-display: block; font-family: "bootstrap-icons"; - src: url("/lib/bootstrap-icons/font/fonts//bootstrap-icons.woff2?8d200481aa7f02a2d63a331fc782cfaf") format("woff2"), url("/lib/bootstrap-icons/font/fonts//bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("woff"); + src: url("../lib/bootstrap-icons/font/fonts/bootstrap-icons.woff2?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff2"), url("../lib/bootstrap-icons/font/fonts/bootstrap-icons.woff?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff"); } .bi::before, [class^=bi-]::before, @@ -1704,10 +1710,6 @@ content: "\f2a4"; } -.bi-cloud-haze-1::before { - content: "\f2a5"; -} - .bi-cloud-haze-fill::before { content: "\f2a6"; } @@ -5688,10 +5690,6 @@ content: "\f689"; } -.bi-envelope-check-1::before { - content: "\f68a"; -} - .bi-envelope-check-fill::before { content: "\f68b"; } @@ -5700,10 +5698,6 @@ content: "\f68c"; } -.bi-envelope-dash-1::before { - content: "\f68d"; -} - .bi-envelope-dash-fill::before { content: "\f68e"; } @@ -5712,10 +5706,6 @@ content: "\f68f"; } -.bi-envelope-exclamation-1::before { - content: "\f690"; -} - .bi-envelope-exclamation-fill::before { content: "\f691"; } @@ -5732,10 +5722,6 @@ content: "\f694"; } -.bi-envelope-slash-1::before { - content: "\f695"; -} - .bi-envelope-slash-fill::before { content: "\f696"; } @@ -5744,10 +5730,6 @@ content: "\f697"; } -.bi-envelope-x-1::before { - content: "\f698"; -} - .bi-envelope-x-fill::before { content: "\f699"; } @@ -5784,14 +5766,6 @@ content: "\f6a1"; } -.bi-mortorboard-fill::before { - content: "\f6a2"; -} - -.bi-mortorboard::before { - content: "\f6a3"; -} - .bi-nintendo-switch::before { content: "\f6a4"; } @@ -5864,10 +5838,6 @@ content: "\f6b5"; } -.bi-send-exclamation-1::before { - content: "\f6b6"; -} - .bi-send-exclamation-fill::before { content: "\f6b7"; } @@ -5912,10 +5882,6 @@ content: "\f6c1"; } -.bi-terminal-dash-1::before { - content: "\f6c2"; -} - .bi-terminal-dash::before { content: "\f6c3"; } @@ -6032,10 +5998,6 @@ content: "\f6df"; } -.bi-displayport-1::before { - content: "\f6e0"; -} - .bi-displayport::before { content: "\f6e1"; } @@ -6084,14 +6046,6 @@ content: "\f6ec"; } -.bi-ssd-fill::before { - content: "\f6ed"; -} - -.bi-ssd::before { - content: "\f6ee"; -} - .bi-thunderbolt-fill::before { content: "\f6ef"; } @@ -6516,10 +6470,6 @@ content: "\f758"; } -.bi-filetype-ppt-1::before { - content: "\f759"; -} - .bi-filetype-ppt::before { content: "\f75a"; } @@ -6580,10 +6530,6 @@ content: "\f768"; } -.bi-filetype-xls-1::before { - content: "\f769"; -} - .bi-filetype-xls::before { content: "\f76a"; } @@ -6752,14 +6698,6 @@ content: "\f793"; } -.bi-1-circle-1::before { - content: "\f794"; -} - -.bi-1-circle-fill-1::before { - content: "\f795"; -} - .bi-1-circle-fill::before { content: "\f796"; } @@ -6776,14 +6714,6 @@ content: "\f799"; } -.bi-2-circle-1::before { - content: "\f79a"; -} - -.bi-2-circle-fill-1::before { - content: "\f79b"; -} - .bi-2-circle-fill::before { content: "\f79c"; } @@ -6800,14 +6730,6 @@ content: "\f79f"; } -.bi-3-circle-1::before { - content: "\f7a0"; -} - -.bi-3-circle-fill-1::before { - content: "\f7a1"; -} - .bi-3-circle-fill::before { content: "\f7a2"; } @@ -6824,14 +6746,6 @@ content: "\f7a5"; } -.bi-4-circle-1::before { - content: "\f7a6"; -} - -.bi-4-circle-fill-1::before { - content: "\f7a7"; -} - .bi-4-circle-fill::before { content: "\f7a8"; } @@ -6848,14 +6762,6 @@ content: "\f7ab"; } -.bi-5-circle-1::before { - content: "\f7ac"; -} - -.bi-5-circle-fill-1::before { - content: "\f7ad"; -} - .bi-5-circle-fill::before { content: "\f7ae"; } @@ -6872,14 +6778,6 @@ content: "\f7b1"; } -.bi-6-circle-1::before { - content: "\f7b2"; -} - -.bi-6-circle-fill-1::before { - content: "\f7b3"; -} - .bi-6-circle-fill::before { content: "\f7b4"; } @@ -6896,14 +6794,6 @@ content: "\f7b7"; } -.bi-7-circle-1::before { - content: "\f7b8"; -} - -.bi-7-circle-fill-1::before { - content: "\f7b9"; -} - .bi-7-circle-fill::before { content: "\f7ba"; } @@ -6920,14 +6810,6 @@ content: "\f7bd"; } -.bi-8-circle-1::before { - content: "\f7be"; -} - -.bi-8-circle-fill-1::before { - content: "\f7bf"; -} - .bi-8-circle-fill::before { content: "\f7c0"; } @@ -6944,14 +6826,6 @@ content: "\f7c3"; } -.bi-9-circle-1::before { - content: "\f7c4"; -} - -.bi-9-circle-fill-1::before { - content: "\f7c5"; -} - .bi-9-circle-fill::before { content: "\f7c6"; } @@ -7024,14 +6898,6 @@ content: "\f7d7"; } -.bi-c-circle-1::before { - content: "\f7d8"; -} - -.bi-c-circle-fill-1::before { - content: "\f7d9"; -} - .bi-c-circle-fill::before { content: "\f7da"; } @@ -7072,14 +6938,6 @@ content: "\f7e3"; } -.bi-cc-circle-1::before { - content: "\f7e4"; -} - -.bi-cc-circle-fill-1::before { - content: "\f7e5"; -} - .bi-cc-circle-fill::before { content: "\f7e6"; } @@ -7152,14 +7010,6 @@ content: "\f7f7"; } -.bi-h-circle-1::before { - content: "\f7f8"; -} - -.bi-h-circle-fill-1::before { - content: "\f7f9"; -} - .bi-h-circle-fill::before { content: "\f7fa"; } @@ -7192,14 +7042,6 @@ content: "\f801"; } -.bi-p-circle-1::before { - content: "\f802"; -} - -.bi-p-circle-fill-1::before { - content: "\f803"; -} - .bi-p-circle-fill::before { content: "\f804"; } @@ -7232,14 +7074,6 @@ content: "\f80b"; } -.bi-r-circle-1::before { - content: "\f80c"; -} - -.bi-r-circle-fill-1::before { - content: "\f80d"; -} - .bi-r-circle-fill::before { content: "\f80e"; } @@ -7436,935 +7270,2054 @@ content: "\f83e"; } -/*! -* Bootstrap v5.1.3 (https://getbootstrap.com/) -* Copyright 2011-2021 The Bootstrap Authors -* Copyright 2011-2021 Twitter, Inc. -* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) -*/ -:root { - --bs-blue: #0d6efd; - --bs-indigo: #6610f2; - --bs-purple: #6f42c1; - --bs-pink: #d63384; - --bs-red: #dc3545; - --bs-orange: #fd7e14; - --bs-yellow: #ffc107; - --bs-green: #198754; - --bs-teal: #20c997; - --bs-cyan: #0dcaf0; - --bs-white: #fff; - --bs-gray: #6c757d; - --bs-gray-dark: #343a40; - --bs-gray-100: #f8f9fa; - --bs-gray-200: #e9ecef; - --bs-gray-300: #dee2e6; - --bs-gray-400: #ced4da; - --bs-gray-500: #adb5bd; - --bs-gray-600: #6c757d; - --bs-gray-700: #495057; - --bs-gray-800: #343a40; - --bs-gray-900: #212529; - --bs-primary: #4276A7; - --bs-secondary: #6c757d; - --bs-success: #6EAB63; - --bs-info: #6DA3B7; - --bs-warning: #D5804C; - --bs-danger: #C54C45; - --bs-light: #F5FAFF; - --bs-dark: #30475D; - --bs-primary-rgb: 66, 118, 167; - --bs-secondary-rgb: 108, 117, 125; - --bs-success-rgb: 110, 171, 99; - --bs-info-rgb: 109, 163, 183; - --bs-warning-rgb: 213, 128, 76; - --bs-danger-rgb: 197, 76, 69; - --bs-light-rgb: 245, 250, 255; - --bs-dark-rgb: 48, 71, 93; - --bs-white-rgb: 255, 255, 255; - --bs-black-rgb: 0, 0, 0; - --bs-body-color-rgb: 0, 48, 94; - --bs-body-bg-rgb: 255, 255, 255; - --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); - --bs-root-font-size: 15px; - --bs-body-font-family: Source Sans Pro, sans-serif; - --bs-body-font-size: 1rem; - --bs-body-font-weight: 400; - --bs-body-line-height: 1.5; - --bs-body-color: #00305E; - --bs-body-bg: #fff; +.bi-0-circle-fill::before { + content: "\f83f"; } -*, -*::before, -*::after { - box-sizing: border-box; +.bi-0-circle::before { + content: "\f840"; } -:root { - font-size: var(--bs-root-font-size); +.bi-0-square-fill::before { + content: "\f841"; } -@media (prefers-reduced-motion: no-preference) { - :root { - scroll-behavior: smooth; - } + +.bi-0-square::before { + content: "\f842"; } -body { - margin: 0; - font-family: var(--bs-body-font-family); - font-size: var(--bs-body-font-size); - font-weight: var(--bs-body-font-weight); - line-height: var(--bs-body-line-height); - color: var(--bs-body-color); - text-align: var(--bs-body-text-align); - background-color: var(--bs-body-bg); - -webkit-text-size-adjust: 100%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +.bi-rocket-fill::before { + content: "\f843"; } -hr { - margin: 1rem 0; - color: inherit; - background-color: currentColor; - border: 0; - opacity: 0.25; +.bi-rocket-takeoff-fill::before { + content: "\f844"; } -hr:not([size]) { - height: 1px; +.bi-rocket-takeoff::before { + content: "\f845"; } -h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { - margin-top: 0; - margin-bottom: 0.5rem; - font-weight: 500; - line-height: 1.2; +.bi-rocket::before { + content: "\f846"; } -h1, .h1 { - font-size: calc(1.375rem + 1.5vw); +.bi-stripe::before { + content: "\f847"; } -@media (min-width: 1200px) { - h1, .h1 { - font-size: 2.5rem; - } + +.bi-subscript::before { + content: "\f848"; } -h2, .h2 { - font-size: calc(1.325rem + 0.9vw); +.bi-superscript::before { + content: "\f849"; } -@media (min-width: 1200px) { - h2, .h2 { - font-size: 2rem; - } + +.bi-trello::before { + content: "\f84a"; } -h3, .h3 { - font-size: calc(1.3rem + 0.6vw); +.bi-envelope-at-fill::before { + content: "\f84b"; } -@media (min-width: 1200px) { - h3, .h3 { - font-size: 1.75rem; - } + +.bi-envelope-at::before { + content: "\f84c"; } -h4, .h4 { - font-size: calc(1.275rem + 0.3vw); +.bi-regex::before { + content: "\f84d"; } -@media (min-width: 1200px) { - h4, .h4 { - font-size: 1.5rem; - } + +.bi-text-wrap::before { + content: "\f84e"; } -h5, .h5 { - font-size: 1.25rem; +.bi-sign-dead-end-fill::before { + content: "\f84f"; } -h6, .h6 { - font-size: 1rem; +.bi-sign-dead-end::before { + content: "\f850"; } -p { - margin-top: 0; - margin-bottom: 1rem; +.bi-sign-do-not-enter-fill::before { + content: "\f851"; } -abbr[title], -abbr[data-bs-original-title] { - text-decoration: underline dotted; - cursor: help; - text-decoration-skip-ink: none; +.bi-sign-do-not-enter::before { + content: "\f852"; } -address { - margin-bottom: 1rem; - font-style: normal; - line-height: inherit; +.bi-sign-intersection-fill::before { + content: "\f853"; } -ol, -ul { - padding-left: 2rem; +.bi-sign-intersection-side-fill::before { + content: "\f854"; } -ol, -ul, -dl { - margin-top: 0; - margin-bottom: 1rem; +.bi-sign-intersection-side::before { + content: "\f855"; } -ol ol, -ul ul, -ol ul, -ul ol { - margin-bottom: 0; +.bi-sign-intersection-t-fill::before { + content: "\f856"; } -dt { - font-weight: 700; +.bi-sign-intersection-t::before { + content: "\f857"; } -dd { - margin-bottom: 0.5rem; - margin-left: 0; +.bi-sign-intersection-y-fill::before { + content: "\f858"; } -blockquote { - margin: 0 0 1rem; +.bi-sign-intersection-y::before { + content: "\f859"; } -b, -strong { - font-weight: bolder; +.bi-sign-intersection::before { + content: "\f85a"; } -small, .small { - font-size: 0.875em; +.bi-sign-merge-left-fill::before { + content: "\f85b"; } -mark, .mark { - padding: 0.2em; - background-color: #fcf8e3; +.bi-sign-merge-left::before { + content: "\f85c"; } -sub, -sup { - position: relative; - font-size: 0.75em; - line-height: 0; - vertical-align: baseline; +.bi-sign-merge-right-fill::before { + content: "\f85d"; } -sub { - bottom: -0.25em; +.bi-sign-merge-right::before { + content: "\f85e"; } -sup { - top: -0.5em; +.bi-sign-no-left-turn-fill::before { + content: "\f85f"; } -a { - color: #4276A7; - text-decoration: underline; +.bi-sign-no-left-turn::before { + content: "\f860"; } -a:hover { - color: #355e86; + +.bi-sign-no-parking-fill::before { + content: "\f861"; } -a:not([href]):not([class]), a:not([href]):not([class]):hover { - color: inherit; - text-decoration: none; +.bi-sign-no-parking::before { + content: "\f862"; } -pre, -code, -kbd, -samp { - font-family: var(--bs-font-monospace); - font-size: 1em; - direction: ltr /* rtl:ignore */; - unicode-bidi: bidi-override; +.bi-sign-no-right-turn-fill::before { + content: "\f863"; } -pre { - display: block; - margin-top: 0; - margin-bottom: 1rem; - overflow: auto; - font-size: 0.875em; +.bi-sign-no-right-turn::before { + content: "\f864"; } -pre code { - font-size: inherit; - color: inherit; - word-break: normal; + +.bi-sign-railroad-fill::before { + content: "\f865"; } -code { - font-size: 0.875em; - color: #d63384; - word-wrap: break-word; +.bi-sign-railroad::before { + content: "\f866"; } -a > code { - color: inherit; + +.bi-building-add::before { + content: "\f867"; } -kbd { - padding: 0.2rem 0.4rem; - font-size: 0.875em; - color: #fff; - background-color: #212529; - border-radius: 0.2rem; +.bi-building-check::before { + content: "\f868"; } -kbd kbd { - padding: 0; - font-size: 1em; - font-weight: 700; + +.bi-building-dash::before { + content: "\f869"; } -figure { - margin: 0 0 1rem; +.bi-building-down::before { + content: "\f86a"; } -img, -svg { - vertical-align: middle; +.bi-building-exclamation::before { + content: "\f86b"; } -table { - caption-side: bottom; - border-collapse: collapse; +.bi-building-fill-add::before { + content: "\f86c"; } -caption { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - color: #6c757d; - text-align: left; +.bi-building-fill-check::before { + content: "\f86d"; } -th { - text-align: inherit; - text-align: -webkit-match-parent; +.bi-building-fill-dash::before { + content: "\f86e"; } -thead, -tbody, -tfoot, -tr, -td, -th { - border-color: inherit; - border-style: solid; - border-width: 0; +.bi-building-fill-down::before { + content: "\f86f"; } -label { - display: inline-block; +.bi-building-fill-exclamation::before { + content: "\f870"; } -button { - border-radius: 0; +.bi-building-fill-gear::before { + content: "\f871"; } -button:focus:not(:focus-visible) { - outline: 0; +.bi-building-fill-lock::before { + content: "\f872"; } -input, -button, -select, -optgroup, -textarea { - margin: 0; - font-family: inherit; - font-size: inherit; - line-height: inherit; +.bi-building-fill-slash::before { + content: "\f873"; } -button, -select { - text-transform: none; +.bi-building-fill-up::before { + content: "\f874"; } -[role=button] { - cursor: pointer; +.bi-building-fill-x::before { + content: "\f875"; } -select { - word-wrap: normal; +.bi-building-fill::before { + content: "\f876"; } -select:disabled { - opacity: 1; + +.bi-building-gear::before { + content: "\f877"; } -[list]::-webkit-calendar-picker-indicator { - display: none; +.bi-building-lock::before { + content: "\f878"; } -button, -[type=button], -[type=reset], -[type=submit] { - -webkit-appearance: button; +.bi-building-slash::before { + content: "\f879"; } -button:not(:disabled), -[type=button]:not(:disabled), -[type=reset]:not(:disabled), -[type=submit]:not(:disabled) { - cursor: pointer; + +.bi-building-up::before { + content: "\f87a"; } -::-moz-focus-inner { - padding: 0; - border-style: none; +.bi-building-x::before { + content: "\f87b"; } -textarea { - resize: vertical; +.bi-buildings-fill::before { + content: "\f87c"; } -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; +.bi-buildings::before { + content: "\f87d"; } -legend { - float: left; - width: 100%; - padding: 0; - margin-bottom: 0.5rem; - font-size: calc(1.275rem + 0.3vw); - line-height: inherit; +.bi-bus-front-fill::before { + content: "\f87e"; } -@media (min-width: 1200px) { - legend { - font-size: 1.5rem; - } + +.bi-bus-front::before { + content: "\f87f"; } -legend + * { - clear: left; + +.bi-ev-front-fill::before { + content: "\f880"; } -::-webkit-datetime-edit-fields-wrapper, -::-webkit-datetime-edit-text, -::-webkit-datetime-edit-minute, -::-webkit-datetime-edit-hour-field, -::-webkit-datetime-edit-day-field, -::-webkit-datetime-edit-month-field, -::-webkit-datetime-edit-year-field { - padding: 0; +.bi-ev-front::before { + content: "\f881"; } -::-webkit-inner-spin-button { - height: auto; +.bi-globe-americas::before { + content: "\f882"; } -[type=search] { - outline-offset: -2px; - -webkit-appearance: textfield; +.bi-globe-asia-australia::before { + content: "\f883"; } -/* rtl:raw: -[type="tel"], -[type="url"], -[type="email"], -[type="number"] { - direction: ltr; +.bi-globe-central-south-asia::before { + content: "\f884"; } -*/ -::-webkit-search-decoration { - -webkit-appearance: none; + +.bi-globe-europe-africa::before { + content: "\f885"; } -::-webkit-color-swatch-wrapper { - padding: 0; +.bi-house-add-fill::before { + content: "\f886"; } -::file-selector-button { - font: inherit; +.bi-house-add::before { + content: "\f887"; } -::-webkit-file-upload-button { - font: inherit; - -webkit-appearance: button; +.bi-house-check-fill::before { + content: "\f888"; } -output { - display: inline-block; +.bi-house-check::before { + content: "\f889"; } -iframe { - border: 0; +.bi-house-dash-fill::before { + content: "\f88a"; } -summary { - display: list-item; - cursor: pointer; +.bi-house-dash::before { + content: "\f88b"; } -progress { - vertical-align: baseline; +.bi-house-down-fill::before { + content: "\f88c"; } -[hidden] { - display: none !important; +.bi-house-down::before { + content: "\f88d"; } -.lead { - font-size: 1.25rem; - font-weight: 300; +.bi-house-exclamation-fill::before { + content: "\f88e"; } -.display-1 { - font-size: calc(1.625rem + 4.5vw); - font-weight: 300; - line-height: 1.2; +.bi-house-exclamation::before { + content: "\f88f"; } -@media (min-width: 1200px) { - .display-1 { - font-size: 5rem; - } + +.bi-house-gear-fill::before { + content: "\f890"; } -.display-2 { - font-size: calc(1.575rem + 3.9vw); - font-weight: 300; - line-height: 1.2; +.bi-house-gear::before { + content: "\f891"; } -@media (min-width: 1200px) { - .display-2 { - font-size: 4.5rem; - } + +.bi-house-lock-fill::before { + content: "\f892"; } -.display-3 { - font-size: calc(1.525rem + 3.3vw); - font-weight: 300; - line-height: 1.2; +.bi-house-lock::before { + content: "\f893"; } -@media (min-width: 1200px) { - .display-3 { - font-size: 4rem; - } + +.bi-house-slash-fill::before { + content: "\f894"; } -.display-4 { - font-size: calc(1.475rem + 2.7vw); - font-weight: 300; - line-height: 1.2; +.bi-house-slash::before { + content: "\f895"; } -@media (min-width: 1200px) { - .display-4 { - font-size: 3.5rem; - } + +.bi-house-up-fill::before { + content: "\f896"; } -.display-5 { - font-size: calc(1.425rem + 2.1vw); - font-weight: 300; - line-height: 1.2; +.bi-house-up::before { + content: "\f897"; } -@media (min-width: 1200px) { - .display-5 { - font-size: 3rem; - } + +.bi-house-x-fill::before { + content: "\f898"; } -.display-6 { - font-size: calc(1.375rem + 1.5vw); - font-weight: 300; - line-height: 1.2; +.bi-house-x::before { + content: "\f899"; } -@media (min-width: 1200px) { - .display-6 { - font-size: 2.5rem; - } + +.bi-person-add::before { + content: "\f89a"; } -.list-unstyled { - padding-left: 0; - list-style: none; +.bi-person-down::before { + content: "\f89b"; } -.list-inline { - padding-left: 0; - list-style: none; +.bi-person-exclamation::before { + content: "\f89c"; } -.list-inline-item { - display: inline-block; +.bi-person-fill-add::before { + content: "\f89d"; } -.list-inline-item:not(:last-child) { - margin-right: 0.5rem; + +.bi-person-fill-check::before { + content: "\f89e"; } -.initialism { - font-size: 0.875em; - text-transform: uppercase; +.bi-person-fill-dash::before { + content: "\f89f"; } -.blockquote { - margin-bottom: 1rem; - font-size: 1.25rem; +.bi-person-fill-down::before { + content: "\f8a0"; } -.blockquote > :last-child { - margin-bottom: 0; + +.bi-person-fill-exclamation::before { + content: "\f8a1"; } -.blockquote-footer { - margin-top: -1rem; - margin-bottom: 1rem; - font-size: 0.875em; - color: #6c757d; +.bi-person-fill-gear::before { + content: "\f8a2"; } -.blockquote-footer::before { - content: "— "; + +.bi-person-fill-lock::before { + content: "\f8a3"; } -.img-fluid { - max-width: 100%; - height: auto; +.bi-person-fill-slash::before { + content: "\f8a4"; } -.img-thumbnail { - padding: 0.25rem; - background-color: #fff; - border: 1px solid #dee2e6; - border-radius: 0.25rem; - max-width: 100%; - height: auto; +.bi-person-fill-up::before { + content: "\f8a5"; } -.figure { - display: inline-block; +.bi-person-fill-x::before { + content: "\f8a6"; } -.figure-img { - margin-bottom: 0.5rem; - line-height: 1; +.bi-person-gear::before { + content: "\f8a7"; } -.figure-caption { - font-size: 0.875em; - color: #6c757d; +.bi-person-lock::before { + content: "\f8a8"; } -.container, -.container-fluid, -.container-xxl, -.container-xl, -.container-lg, -.container-md, -.container-sm { - width: 100%; - padding-right: var(--bs-gutter-x, 0.75rem); - padding-left: var(--bs-gutter-x, 0.75rem); - margin-right: auto; - margin-left: auto; +.bi-person-slash::before { + content: "\f8a9"; } -@media (min-width: 576px) { - .container-sm, .container { - max-width: 540px; - } +.bi-person-up::before { + content: "\f8aa"; } -@media (min-width: 768px) { - .container-md, .container-sm, .container { - max-width: 720px; - } + +.bi-scooter::before { + content: "\f8ab"; } -@media (min-width: 992px) { - .container-lg, .container-md, .container-sm, .container { - max-width: 960px; - } + +.bi-taxi-front-fill::before { + content: "\f8ac"; } -@media (min-width: 1200px) { - .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1140px; - } + +.bi-taxi-front::before { + content: "\f8ad"; } -@media (min-width: 1400px) { - .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { - max-width: 1320px; - } + +.bi-amd::before { + content: "\f8ae"; } -.row { - --bs-gutter-x: 1.5rem; - --bs-gutter-y: 0; - display: flex; - flex-wrap: wrap; - margin-top: calc(-1 * var(--bs-gutter-y)); - margin-right: calc(-0.5 * var(--bs-gutter-x)); - margin-left: calc(-0.5 * var(--bs-gutter-x)); + +.bi-database-add::before { + content: "\f8af"; } -.row > * { - flex-shrink: 0; - width: 100%; - max-width: 100%; - padding-right: calc(var(--bs-gutter-x) * 0.5); - padding-left: calc(var(--bs-gutter-x) * 0.5); - margin-top: var(--bs-gutter-y); + +.bi-database-check::before { + content: "\f8b0"; } -.col { - flex: 1 0 0%; +.bi-database-dash::before { + content: "\f8b1"; } -.row-cols-auto > * { - flex: 0 0 auto; - width: auto; +.bi-database-down::before { + content: "\f8b2"; } -.row-cols-1 > * { - flex: 0 0 auto; - width: 100%; +.bi-database-exclamation::before { + content: "\f8b3"; } -.row-cols-2 > * { - flex: 0 0 auto; - width: 50%; +.bi-database-fill-add::before { + content: "\f8b4"; } -.row-cols-3 > * { - flex: 0 0 auto; - width: 33.3333333333%; +.bi-database-fill-check::before { + content: "\f8b5"; } -.row-cols-4 > * { - flex: 0 0 auto; - width: 25%; +.bi-database-fill-dash::before { + content: "\f8b6"; } -.row-cols-5 > * { - flex: 0 0 auto; - width: 20%; +.bi-database-fill-down::before { + content: "\f8b7"; } -.row-cols-6 > * { - flex: 0 0 auto; - width: 16.6666666667%; +.bi-database-fill-exclamation::before { + content: "\f8b8"; } -.col-auto { - flex: 0 0 auto; - width: auto; +.bi-database-fill-gear::before { + content: "\f8b9"; } -.col-1 { - flex: 0 0 auto; - width: 8.33333333%; +.bi-database-fill-lock::before { + content: "\f8ba"; } -.col-2 { - flex: 0 0 auto; - width: 16.66666667%; +.bi-database-fill-slash::before { + content: "\f8bb"; } -.col-3 { - flex: 0 0 auto; - width: 25%; +.bi-database-fill-up::before { + content: "\f8bc"; } -.col-4 { - flex: 0 0 auto; - width: 33.33333333%; +.bi-database-fill-x::before { + content: "\f8bd"; } -.col-5 { - flex: 0 0 auto; - width: 41.66666667%; +.bi-database-fill::before { + content: "\f8be"; } -.col-6 { - flex: 0 0 auto; - width: 50%; +.bi-database-gear::before { + content: "\f8bf"; } -.col-7 { - flex: 0 0 auto; - width: 58.33333333%; +.bi-database-lock::before { + content: "\f8c0"; } -.col-8 { - flex: 0 0 auto; - width: 66.66666667%; +.bi-database-slash::before { + content: "\f8c1"; } -.col-9 { - flex: 0 0 auto; - width: 75%; +.bi-database-up::before { + content: "\f8c2"; } -.col-10 { - flex: 0 0 auto; - width: 83.33333333%; +.bi-database-x::before { + content: "\f8c3"; } -.col-11 { - flex: 0 0 auto; - width: 91.66666667%; +.bi-database::before { + content: "\f8c4"; } -.col-12 { - flex: 0 0 auto; - width: 100%; +.bi-houses-fill::before { + content: "\f8c5"; } -.offset-1 { - margin-left: 8.33333333%; +.bi-houses::before { + content: "\f8c6"; } -.offset-2 { - margin-left: 16.66666667%; +.bi-nvidia::before { + content: "\f8c7"; } -.offset-3 { - margin-left: 25%; +.bi-person-vcard-fill::before { + content: "\f8c8"; } -.offset-4 { - margin-left: 33.33333333%; +.bi-person-vcard::before { + content: "\f8c9"; } -.offset-5 { - margin-left: 41.66666667%; +.bi-sina-weibo::before { + content: "\f8ca"; } -.offset-6 { - margin-left: 50%; +.bi-tencent-qq::before { + content: "\f8cb"; } -.offset-7 { - margin-left: 58.33333333%; +.bi-wikipedia::before { + content: "\f8cc"; } -.offset-8 { - margin-left: 66.66666667%; +.bi-alphabet-uppercase::before { + content: "\f2a5"; } -.offset-9 { - margin-left: 75%; +.bi-alphabet::before { + content: "\f68a"; } -.offset-10 { - margin-left: 83.33333333%; +.bi-amazon::before { + content: "\f68d"; } -.offset-11 { - margin-left: 91.66666667%; +.bi-arrows-collapse-vertical::before { + content: "\f690"; } -.g-0, -.gx-0 { - --bs-gutter-x: 0; +.bi-arrows-expand-vertical::before { + content: "\f695"; } -.g-0, -.gy-0 { - --bs-gutter-y: 0; +.bi-arrows-vertical::before { + content: "\f698"; } -.g-1, -.gx-1 { - --bs-gutter-x: 0.25rem; +.bi-arrows::before { + content: "\f6a2"; } -.g-1, -.gy-1 { - --bs-gutter-y: 0.25rem; +.bi-ban-fill::before { + content: "\f6a3"; } -.g-2, -.gx-2 { - --bs-gutter-x: 0.5rem; +.bi-ban::before { + content: "\f6b6"; } -.g-2, -.gy-2 { - --bs-gutter-y: 0.5rem; +.bi-bing::before { + content: "\f6c2"; } -.g-3, -.gx-3 { - --bs-gutter-x: 1rem; +.bi-cake::before { + content: "\f6e0"; } -.g-3, -.gy-3 { - --bs-gutter-y: 1rem; +.bi-cake2::before { + content: "\f6ed"; } -.g-4, -.gx-4 { - --bs-gutter-x: 1.5rem; +.bi-cookie::before { + content: "\f6ee"; } -.g-4, -.gy-4 { - --bs-gutter-y: 1.5rem; +.bi-copy::before { + content: "\f759"; } -.g-5, -.gx-5 { - --bs-gutter-x: 3rem; +.bi-crosshair::before { + content: "\f769"; } -.g-5, -.gy-5 { - --bs-gutter-y: 3rem; +.bi-crosshair2::before { + content: "\f794"; } -@media (min-width: 576px) { - .col-sm { - flex: 1 0 0%; - } - .row-cols-sm-auto > * { - flex: 0 0 auto; - width: auto; - } - .row-cols-sm-1 > * { - flex: 0 0 auto; - width: 100%; - } - .row-cols-sm-2 > * { - flex: 0 0 auto; - width: 50%; - } - .row-cols-sm-3 > * { - flex: 0 0 auto; - width: 33.3333333333%; - } - .row-cols-sm-4 > * { - flex: 0 0 auto; - width: 25%; - } - .row-cols-sm-5 > * { - flex: 0 0 auto; - width: 20%; - } - .row-cols-sm-6 > * { - flex: 0 0 auto; - width: 16.6666666667%; - } - .col-sm-auto { - flex: 0 0 auto; - width: auto; - } - .col-sm-1 { - flex: 0 0 auto; - width: 8.33333333%; - } - .col-sm-2 { - flex: 0 0 auto; - width: 16.66666667%; - } +.bi-emoji-astonished-fill::before { + content: "\f795"; +} + +.bi-emoji-astonished::before { + content: "\f79a"; +} + +.bi-emoji-grimace-fill::before { + content: "\f79b"; +} + +.bi-emoji-grimace::before { + content: "\f7a0"; +} + +.bi-emoji-grin-fill::before { + content: "\f7a1"; +} + +.bi-emoji-grin::before { + content: "\f7a6"; +} + +.bi-emoji-surprise-fill::before { + content: "\f7a7"; +} + +.bi-emoji-surprise::before { + content: "\f7ac"; +} + +.bi-emoji-tear-fill::before { + content: "\f7ad"; +} + +.bi-emoji-tear::before { + content: "\f7b2"; +} + +.bi-envelope-arrow-down-fill::before { + content: "\f7b3"; +} + +.bi-envelope-arrow-down::before { + content: "\f7b8"; +} + +.bi-envelope-arrow-up-fill::before { + content: "\f7b9"; +} + +.bi-envelope-arrow-up::before { + content: "\f7be"; +} + +.bi-feather::before { + content: "\f7bf"; +} + +.bi-feather2::before { + content: "\f7c4"; +} + +.bi-floppy-fill::before { + content: "\f7c5"; +} + +.bi-floppy::before { + content: "\f7d8"; +} + +.bi-floppy2-fill::before { + content: "\f7d9"; +} + +.bi-floppy2::before { + content: "\f7e4"; +} + +.bi-gitlab::before { + content: "\f7e5"; +} + +.bi-highlighter::before { + content: "\f7f8"; +} + +.bi-marker-tip::before { + content: "\f802"; +} + +.bi-nvme-fill::before { + content: "\f803"; +} + +.bi-nvme::before { + content: "\f80c"; +} + +.bi-opencollective::before { + content: "\f80d"; +} + +.bi-pci-card-network::before { + content: "\f8cd"; +} + +.bi-pci-card-sound::before { + content: "\f8ce"; +} + +.bi-radar::before { + content: "\f8cf"; +} + +.bi-send-arrow-down-fill::before { + content: "\f8d0"; +} + +.bi-send-arrow-down::before { + content: "\f8d1"; +} + +.bi-send-arrow-up-fill::before { + content: "\f8d2"; +} + +.bi-send-arrow-up::before { + content: "\f8d3"; +} + +.bi-sim-slash-fill::before { + content: "\f8d4"; +} + +.bi-sim-slash::before { + content: "\f8d5"; +} + +.bi-sourceforge::before { + content: "\f8d6"; +} + +.bi-substack::before { + content: "\f8d7"; +} + +.bi-threads-fill::before { + content: "\f8d8"; +} + +.bi-threads::before { + content: "\f8d9"; +} + +.bi-transparency::before { + content: "\f8da"; +} + +.bi-twitter-x::before { + content: "\f8db"; +} + +.bi-type-h4::before { + content: "\f8dc"; +} + +.bi-type-h5::before { + content: "\f8dd"; +} + +.bi-type-h6::before { + content: "\f8de"; +} + +.bi-backpack-fill::before { + content: "\f8df"; +} + +.bi-backpack::before { + content: "\f8e0"; +} + +.bi-backpack2-fill::before { + content: "\f8e1"; +} + +.bi-backpack2::before { + content: "\f8e2"; +} + +.bi-backpack3-fill::before { + content: "\f8e3"; +} + +.bi-backpack3::before { + content: "\f8e4"; +} + +.bi-backpack4-fill::before { + content: "\f8e5"; +} + +.bi-backpack4::before { + content: "\f8e6"; +} + +.bi-brilliance::before { + content: "\f8e7"; +} + +.bi-cake-fill::before { + content: "\f8e8"; +} + +.bi-cake2-fill::before { + content: "\f8e9"; +} + +.bi-duffle-fill::before { + content: "\f8ea"; +} + +.bi-duffle::before { + content: "\f8eb"; +} + +.bi-exposure::before { + content: "\f8ec"; +} + +.bi-gender-neuter::before { + content: "\f8ed"; +} + +.bi-highlights::before { + content: "\f8ee"; +} + +.bi-luggage-fill::before { + content: "\f8ef"; +} + +.bi-luggage::before { + content: "\f8f0"; +} + +.bi-mailbox-flag::before { + content: "\f8f1"; +} + +.bi-mailbox2-flag::before { + content: "\f8f2"; +} + +.bi-noise-reduction::before { + content: "\f8f3"; +} + +.bi-passport-fill::before { + content: "\f8f4"; +} + +.bi-passport::before { + content: "\f8f5"; +} + +.bi-person-arms-up::before { + content: "\f8f6"; +} + +.bi-person-raised-hand::before { + content: "\f8f7"; +} + +.bi-person-standing-dress::before { + content: "\f8f8"; +} + +.bi-person-standing::before { + content: "\f8f9"; +} + +.bi-person-walking::before { + content: "\f8fa"; +} + +.bi-person-wheelchair::before { + content: "\f8fb"; +} + +.bi-shadows::before { + content: "\f8fc"; +} + +.bi-suitcase-fill::before { + content: "\f8fd"; +} + +.bi-suitcase-lg-fill::before { + content: "\f8fe"; +} + +.bi-suitcase-lg::before { + content: "\f8ff"; +} + +.bi-suitcase::before { + content: "豈"; +} + +.bi-suitcase2-fill::before { + content: "更"; +} + +.bi-suitcase2::before { + content: "車"; +} + +.bi-vignette::before { + content: "賈"; +} + +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +:root, +[data-bs-theme=light] { + --bs-blue: #0d6efd; + --bs-indigo: #6610f2; + --bs-purple: #6f42c1; + --bs-pink: #d63384; + --bs-red: #dc3545; + --bs-orange: #fd7e14; + --bs-yellow: #ffc107; + --bs-green: #198754; + --bs-teal: #20c997; + --bs-cyan: #0dcaf0; + --bs-black: #000; + --bs-white: #fff; + --bs-gray: #6c757d; + --bs-gray-dark: #343a40; + --bs-accent: #9CEC5B; + --bs-cinereous: #837569; + --bs-verdigris: #50C5B7; + --bs-icterine: #F0F465; + --bs-mute: #6c757d; + --bs-gray-100: #f8f9fa; + --bs-gray-200: #e9ecef; + --bs-gray-300: #dee2e6; + --bs-gray-400: #ced4da; + --bs-gray-500: #adb5bd; + --bs-gray-600: #6c757d; + --bs-gray-700: #495057; + --bs-gray-800: #343a40; + --bs-gray-900: #212529; + --bs-primary: #8c68cd; + --bs-secondary: #3c8cd6; + --bs-success: #198754; + --bs-info: #0dcaf0; + --bs-warning: #ffc107; + --bs-danger: #dc3545; + --bs-light: #f8f9fa; + --bs-dark: #212529; + --bs-accent: #9CEC5B; + --bs-cinereous: #837569; + --bs-verdigris: #50C5B7; + --bs-icterine: #F0F465; + --bs-mute: #6c757d; + --bs-primary-rgb: 140, 104, 205; + --bs-secondary-rgb: 60, 140, 214; + --bs-success-rgb: 25, 135, 84; + --bs-info-rgb: 13, 202, 240; + --bs-warning-rgb: 255, 193, 7; + --bs-danger-rgb: 220, 53, 69; + --bs-light-rgb: 248, 249, 250; + --bs-dark-rgb: 33, 37, 41; + --bs-accent-rgb: 156, 236, 91; + --bs-cinereous-rgb: 131, 117, 105; + --bs-verdigris-rgb: 80, 197, 183; + --bs-icterine-rgb: 240, 244, 101; + --bs-mute-rgb: 108, 117, 125; + --bs-primary-text-emphasis: #382a52; + --bs-secondary-text-emphasis: #183856; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #7d59bd; + --bs-secondary-bg-subtle: #d8e8f7; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-accent-bg-subtle: #9CEC5B; + --bs-cinereous-bg-subtle: #837569; + --bs-verdigris-bg-subtle: #50C5B7; + --bs-icterine-bg-subtle: #F0F465; + --bs-mute-bg-subtle: #6c757d; + --bs-primary-border-subtle: #714db1; + --bs-secondary-border-subtle: #b1d1ef; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; + --bs-accent-border-subtle: #9CEC5B; + --bs-cinereous-border-subtle: #837569; + --bs-verdigris-border-subtle: #50C5B7; + --bs-icterine-border-subtle: #F0F465; + --bs-mute-border-subtle: #6c757d; + --bs-white-rgb: 255, 255, 255; + --bs-black-rgb: 0, 0, 0; + --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); + --bs-root-font-size: 15px; + --bs-body-font-family: "Inconsolata", monospace; + --bs-body-font-size: 1rem; + --bs-body-font-weight: 400; + --bs-body-line-height: 1.5; + --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; + --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #8c68cd; + --bs-link-color-rgb: 140, 104, 205; + --bs-link-decoration: underline; + --bs-link-hover-color: #7053a4; + --bs-link-hover-color-rgb: 112, 83, 164; + --bs-code-color: #d63384; + --bs-highlight-color: #212529; + --bs-highlight-bg: #fff3cd; + --bs-border-width: 1px; + --bs-border-style: solid; + --bs-border-color: #dee2e6; + --bs-border-color-translucent: rgba(0, 0, 0, 0.175); + --bs-border-radius: 0.375rem; + --bs-border-radius-sm: 0.25rem; + --bs-border-radius-lg: 0.5rem; + --bs-border-radius-xl: 1rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); + --bs-border-radius-pill: 50rem; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(140, 104, 205, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; +} + +[data-bs-theme=dark] { + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #baa4e1; + --bs-secondary-text-emphasis: #8abae6; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-accent-text-emphasis: #9CEC5B; + --bs-cinereous-text-emphasis: #837569; + --bs-verdigris-text-emphasis: #50C5B7; + --bs-icterine-text-emphasis: #F0F465; + --bs-mute-text-emphasis: #6c757d; + --bs-primary-bg-subtle: #7d59bd; + --bs-secondary-bg-subtle: #0c1c2b; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-accent-bg-subtle: #9CEC5B; + --bs-cinereous-bg-subtle: #837569; + --bs-verdigris-bg-subtle: #50C5B7; + --bs-icterine-bg-subtle: #F0F465; + --bs-mute-bg-subtle: #6c757d; + --bs-primary-border-subtle: #714db1; + --bs-secondary-border-subtle: #245480; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-accent-border-subtle: #9CEC5B; + --bs-cinereous-border-subtle: #837569; + --bs-verdigris-border-subtle: #50C5B7; + --bs-icterine-border-subtle: #F0F465; + --bs-mute-border-subtle: #6c757d; + --bs-heading-color: inherit; + --bs-link-color: #8c68cd; + --bs-link-hover-color: #c5b3e6; + --bs-link-color-rgb: 140, 104, 205; + --bs-link-hover-color-rgb: 197, 179, 230; + --bs-code-color: #e685b5; + --bs-highlight-color: #dee2e6; + --bs-highlight-bg: #664d03; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +:root { + font-size: var(--bs-root-font-size); +} +@media (prefers-reduced-motion: no-preference) { + :root { + scroll-behavior: smooth; + } +} + +body { + margin: 0; + font-family: var(--bs-body-font-family); + font-size: var(--bs-body-font-size); + font-weight: var(--bs-body-font-weight); + line-height: var(--bs-body-line-height); + color: var(--bs-body-color); + text-align: var(--bs-body-text-align); + background-color: var(--bs-body-bg); + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +hr { + margin: 1rem 0; + color: inherit; + border: 0; + border-top: var(--bs-border-width) solid; + opacity: 0.25; +} + +h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { + margin-top: 0; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; + color: var(--bs-heading-color); +} + +h1, .h1 { + font-size: calc(1.375rem + 1.5vw); +} +@media (min-width: 1200px) { + h1, .h1 { + font-size: 2.5rem; + } +} + +h2, .h2 { + font-size: calc(1.325rem + 0.9vw); +} +@media (min-width: 1200px) { + h2, .h2 { + font-size: 2rem; + } +} + +h3, .h3 { + font-size: calc(1.3rem + 0.6vw); +} +@media (min-width: 1200px) { + h3, .h3 { + font-size: 1.75rem; + } +} + +h4, .h4 { + font-size: calc(1.275rem + 0.3vw); +} +@media (min-width: 1200px) { + h4, .h4 { + font-size: 1.5rem; + } +} + +h5, .h5 { + font-size: 1.25rem; +} + +h6, .h6 { + font-size: 1rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +abbr[title] { + text-decoration: underline dotted; + cursor: help; + text-decoration-skip-ink: none; +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; +} + +ol, +ul { + padding-left: 2rem; +} + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; +} + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; +} + +dt { + font-weight: 700; +} + +dd { + margin-bottom: 0.5rem; + margin-left: 0; +} + +blockquote { + margin: 0 0 1rem; +} + +b, +strong { + font-weight: bolder; +} + +small, .small { + font-size: 0.875em; +} + +mark, .mark { + padding: 0.1875em; + color: var(--bs-highlight-color); + background-color: var(--bs-highlight-bg); +} + +sub, +sup { + position: relative; + font-size: 0.75em; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +a { + color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); + text-decoration: underline; +} +a:hover { + --bs-link-color-rgb: var(--bs-link-hover-color-rgb); +} + +a:not([href]):not([class]), a:not([href]):not([class]):hover { + color: inherit; + text-decoration: none; +} + +pre, +code, +kbd, +samp { + font-family: var(--bs-font-monospace); + font-size: 1em; +} + +pre { + display: block; + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + font-size: 0.875em; +} +pre code { + font-size: inherit; + color: inherit; + word-break: normal; +} + +code { + font-size: 0.875em; + color: var(--bs-code-color); + word-wrap: break-word; +} +a > code { + color: inherit; +} + +kbd { + padding: 0.1875rem 0.375rem; + font-size: 0.875em; + color: var(--bs-body-bg); + background-color: var(--bs-body-color); + border-radius: 0.25rem; +} +kbd kbd { + padding: 0; + font-size: 1em; +} + +figure { + margin: 0 0 1rem; +} + +img, +svg { + vertical-align: middle; +} + +table { + caption-side: bottom; + border-collapse: collapse; +} + +caption { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-secondary-color); + text-align: left; +} + +th { + text-align: inherit; + text-align: -webkit-match-parent; +} + +thead, +tbody, +tfoot, +tr, +td, +th { + border-color: inherit; + border-style: solid; + border-width: 0; +} + +label { + display: inline-block; +} + +button { + border-radius: 0; +} + +button:focus:not(:focus-visible) { + outline: 0; +} + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +select { + text-transform: none; +} + +[role=button] { + cursor: pointer; +} + +select { + word-wrap: normal; +} +select:disabled { + opacity: 1; +} + +[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator { + display: none !important; +} + +button, +[type=button], +[type=reset], +[type=submit] { + -webkit-appearance: button; +} +button:not(:disabled), +[type=button]:not(:disabled), +[type=reset]:not(:disabled), +[type=submit]:not(:disabled) { + cursor: pointer; +} + +::-moz-focus-inner { + padding: 0; + border-style: none; +} + +textarea { + resize: vertical; +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + float: left; + width: 100%; + padding: 0; + margin-bottom: 0.5rem; + font-size: calc(1.275rem + 0.3vw); + line-height: inherit; +} +@media (min-width: 1200px) { + legend { + font-size: 1.5rem; + } +} +legend + * { + clear: left; +} + +::-webkit-datetime-edit-fields-wrapper, +::-webkit-datetime-edit-text, +::-webkit-datetime-edit-minute, +::-webkit-datetime-edit-hour-field, +::-webkit-datetime-edit-day-field, +::-webkit-datetime-edit-month-field, +::-webkit-datetime-edit-year-field { + padding: 0; +} + +::-webkit-inner-spin-button { + height: auto; +} + +[type=search] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +/* rtl:raw: +[type="tel"], +[type="url"], +[type="email"], +[type="number"] { + direction: ltr; +} +*/ +::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-color-swatch-wrapper { + padding: 0; +} + +::file-selector-button { + font: inherit; + -webkit-appearance: button; +} + +output { + display: inline-block; +} + +iframe { + border: 0; +} + +summary { + display: list-item; + cursor: pointer; +} + +progress { + vertical-align: baseline; +} + +[hidden] { + display: none !important; +} + +.lead { + font-size: 1.25rem; + font-weight: 300; +} + +.display-1 { + font-size: calc(1.625rem + 4.5vw); + font-weight: 300; + line-height: 1.2; +} +@media (min-width: 1200px) { + .display-1 { + font-size: 5rem; + } +} + +.display-2 { + font-size: calc(1.575rem + 3.9vw); + font-weight: 300; + line-height: 1.2; +} +@media (min-width: 1200px) { + .display-2 { + font-size: 4.5rem; + } +} + +.display-3 { + font-size: calc(1.525rem + 3.3vw); + font-weight: 300; + line-height: 1.2; +} +@media (min-width: 1200px) { + .display-3 { + font-size: 4rem; + } +} + +.display-4 { + font-size: calc(1.475rem + 2.7vw); + font-weight: 300; + line-height: 1.2; +} +@media (min-width: 1200px) { + .display-4 { + font-size: 3.5rem; + } +} + +.display-5 { + font-size: calc(1.425rem + 2.1vw); + font-weight: 300; + line-height: 1.2; +} +@media (min-width: 1200px) { + .display-5 { + font-size: 3rem; + } +} + +.display-6 { + font-size: calc(1.375rem + 1.5vw); + font-weight: 300; + line-height: 1.2; +} +@media (min-width: 1200px) { + .display-6 { + font-size: 2.5rem; + } +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline-item { + display: inline-block; +} +.list-inline-item:not(:last-child) { + margin-right: 0.5rem; +} + +.initialism { + font-size: 0.875em; + text-transform: uppercase; +} + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem; +} +.blockquote > :last-child { + margin-bottom: 0; +} + +.blockquote-footer { + margin-top: -1rem; + margin-bottom: 1rem; + font-size: 0.875em; + color: #6c757d; +} +.blockquote-footer::before { + content: "— "; +} + +.img-fluid { + max-width: 100%; + height: auto; +} + +.img-thumbnail { + padding: 0.25rem; + background-color: var(--bs-body-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + max-width: 100%; + height: auto; +} + +.figure { + display: inline-block; +} + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; +} + +.figure-caption { + font-size: 0.875em; + color: var(--bs-secondary-color); +} + +.container, +.container-fluid, +.container-xxl, +.container-xl, +.container-lg, +.container-md, +.container-sm { + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + width: 100%; + padding-right: calc(var(--bs-gutter-x) * 0.5); + padding-left: calc(var(--bs-gutter-x) * 0.5); + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container-sm, .container { + max-width: 540px; + } +} +@media (min-width: 768px) { + .container-md, .container-sm, .container { + max-width: 720px; + } +} +@media (min-width: 992px) { + .container-lg, .container-md, .container-sm, .container { + max-width: 960px; + } +} +@media (min-width: 1200px) { + .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1140px; + } +} +@media (min-width: 1400px) { + .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container { + max-width: 1320px; + } +} +:root { + --bs-breakpoint-xs: 0; + --bs-breakpoint-sm: 576px; + --bs-breakpoint-md: 768px; + --bs-breakpoint-lg: 992px; + --bs-breakpoint-xl: 1200px; + --bs-breakpoint-xxl: 1400px; +} + +.row { + --bs-gutter-x: 1.5rem; + --bs-gutter-y: 0; + display: flex; + flex-wrap: wrap; + margin-top: calc(-1 * var(--bs-gutter-y)); + margin-right: calc(-0.5 * var(--bs-gutter-x)); + margin-left: calc(-0.5 * var(--bs-gutter-x)); +} +.row > * { + flex-shrink: 0; + width: 100%; + max-width: 100%; + padding-right: calc(var(--bs-gutter-x) * 0.5); + padding-left: calc(var(--bs-gutter-x) * 0.5); + margin-top: var(--bs-gutter-y); +} + +.col { + flex: 1 0 0%; +} + +.row-cols-auto > * { + flex: 0 0 auto; + width: auto; +} + +.row-cols-1 > * { + flex: 0 0 auto; + width: 100%; +} + +.row-cols-2 > * { + flex: 0 0 auto; + width: 50%; +} + +.row-cols-3 > * { + flex: 0 0 auto; + width: 33.33333333%; +} + +.row-cols-4 > * { + flex: 0 0 auto; + width: 25%; +} + +.row-cols-5 > * { + flex: 0 0 auto; + width: 20%; +} + +.row-cols-6 > * { + flex: 0 0 auto; + width: 16.66666667%; +} + +.col-auto { + flex: 0 0 auto; + width: auto; +} + +.col-1 { + flex: 0 0 auto; + width: 8.33333333%; +} + +.col-2 { + flex: 0 0 auto; + width: 16.66666667%; +} + +.col-3 { + flex: 0 0 auto; + width: 25%; +} + +.col-4 { + flex: 0 0 auto; + width: 33.33333333%; +} + +.col-5 { + flex: 0 0 auto; + width: 41.66666667%; +} + +.col-6 { + flex: 0 0 auto; + width: 50%; +} + +.col-7 { + flex: 0 0 auto; + width: 58.33333333%; +} + +.col-8 { + flex: 0 0 auto; + width: 66.66666667%; +} + +.col-9 { + flex: 0 0 auto; + width: 75%; +} + +.col-10 { + flex: 0 0 auto; + width: 83.33333333%; +} + +.col-11 { + flex: 0 0 auto; + width: 91.66666667%; +} + +.col-12 { + flex: 0 0 auto; + width: 100%; +} + +.offset-1 { + margin-left: 8.33333333%; +} + +.offset-2 { + margin-left: 16.66666667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.33333333%; +} + +.offset-5 { + margin-left: 41.66666667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.33333333%; +} + +.offset-8 { + margin-left: 66.66666667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.33333333%; +} + +.offset-11 { + margin-left: 91.66666667%; +} + +.g-0, +.gx-0 { + --bs-gutter-x: 0; +} + +.g-0, +.gy-0 { + --bs-gutter-y: 0; +} + +.g-1, +.gx-1 { + --bs-gutter-x: 0.25rem; +} + +.g-1, +.gy-1 { + --bs-gutter-y: 0.25rem; +} + +.g-2, +.gx-2 { + --bs-gutter-x: 0.5rem; +} + +.g-2, +.gy-2 { + --bs-gutter-y: 0.5rem; +} + +.g-3, +.gx-3 { + --bs-gutter-x: 1rem; +} + +.g-3, +.gy-3 { + --bs-gutter-y: 1rem; +} + +.g-4, +.gx-4 { + --bs-gutter-x: 1.5rem; +} + +.g-4, +.gy-4 { + --bs-gutter-y: 1.5rem; +} + +.g-5, +.gx-5 { + --bs-gutter-x: 3rem; +} + +.g-5, +.gy-5 { + --bs-gutter-y: 3rem; +} + +@media (min-width: 576px) { + .col-sm { + flex: 1 0 0%; + } + .row-cols-sm-auto > * { + flex: 0 0 auto; + width: auto; + } + .row-cols-sm-1 > * { + flex: 0 0 auto; + width: 100%; + } + .row-cols-sm-2 > * { + flex: 0 0 auto; + width: 50%; + } + .row-cols-sm-3 > * { + flex: 0 0 auto; + width: 33.33333333%; + } + .row-cols-sm-4 > * { + flex: 0 0 auto; + width: 25%; + } + .row-cols-sm-5 > * { + flex: 0 0 auto; + width: 20%; + } + .row-cols-sm-6 > * { + flex: 0 0 auto; + width: 16.66666667%; + } + .col-sm-auto { + flex: 0 0 auto; + width: auto; + } + .col-sm-1 { + flex: 0 0 auto; + width: 8.33333333%; + } + .col-sm-2 { + flex: 0 0 auto; + width: 16.66666667%; + } .col-sm-3 { flex: 0 0 auto; width: 25%; @@ -8508,7 +9461,7 @@ progress { } .row-cols-md-3 > * { flex: 0 0 auto; - width: 33.3333333333%; + width: 33.33333333%; } .row-cols-md-4 > * { flex: 0 0 auto; @@ -8520,7 +9473,7 @@ progress { } .row-cols-md-6 > * { flex: 0 0 auto; - width: 16.6666666667%; + width: 16.66666667%; } .col-md-auto { flex: 0 0 auto; @@ -8677,7 +9630,7 @@ progress { } .row-cols-lg-3 > * { flex: 0 0 auto; - width: 33.3333333333%; + width: 33.33333333%; } .row-cols-lg-4 > * { flex: 0 0 auto; @@ -8689,7 +9642,7 @@ progress { } .row-cols-lg-6 > * { flex: 0 0 auto; - width: 16.6666666667%; + width: 16.66666667%; } .col-lg-auto { flex: 0 0 auto; @@ -8846,7 +9799,7 @@ progress { } .row-cols-xl-3 > * { flex: 0 0 auto; - width: 33.3333333333%; + width: 33.33333333%; } .row-cols-xl-4 > * { flex: 0 0 auto; @@ -8858,7 +9811,7 @@ progress { } .row-cols-xl-6 > * { flex: 0 0 auto; - width: 16.6666666667%; + width: 16.66666667%; } .col-xl-auto { flex: 0 0 auto; @@ -9015,7 +9968,7 @@ progress { } .row-cols-xxl-3 > * { flex: 0 0 auto; - width: 33.3333333333%; + width: 33.33333333%; } .row-cols-xxl-4 > * { flex: 0 0 auto; @@ -9027,7 +9980,7 @@ progress { } .row-cols-xxl-6 > * { flex: 0 0 auto; - width: 16.6666666667%; + width: 16.66666667%; } .col-xxl-auto { flex: 0 0 auto; @@ -9149,5665 +10102,7303 @@ progress { .gy-xxl-3 { --bs-gutter-y: 1rem; } - .g-xxl-4, - .gx-xxl-4 { - --bs-gutter-x: 1.5rem; + .g-xxl-4, + .gx-xxl-4 { + --bs-gutter-x: 1.5rem; + } + .g-xxl-4, + .gy-xxl-4 { + --bs-gutter-y: 1.5rem; + } + .g-xxl-5, + .gx-xxl-5 { + --bs-gutter-x: 3rem; + } + .g-xxl-5, + .gy-xxl-5 { + --bs-gutter-y: 3rem; + } +} +.table { + --bs-table-color-type: initial; + --bs-table-bg-type: initial; + --bs-table-color-state: initial; + --bs-table-bg-state: initial; + --bs-table-color: #dee2e6; + --bs-table-bg: var(--bs-body-bg); + --bs-table-border-color: var(--bs-border-color); + --bs-table-accent-bg: transparent; + --bs-table-striped-color: var(--bs-emphasis-color); + --bs-table-striped-bg: rgba(var(--bs-emphasis-color-rgb), 0.05); + --bs-table-active-color: var(--bs-emphasis-color); + --bs-table-active-bg: rgba(var(--bs-emphasis-color-rgb), 0.1); + --bs-table-hover-color: var(--bs-emphasis-color); + --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075); + width: 100%; + margin-bottom: 1rem; + vertical-align: top; + border-color: var(--bs-table-border-color); +} +.table > :not(caption) > * > * { + padding: 0.5rem 0.5rem; + color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color))); + background-color: var(--bs-table-bg); + border-bottom-width: var(--bs-border-width); + box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); +} +.table > tbody { + vertical-align: inherit; +} +.table > thead { + vertical-align: bottom; +} + +.table-group-divider { + border-top: calc(var(--bs-border-width) * 2) solid currentcolor; +} + +.caption-top { + caption-side: top; +} + +.table-sm > :not(caption) > * > * { + padding: 0.25rem 0.25rem; +} + +.table-bordered > :not(caption) > * { + border-width: var(--bs-border-width) 0; +} +.table-bordered > :not(caption) > * > * { + border-width: 0 var(--bs-border-width); +} + +.table-borderless > :not(caption) > * > * { + border-bottom-width: 0; +} +.table-borderless > :not(:first-child) { + border-top-width: 0; +} + +.table-striped > tbody > tr:nth-of-type(odd) > * { + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); +} + +.table-striped-columns > :not(caption) > tr > :nth-child(even) { + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); +} + +.table-active { + --bs-table-color-state: var(--bs-table-active-color); + --bs-table-bg-state: var(--bs-table-active-bg); +} + +.table-hover > tbody > tr:hover > * { + --bs-table-color-state: var(--bs-table-hover-color); + --bs-table-bg-state: var(--bs-table-hover-bg); +} + +.table-primary { + --bs-table-color: #000; + --bs-table-bg: #e8e1f5; + --bs-table-border-color: #bab4c4; + --bs-table-striped-bg: #dcd6e9; + --bs-table-striped-color: #000; + --bs-table-active-bg: #d1cbdd; + --bs-table-active-color: #000; + --bs-table-hover-bg: #d7d0e3; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-secondary { + --bs-table-color: #000; + --bs-table-bg: #d8e8f7; + --bs-table-border-color: #adbac6; + --bs-table-striped-bg: #cddceb; + --bs-table-striped-color: #000; + --bs-table-active-bg: #c2d1de; + --bs-table-active-color: #000; + --bs-table-hover-bg: #c8d7e4; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-success { + --bs-table-color: #000; + --bs-table-bg: #d1e7dd; + --bs-table-border-color: #a7b9b1; + --bs-table-striped-bg: #c7dbd2; + --bs-table-striped-color: #000; + --bs-table-active-bg: #bcd0c7; + --bs-table-active-color: #000; + --bs-table-hover-bg: #c1d6cc; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-info { + --bs-table-color: #000; + --bs-table-bg: #cff4fc; + --bs-table-border-color: #a6c3ca; + --bs-table-striped-bg: #c5e8ef; + --bs-table-striped-color: #000; + --bs-table-active-bg: #badce3; + --bs-table-active-color: #000; + --bs-table-hover-bg: #bfe2e9; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-warning { + --bs-table-color: #000; + --bs-table-bg: #fff3cd; + --bs-table-border-color: #ccc2a4; + --bs-table-striped-bg: #f2e7c3; + --bs-table-striped-color: #000; + --bs-table-active-bg: #e6dbb9; + --bs-table-active-color: #000; + --bs-table-hover-bg: #ece1be; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-danger { + --bs-table-color: #000; + --bs-table-bg: #f8d7da; + --bs-table-border-color: #c6acae; + --bs-table-striped-bg: #eccccf; + --bs-table-striped-color: #000; + --bs-table-active-bg: #dfc2c4; + --bs-table-active-color: #000; + --bs-table-hover-bg: #e5c7ca; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-light { + --bs-table-color: #000; + --bs-table-bg: #f8f9fa; + --bs-table-border-color: #c6c7c8; + --bs-table-striped-bg: #ecedee; + --bs-table-striped-color: #000; + --bs-table-active-bg: #dfe0e1; + --bs-table-active-color: #000; + --bs-table-hover-bg: #e5e6e7; + --bs-table-hover-color: #000; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-dark { + --bs-table-color: #fff; + --bs-table-bg: #212529; + --bs-table-border-color: #4d5154; + --bs-table-striped-bg: #2c3034; + --bs-table-striped-color: #fff; + --bs-table-active-bg: #373b3e; + --bs-table-active-color: #fff; + --bs-table-hover-bg: #323539; + --bs-table-hover-color: #fff; + color: var(--bs-table-color); + border-color: var(--bs-table-border-color); +} + +.table-responsive { + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } +} +@media (max-width: 767.98px) { + .table-responsive-md { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } +} +@media (max-width: 991.98px) { + .table-responsive-lg { + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } +} +@media (max-width: 1199.98px) { + .table-responsive-xl { + overflow-x: auto; + -webkit-overflow-scrolling: touch; } - .g-xxl-4, - .gy-xxl-4 { - --bs-gutter-y: 1.5rem; +} +@media (max-width: 1399.98px) { + .table-responsive-xxl { + overflow-x: auto; + -webkit-overflow-scrolling: touch; } - .g-xxl-5, - .gx-xxl-5 { - --bs-gutter-x: 3rem; +} +.form-label { + margin-bottom: 0.5rem; +} + +.col-form-label { + padding-top: calc(0.375rem + var(--bs-border-width)); + padding-bottom: calc(0.375rem + var(--bs-border-width)); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; +} + +.col-form-label-lg { + padding-top: calc(0.5rem + var(--bs-border-width)); + padding-bottom: calc(0.5rem + var(--bs-border-width)); + font-size: 1.25rem; +} + +.col-form-label-sm { + padding-top: calc(0.25rem + var(--bs-border-width)); + padding-bottom: calc(0.25rem + var(--bs-border-width)); + font-size: 0.875rem; +} + +.form-text { + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-secondary-color); +} + +.form-control { + display: block; + width: 100%; + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + appearance: none; + background-color: var(--bs-body-bg); + background-clip: padding-box; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .form-control { + transition: none; } - .g-xxl-5, - .gy-xxl-5 { - --bs-gutter-y: 3rem; +} +.form-control[type=file] { + overflow: hidden; +} +.form-control[type=file]:not(:disabled):not([readonly]) { + cursor: pointer; +} +.form-control:focus { + color: var(--bs-body-color); + background-color: var(--bs-body-bg); + border-color: #c6b4e6; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(140, 104, 205, 0.25); +} +.form-control::-webkit-date-and-time-value { + min-width: 85px; + height: 1.5em; + margin: 0; +} +.form-control::-webkit-datetime-edit { + display: block; + padding: 0; +} +.form-control::placeholder { + color: var(--bs-secondary-color); + opacity: 1; +} +.form-control:disabled { + background-color: var(--bs-secondary-bg); + opacity: 1; +} +.form-control::file-selector-button { + padding: 0.375rem 0.75rem; + margin: -0.375rem -0.75rem; + margin-inline-end: 0.75rem; + color: var(--bs-body-color); + background-color: var(--bs-tertiary-bg); + pointer-events: none; + border-color: inherit; + border-style: solid; + border-width: 0; + border-inline-end-width: var(--bs-border-width); + border-radius: 0; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .form-control::file-selector-button { + transition: none; } } -.table { - --bs-table-bg: transparent; - --bs-table-accent-bg: transparent; - --bs-table-striped-color: #00305E; - --bs-table-striped-bg: rgba(0, 0, 0, 0.05); - --bs-table-active-color: #00305E; - --bs-table-active-bg: rgba(0, 0, 0, 0.1); - --bs-table-hover-color: #00305E; - --bs-table-hover-bg: rgba(0, 0, 0, 0.075); +.form-control:hover:not(:disabled):not([readonly])::file-selector-button { + background-color: var(--bs-secondary-bg); +} + +.form-control-plaintext { + display: block; width: 100%; - margin-bottom: 1rem; - color: #00305E; + padding: 0.375rem 0; + margin-bottom: 0; + line-height: 1.5; + color: var(--bs-body-color); + background-color: transparent; + border: solid transparent; + border-width: var(--bs-border-width) 0; +} +.form-control-plaintext:focus { + outline: 0; +} +.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; +} + +.form-control-sm { + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); +} +.form-control-sm::file-selector-button { + padding: 0.25rem 0.5rem; + margin: -0.25rem -0.5rem; + margin-inline-end: 0.5rem; +} + +.form-control-lg { + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); +} +.form-control-lg::file-selector-button { + padding: 0.5rem 1rem; + margin: -0.5rem -1rem; + margin-inline-end: 1rem; +} + +textarea.form-control { + min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); +} +textarea.form-control-sm { + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); +} +textarea.form-control-lg { + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); +} + +.form-control-color { + width: 3rem; + height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); + padding: 0.375rem; +} +.form-control-color:not(:disabled):not([readonly]) { + cursor: pointer; +} +.form-control-color::-moz-color-swatch { + border: 0 !important; + border-radius: var(--bs-border-radius); +} +.form-control-color::-webkit-color-swatch { + border: 0 !important; + border-radius: var(--bs-border-radius); +} +.form-control-color.form-control-sm { + height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); +} +.form-control-color.form-control-lg { + height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); +} + +.form-select { + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); + display: block; + width: 100%; + padding: 0.375rem 2.25rem 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: var(--bs-body-color); + appearance: none; + background-color: var(--bs-body-bg); + background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none); + background-repeat: no-repeat; + background-position: right 0.75rem center; + background-size: 16px 12px; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .form-select { + transition: none; + } +} +.form-select:focus { + border-color: #c6b4e6; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(140, 104, 205, 0.25); +} +.form-select[multiple], .form-select[size]:not([size="1"]) { + padding-right: 0.75rem; + background-image: none; +} +.form-select:disabled { + background-color: var(--bs-secondary-bg); +} +.form-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 var(--bs-body-color); +} + +.form-select-sm { + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; + border-radius: var(--bs-border-radius-sm); +} + +.form-select-lg { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); +} + +[data-bs-theme=dark] .form-select { + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); +} + +.form-check { + display: block; + min-height: 1.5rem; + padding-left: 1.5em; + margin-bottom: 0.125rem; +} +.form-check .form-check-input { + float: left; + margin-left: -1.5em; +} + +.form-check-reverse { + padding-right: 1.5em; + padding-left: 0; + text-align: right; +} +.form-check-reverse .form-check-input { + float: right; + margin-right: -1.5em; + margin-left: 0; +} + +.form-check-input { + --bs-form-check-bg: var(--bs-body-bg); + flex-shrink: 0; + width: 1em; + height: 1em; + margin-top: 0.25em; vertical-align: top; - border-color: #dee2e6; + appearance: none; + background-color: var(--bs-form-check-bg); + background-image: var(--bs-form-check-bg-image); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + border: var(--bs-border-width) solid var(--bs-border-color); + print-color-adjust: exact; +} +.form-check-input[type=checkbox] { + border-radius: 0.25em; +} +.form-check-input[type=radio] { + border-radius: 50%; +} +.form-check-input:active { + filter: brightness(90%); +} +.form-check-input:focus { + border-color: #c6b4e6; + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(140, 104, 205, 0.25); } -.table > :not(caption) > * > * { - padding: 0.5rem 0.5rem; - background-color: var(--bs-table-bg); - border-bottom-width: 1px; - box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg); +.form-check-input:checked { + background-color: #8c68cd; + border-color: #8c68cd; } -.table > tbody { - vertical-align: inherit; +.form-check-input:checked[type=checkbox] { + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); } -.table > thead { - vertical-align: bottom; +.form-check-input:checked[type=radio] { + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } -.table > :not(:first-child) { - border-top: 2px solid currentColor; +.form-check-input[type=checkbox]:indeterminate { + background-color: #8c68cd; + border-color: #8c68cd; + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); } - -.caption-top { - caption-side: top; +.form-check-input:disabled { + pointer-events: none; + filter: none; + opacity: 0.5; } - -.table-sm > :not(caption) > * > * { - padding: 0.25rem 0.25rem; +.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { + cursor: default; + opacity: 0.5; } -.table-bordered > :not(caption) > * { - border-width: 1px 0; +.form-switch { + padding-left: 2.5em; } -.table-bordered > :not(caption) > * > * { - border-width: 0 1px; +.form-switch .form-check-input { + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); + width: 2em; + margin-left: -2.5em; + background-image: var(--bs-form-switch-bg); + background-position: left center; + border-radius: 2em; + transition: background-position 0.15s ease-in-out; } - -.table-borderless > :not(caption) > * > * { - border-bottom-width: 0; +@media (prefers-reduced-motion: reduce) { + .form-switch .form-check-input { + transition: none; + } } -.table-borderless > :not(:first-child) { - border-top-width: 0; +.form-switch .form-check-input:focus { + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23c6b4e6'/%3e%3c/svg%3e"); } - -.table-striped > tbody > tr:nth-of-type(odd) > * { - --bs-table-accent-bg: var(--bs-table-striped-bg); - color: var(--bs-table-striped-color); +.form-switch .form-check-input:checked { + background-position: right center; + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } - -.table-active { - --bs-table-accent-bg: var(--bs-table-active-bg); - color: var(--bs-table-active-color); +.form-switch.form-check-reverse { + padding-right: 2.5em; + padding-left: 0; } - -.table-hover > tbody > tr:hover > * { - --bs-table-accent-bg: var(--bs-table-hover-bg); - color: var(--bs-table-hover-color); +.form-switch.form-check-reverse .form-check-input { + margin-right: -2.5em; + margin-left: 0; } -.table-primary { - --bs-table-bg: #d9e4ed; - --bs-table-striped-bg: #ced9e1; - --bs-table-striped-color: #000; - --bs-table-active-bg: #c3cdd5; - --bs-table-active-color: #000; - --bs-table-hover-bg: #c9d3db; - --bs-table-hover-color: #000; - color: #000; - border-color: #c3cdd5; +.form-check-inline { + display: inline-block; + margin-right: 1rem; } -.table-secondary { - --bs-table-bg: #e2e3e5; - --bs-table-striped-bg: #d7d8da; - --bs-table-striped-color: #000; - --bs-table-active-bg: #cbccce; - --bs-table-active-color: #000; - --bs-table-hover-bg: #d1d2d4; - --bs-table-hover-color: #000; - color: #000; - border-color: #cbccce; +.btn-check { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; } - -.table-success { - --bs-table-bg: #e2eee0; - --bs-table-striped-bg: #d7e2d5; - --bs-table-striped-color: #000; - --bs-table-active-bg: #cbd6ca; - --bs-table-active-color: #000; - --bs-table-hover-bg: #d1dccf; - --bs-table-hover-color: #000; - color: #000; - border-color: #cbd6ca; +.btn-check[disabled] + .btn, .btn-check:disabled + .btn { + pointer-events: none; + filter: none; + opacity: 0.65; } -.table-info { - --bs-table-bg: #e2edf1; - --bs-table-striped-bg: #d7e1e5; - --bs-table-striped-color: #000; - --bs-table-active-bg: #cbd5d9; - --bs-table-active-color: #000; - --bs-table-hover-bg: #d1dbdf; - --bs-table-hover-color: #000; - color: #000; - border-color: #cbd5d9; +[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus) { + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e"); } -.table-warning { - --bs-table-bg: #f7e6db; - --bs-table-striped-bg: #ebdbd0; - --bs-table-striped-color: #000; - --bs-table-active-bg: #decfc5; - --bs-table-active-color: #000; - --bs-table-hover-bg: #e4d5cb; - --bs-table-hover-color: #000; - color: #000; - border-color: #decfc5; +.form-range { + width: 100%; + height: 1.5rem; + padding: 0; + appearance: none; + background-color: transparent; } - -.table-danger { - --bs-table-bg: #f3dbda; - --bs-table-striped-bg: #e7d0cf; - --bs-table-striped-color: #000; - --bs-table-active-bg: #dbc5c4; - --bs-table-active-color: #000; - --bs-table-hover-bg: #e1cbca; - --bs-table-hover-color: #000; - color: #000; - border-color: #dbc5c4; +.form-range:focus { + outline: 0; } - -.table-light { - --bs-table-bg: #F5FAFF; - --bs-table-striped-bg: #e9eef2; - --bs-table-striped-color: #000; - --bs-table-active-bg: #dde1e6; - --bs-table-active-color: #000; - --bs-table-hover-bg: #e3e7ec; - --bs-table-hover-color: #000; - color: #000; - border-color: #dde1e6; +.form-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(140, 104, 205, 0.25); } - -.table-dark { - --bs-table-bg: #30475D; - --bs-table-striped-bg: #3a5065; - --bs-table-striped-color: #fff; - --bs-table-active-bg: #45596d; - --bs-table-active-color: #fff; - --bs-table-hover-bg: #405569; - --bs-table-hover-color: #fff; - color: #fff; - border-color: #45596d; +.form-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(140, 104, 205, 0.25); } - -.table-responsive { - overflow-x: auto; - -webkit-overflow-scrolling: touch; +.form-range::-moz-focus-outer { + border: 0; } - -@media (max-width: 575.98px) { - .table-responsive-sm { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } +.form-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + appearance: none; + background-color: #8c68cd; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } -@media (max-width: 767.98px) { - .table-responsive-md { - overflow-x: auto; - -webkit-overflow-scrolling: touch; +@media (prefers-reduced-motion: reduce) { + .form-range::-webkit-slider-thumb { + transition: none; } } -@media (max-width: 991.98px) { - .table-responsive-lg { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } +.form-range::-webkit-slider-thumb:active { + background-color: #ddd2f0; } -@media (max-width: 1199.98px) { - .table-responsive-xl { - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } +.form-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: var(--bs-secondary-bg); + border-color: transparent; + border-radius: 1rem; } -@media (max-width: 1399.98px) { - .table-responsive-xxl { - overflow-x: auto; - -webkit-overflow-scrolling: touch; +.form-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + appearance: none; + background-color: #8c68cd; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} +@media (prefers-reduced-motion: reduce) { + .form-range::-moz-range-thumb { + transition: none; } } -.form-label { - margin-bottom: 0.5rem; +.form-range::-moz-range-thumb:active { + background-color: #ddd2f0; } - -.col-form-label { - padding-top: calc(0.375rem + 1px); - padding-bottom: calc(0.375rem + 1px); - margin-bottom: 0; - font-size: inherit; - line-height: 1.5; +.form-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: var(--bs-secondary-bg); + border-color: transparent; + border-radius: 1rem; } - -.col-form-label-lg { - padding-top: calc(0.5rem + 1px); - padding-bottom: calc(0.5rem + 1px); - font-size: 1.25rem; +.form-range:disabled { + pointer-events: none; } - -.col-form-label-sm { - padding-top: calc(0.25rem + 1px); - padding-bottom: calc(0.25rem + 1px); - font-size: 0.875rem; +.form-range:disabled::-webkit-slider-thumb { + background-color: var(--bs-secondary-color); } - -.form-text { - margin-top: 0.25rem; - font-size: 0.875em; - color: #6c757d; +.form-range:disabled::-moz-range-thumb { + background-color: var(--bs-secondary-color); } -.form-control { - display: block; - width: 100%; - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #00305E; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ced4da; - appearance: none; - border-radius: 0.25rem; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +.form-floating { + position: relative; +} +.form-floating > .form-control, +.form-floating > .form-control-plaintext, +.form-floating > .form-select { + height: calc(3.5rem + calc(var(--bs-border-width) * 2)); + min-height: calc(3.5rem + calc(var(--bs-border-width) * 2)); + line-height: 1.25; +} +.form-floating > label { + position: absolute; + top: 0; + left: 0; + z-index: 2; + height: 100%; + padding: 1rem 0.75rem; + overflow: hidden; + text-align: start; + text-overflow: ellipsis; + white-space: nowrap; + pointer-events: none; + border: var(--bs-border-width) solid transparent; + transform-origin: 0 0; + transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; } @media (prefers-reduced-motion: reduce) { - .form-control { + .form-floating > label { transition: none; } } -.form-control[type=file] { - overflow: hidden; +.form-floating > .form-control, +.form-floating > .form-control-plaintext { + padding: 1rem 0.75rem; +} +.form-floating > .form-control::placeholder, +.form-floating > .form-control-plaintext::placeholder { + color: transparent; +} +.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown), +.form-floating > .form-control-plaintext:focus, +.form-floating > .form-control-plaintext:not(:placeholder-shown) { + padding-top: 1.625rem; + padding-bottom: 0.625rem; +} +.form-floating > .form-control:-webkit-autofill, +.form-floating > .form-control-plaintext:-webkit-autofill { + padding-top: 1.625rem; + padding-bottom: 0.625rem; } -.form-control[type=file]:not(:disabled):not([readonly]) { - cursor: pointer; +.form-floating > .form-select { + padding-top: 1.625rem; + padding-bottom: 0.625rem; } -.form-control:focus { - color: #00305E; - background-color: #fff; - border-color: #a1bbd3; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.25); +.form-floating > .form-control:focus ~ label, +.form-floating > .form-control:not(:placeholder-shown) ~ label, +.form-floating > .form-control-plaintext ~ label, +.form-floating > .form-select ~ label { + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } -.form-control::-webkit-date-and-time-value { +.form-floating > .form-control:focus ~ label::after, +.form-floating > .form-control:not(:placeholder-shown) ~ label::after, +.form-floating > .form-control-plaintext ~ label::after, +.form-floating > .form-select ~ label::after { + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); } -.form-control::placeholder { - color: #6c757d; - opacity: 1; +.form-floating > .form-control:-webkit-autofill ~ label { + color: rgba(var(--bs-body-color-rgb), 0.65); + transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } -.form-control:disabled, .form-control[readonly] { - background-color: #e9ecef; - opacity: 1; +.form-floating > .form-control-plaintext ~ label { + border-width: var(--bs-border-width) 0; } -.form-control::file-selector-button { - padding: 0.375rem 0.75rem; - margin: -0.375rem -0.75rem; - margin-inline-end: 0.75rem; - color: #00305E; - background-color: #e9ecef; - pointer-events: none; - border-color: inherit; - border-style: solid; - border-width: 0; - border-inline-end-width: 1px; - border-radius: 0; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +.form-floating > :disabled ~ label, +.form-floating > .form-control:disabled ~ label { + color: #6c757d; } -@media (prefers-reduced-motion: reduce) { - .form-control::file-selector-button { - transition: none; - } +.form-floating > :disabled ~ label::after, +.form-floating > .form-control:disabled ~ label::after { + background-color: var(--bs-secondary-bg); } -.form-control:hover:not(:disabled):not([readonly])::file-selector-button { - background-color: #dde0e3; + +.input-group { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100%; } -.form-control::-webkit-file-upload-button { - padding: 0.375rem 0.75rem; - margin: -0.375rem -0.75rem; - margin-inline-end: 0.75rem; - color: #00305E; - background-color: #e9ecef; - pointer-events: none; - border-color: inherit; - border-style: solid; - border-width: 0; - border-inline-end-width: 1px; - border-radius: 0; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +.input-group > .form-control, +.input-group > .form-select, +.input-group > .form-floating { + position: relative; + flex: 1 1 auto; + width: 1%; + min-width: 0; } -@media (prefers-reduced-motion: reduce) { - .form-control::-webkit-file-upload-button { - transition: none; - } +.input-group > .form-control:focus, +.input-group > .form-select:focus, +.input-group > .form-floating:focus-within { + z-index: 5; +} +.input-group .btn { + position: relative; + z-index: 2; } -.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { - background-color: #dde0e3; +.input-group .btn:focus { + z-index: 5; } -.form-control-plaintext { - display: block; - width: 100%; - padding: 0.375rem 0; - margin-bottom: 0; +.input-group-text { + display: flex; + align-items: center; + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; line-height: 1.5; - color: #00305E; - background-color: transparent; - border: solid transparent; - border-width: 1px 0; + color: var(--bs-body-color); + text-align: center; + white-space: nowrap; + background-color: var(--bs-tertiary-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); } -.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { - padding-right: 0; - padding-left: 0; + +.input-group-lg > .form-control, +.input-group-lg > .form-select, +.input-group-lg > .input-group-text, +.input-group-lg > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + border-radius: var(--bs-border-radius-lg); } -.form-control-sm { - min-height: calc(1.5em + 0.5rem + 2px); +.input-group-sm > .form-control, +.input-group-sm > .form-select, +.input-group-sm > .input-group-text, +.input-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; - border-radius: 0.2rem; + border-radius: var(--bs-border-radius-sm); } -.form-control-sm::file-selector-button { - padding: 0.25rem 0.5rem; - margin: -0.25rem -0.5rem; - margin-inline-end: 0.5rem; + +.input-group-lg > .form-select, +.input-group-sm > .form-select { + padding-right: 3rem; } -.form-control-sm::-webkit-file-upload-button { - padding: 0.25rem 0.5rem; - margin: -0.25rem -0.5rem; - margin-inline-end: 0.5rem; + +.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), +.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3), +.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control, +.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating), +.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4), +.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-control, +.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-select { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { + margin-left: calc(var(--bs-border-width) * -1); + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group > .form-floating:not(:first-child) > .form-control, +.input-group > .form-floating:not(:first-child) > .form-select { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } -.form-control-lg { - min-height: calc(1.5em + 1rem + 2px); - padding: 0.5rem 1rem; - font-size: 1.25rem; - border-radius: 0.3rem; +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-form-valid-color); } -.form-control-lg::file-selector-button { - padding: 0.5rem 1rem; - margin: -0.5rem -1rem; - margin-inline-end: 1rem; + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + color: #fff; + background-color: var(--bs-success); + border-radius: var(--bs-border-radius); } -.form-control-lg::-webkit-file-upload-button { - padding: 0.5rem 1rem; - margin: -0.5rem -1rem; - margin-inline-end: 1rem; + +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; } -textarea.form-control { - min-height: calc(1.5em + 0.75rem + 2px); +.was-validated .form-control:valid, .form-control.is-valid { + border-color: var(--bs-form-valid-border-color); + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } -textarea.form-control-sm { - min-height: calc(1.5em + 0.5rem + 2px); +.was-validated .form-control:valid:focus, .form-control.is-valid:focus { + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } -textarea.form-control-lg { - min-height: calc(1.5em + 1rem + 2px); + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } -.form-control-color { - width: 3rem; - height: auto; - padding: 0.375rem; +.was-validated .form-select:valid, .form-select.is-valid { + border-color: var(--bs-form-valid-border-color); } -.form-control-color:not(:disabled):not([readonly]) { - cursor: pointer; +.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + padding-right: 4.125rem; + background-position: right 0.75rem center, center right 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } -.form-control-color::-moz-color-swatch { - height: 1.5em; - border-radius: 0.25rem; +.was-validated .form-select:valid:focus, .form-select.is-valid:focus { + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } -.form-control-color::-webkit-color-swatch { - height: 1.5em; - border-radius: 0.25rem; + +.was-validated .form-control-color:valid, .form-control-color.is-valid { + width: calc(3rem + calc(1.5em + 0.75rem)); } -.form-select { - display: block; - width: 100%; - padding: 0.375rem 2.25rem 0.375rem 0.75rem; - -moz-padding-start: calc(0.75rem - 3px); - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #00305E; - background-color: #fff; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right 0.75rem center; - background-size: 16px 12px; - border: 1px solid #ced4da; - border-radius: 0.25rem; - transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - appearance: none; +.was-validated .form-check-input:valid, .form-check-input.is-valid { + border-color: var(--bs-form-valid-border-color); } -@media (prefers-reduced-motion: reduce) { - .form-select { - transition: none; - } +.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { + background-color: var(--bs-form-valid-color); } -.form-select:focus { - border-color: #a1bbd3; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.25); +.was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } -.form-select[multiple], .form-select[size]:not([size="1"]) { - padding-right: 0.75rem; - background-image: none; +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: var(--bs-form-valid-color); } -.form-select:disabled { - background-color: #e9ecef; + +.form-check-inline .form-check-input ~ .valid-feedback { + margin-left: 0.5em; } -.form-select:-moz-focusring { - color: transparent; - text-shadow: 0 0 0 #00305E; + +.was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid, +.was-validated .input-group > .form-select:not(:focus):valid, +.input-group > .form-select:not(:focus).is-valid, +.was-validated .input-group > .form-floating:not(:focus-within):valid, +.input-group > .form-floating:not(:focus-within).is-valid { + z-index: 3; } -.form-select-sm { - padding-top: 0.25rem; - padding-bottom: 0.25rem; - padding-left: 0.5rem; - font-size: 0.875rem; - border-radius: 0.2rem; +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 0.875em; + color: var(--bs-form-invalid-color); } -.form-select-lg { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - font-size: 1.25rem; - border-radius: 0.3rem; +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: 0.1rem; + font-size: 0.875rem; + color: #fff; + background-color: var(--bs-danger); + border-radius: var(--bs-border-radius); } -.form-check { +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { display: block; - min-height: 1.5rem; - padding-left: 1.5em; - margin-bottom: 0.125rem; -} -.form-check .form-check-input { - float: left; - margin-left: -1.5em; } -.form-check-input { - width: 1em; - height: 1em; - margin-top: 0.25em; - vertical-align: top; - background-color: #fff; +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: var(--bs-form-invalid-border-color); + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; - background-position: center; - background-size: contain; - border: 1px solid rgba(0, 0, 0, 0.25); - appearance: none; - color-adjust: exact; -} -.form-check-input[type=checkbox] { - border-radius: 0.25em; -} -.form-check-input[type=radio] { - border-radius: 50%; -} -.form-check-input:active { - filter: brightness(90%); -} -.form-check-input:focus { - border-color: #a1bbd3; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.25); -} -.form-check-input:checked { - background-color: #4276A7; - border-color: #4276A7; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } -.form-check-input:checked[type=checkbox] { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e"); +.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } -.form-check-input:checked[type=radio] { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } -.form-check-input[type=checkbox]:indeterminate { - background-color: #4276A7; - border-color: #4276A7; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); + +.was-validated .form-select:invalid, .form-select.is-invalid { + border-color: var(--bs-form-invalid-border-color); } -.form-check-input:disabled { - pointer-events: none; - filter: none; - opacity: 0.5; +.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + padding-right: 4.125rem; + background-position: right 0.75rem center, center right 2.25rem; + background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } -.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { - opacity: 0.5; +.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } -.form-switch { - padding-left: 2.5em; +.was-validated .form-control-color:invalid, .form-control-color.is-invalid { + width: calc(3rem + calc(1.5em + 0.75rem)); } -.form-switch .form-check-input { - width: 2em; - margin-left: -2.5em; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); - background-position: left center; - border-radius: 2em; - transition: background-position 0.15s ease-in-out; + +.was-validated .form-check-input:invalid, .form-check-input.is-invalid { + border-color: var(--bs-form-invalid-border-color); } -@media (prefers-reduced-motion: reduce) { - .form-switch .form-check-input { - transition: none; - } +.was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { + background-color: var(--bs-form-invalid-color); } -.form-switch .form-check-input:focus { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23a1bbd3'/%3e%3c/svg%3e"); +.was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } -.form-switch .form-check-input:checked { - background-position: right center; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: var(--bs-form-invalid-color); } -.form-check-inline { - display: inline-block; - margin-right: 1rem; +.form-check-inline .form-check-input ~ .invalid-feedback { + margin-left: 0.5em; } -.btn-check { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.btn-check[disabled] + .btn, .btn-check:disabled + .btn { - pointer-events: none; - filter: none; - opacity: 0.65; +.was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid, +.was-validated .input-group > .form-select:not(:focus):invalid, +.input-group > .form-select:not(:focus).is-invalid, +.was-validated .input-group > .form-floating:not(:focus-within):invalid, +.input-group > .form-floating:not(:focus-within).is-invalid { + z-index: 4; } -.form-range { - width: 100%; - height: 1.5rem; - padding: 0; - background-color: transparent; - appearance: none; +.btn { + --bs-btn-padding-x: 0.75rem; + --bs-btn-padding-y: 0.375rem; + --bs-btn-font-family: ; + --bs-btn-font-size: 1rem; + --bs-btn-font-weight: 400; + --bs-btn-line-height: 1.5; + --bs-btn-color: var(--bs-body-color); + --bs-btn-bg: transparent; + --bs-btn-border-width: var(--bs-border-width); + --bs-btn-border-color: transparent; + --bs-btn-border-radius: var(--bs-border-radius); + --bs-btn-hover-border-color: transparent; + --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); + --bs-btn-disabled-opacity: 0.65; + --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5); + display: inline-block; + padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x); + font-family: var(--bs-btn-font-family); + font-size: var(--bs-btn-font-size); + font-weight: var(--bs-btn-font-weight); + line-height: var(--bs-btn-line-height); + color: var(--bs-btn-color); + text-align: center; + text-decoration: none; + vertical-align: middle; + cursor: pointer; + user-select: none; + border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); + border-radius: var(--bs-btn-border-radius); + background-color: var(--bs-btn-bg); + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } -.form-range:focus { - outline: 0; +@media (prefers-reduced-motion: reduce) { + .btn { + transition: none; + } } -.form-range:focus::-webkit-slider-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(66, 118, 167, 0.25); +.btn:hover { + color: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); +} +.btn-check + .btn:hover { + color: var(--bs-btn-color); + background-color: var(--bs-btn-bg); + border-color: var(--bs-btn-border-color); +} +.btn:focus-visible { + color: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); + outline: 0; + box-shadow: var(--bs-btn-focus-box-shadow); } -.form-range:focus::-moz-range-thumb { - box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(66, 118, 167, 0.25); +.btn-check:focus-visible + .btn { + border-color: var(--bs-btn-hover-border-color); + outline: 0; + box-shadow: var(--bs-btn-focus-box-shadow); } -.form-range::-moz-focus-outer { - border: 0; +.btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show { + color: var(--bs-btn-active-color); + background-color: var(--bs-btn-active-bg); + border-color: var(--bs-btn-active-border-color); } -.form-range::-webkit-slider-thumb { - width: 1rem; - height: 1rem; - margin-top: -0.25rem; - background-color: #4276A7; - border: 0; - border-radius: 1rem; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - appearance: none; +.btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible { + box-shadow: var(--bs-btn-focus-box-shadow); } -@media (prefers-reduced-motion: reduce) { - .form-range::-webkit-slider-thumb { - transition: none; - } +.btn-check:checked:focus-visible + .btn { + box-shadow: var(--bs-btn-focus-box-shadow); } -.form-range::-webkit-slider-thumb:active { - background-color: #c6d6e5; +.btn:disabled, .btn.disabled, fieldset:disabled .btn { + color: var(--bs-btn-disabled-color); + pointer-events: none; + background-color: var(--bs-btn-disabled-bg); + border-color: var(--bs-btn-disabled-border-color); + opacity: var(--bs-btn-disabled-opacity); } -.form-range::-webkit-slider-runnable-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; + +.btn-primary { + --bs-btn-color: #000; + --bs-btn-bg: #8c68cd; + --bs-btn-border-color: #8c68cd; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #9d7fd5; + --bs-btn-hover-border-color: #9877d2; + --bs-btn-focus-shadow-rgb: 119, 88, 174; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #a386d7; + --bs-btn-active-border-color: #9877d2; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #8c68cd; + --bs-btn-disabled-border-color: #8c68cd; } -.form-range::-moz-range-thumb { - width: 1rem; - height: 1rem; - background-color: #4276A7; - border: 0; - border-radius: 1rem; - transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - appearance: none; + +.btn-secondary { + --bs-btn-color: #000; + --bs-btn-bg: #3c8cd6; + --bs-btn-border-color: #3c8cd6; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #599ddc; + --bs-btn-hover-border-color: #5098da; + --bs-btn-focus-shadow-rgb: 51, 119, 182; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #63a3de; + --bs-btn-active-border-color: #5098da; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #3c8cd6; + --bs-btn-disabled-border-color: #3c8cd6; } -@media (prefers-reduced-motion: reduce) { - .form-range::-moz-range-thumb { - transition: none; - } + +.btn-success { + --bs-btn-color: #fff; + --bs-btn-bg: #198754; + --bs-btn-border-color: #198754; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #157347; + --bs-btn-hover-border-color: #146c43; + --bs-btn-focus-shadow-rgb: 60, 153, 110; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #146c43; + --bs-btn-active-border-color: #13653f; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #198754; + --bs-btn-disabled-border-color: #198754; } -.form-range::-moz-range-thumb:active { - background-color: #c6d6e5; + +.btn-info { + --bs-btn-color: #000; + --bs-btn-bg: #0dcaf0; + --bs-btn-border-color: #0dcaf0; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #31d2f2; + --bs-btn-hover-border-color: #25cff2; + --bs-btn-focus-shadow-rgb: 11, 172, 204; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #3dd5f3; + --bs-btn-active-border-color: #25cff2; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #0dcaf0; + --bs-btn-disabled-border-color: #0dcaf0; } -.form-range::-moz-range-track { - width: 100%; - height: 0.5rem; - color: transparent; - cursor: pointer; - background-color: #dee2e6; - border-color: transparent; - border-radius: 1rem; + +.btn-warning { + --bs-btn-color: #000; + --bs-btn-bg: #ffc107; + --bs-btn-border-color: #ffc107; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #ffca2c; + --bs-btn-hover-border-color: #ffc720; + --bs-btn-focus-shadow-rgb: 217, 164, 6; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #ffcd39; + --bs-btn-active-border-color: #ffc720; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #ffc107; + --bs-btn-disabled-border-color: #ffc107; } -.form-range:disabled { - pointer-events: none; + +.btn-danger { + --bs-btn-color: #fff; + --bs-btn-bg: #dc3545; + --bs-btn-border-color: #dc3545; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #bb2d3b; + --bs-btn-hover-border-color: #b02a37; + --bs-btn-focus-shadow-rgb: 225, 83, 97; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #b02a37; + --bs-btn-active-border-color: #a52834; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #dc3545; + --bs-btn-disabled-border-color: #dc3545; } -.form-range:disabled::-webkit-slider-thumb { - background-color: #adb5bd; + +.btn-light { + --bs-btn-color: #000; + --bs-btn-bg: #f8f9fa; + --bs-btn-border-color: #f8f9fa; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #d3d4d5; + --bs-btn-hover-border-color: #c6c7c8; + --bs-btn-focus-shadow-rgb: 211, 212, 213; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #c6c7c8; + --bs-btn-active-border-color: #babbbc; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #f8f9fa; + --bs-btn-disabled-border-color: #f8f9fa; } -.form-range:disabled::-moz-range-thumb { - background-color: #adb5bd; + +.btn-dark { + --bs-btn-color: #fff; + --bs-btn-bg: #212529; + --bs-btn-border-color: #212529; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #424649; + --bs-btn-hover-border-color: #373b3e; + --bs-btn-focus-shadow-rgb: 66, 70, 73; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #4d5154; + --bs-btn-active-border-color: #373b3e; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #212529; + --bs-btn-disabled-border-color: #212529; +} + +.btn-accent { + --bs-btn-color: #000; + --bs-btn-bg: #9CEC5B; + --bs-btn-border-color: #9CEC5B; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #abef74; + --bs-btn-hover-border-color: #a6ee6b; + --bs-btn-focus-shadow-rgb: 133, 201, 77; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #b0f07c; + --bs-btn-active-border-color: #a6ee6b; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #9CEC5B; + --bs-btn-disabled-border-color: #9CEC5B; +} + +.btn-cinereous { + --bs-btn-color: #000; + --bs-btn-bg: #837569; + --bs-btn-border-color: #837569; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #968a80; + --bs-btn-hover-border-color: #8f8378; + --bs-btn-focus-shadow-rgb: 111, 99, 89; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #9c9187; + --bs-btn-active-border-color: #8f8378; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #837569; + --bs-btn-disabled-border-color: #837569; +} + +.btn-verdigris { + --bs-btn-color: #000; + --bs-btn-bg: #50C5B7; + --bs-btn-border-color: #50C5B7; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #6acec2; + --bs-btn-hover-border-color: #62cbbe; + --bs-btn-focus-shadow-rgb: 68, 167, 156; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #73d1c5; + --bs-btn-active-border-color: #62cbbe; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #50C5B7; + --bs-btn-disabled-border-color: #50C5B7; +} + +.btn-icterine { + --bs-btn-color: #000; + --bs-btn-bg: #F0F465; + --bs-btn-border-color: #F0F465; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #f2f67c; + --bs-btn-hover-border-color: #f2f574; + --bs-btn-focus-shadow-rgb: 204, 207, 86; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #f3f684; + --bs-btn-active-border-color: #f2f574; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #F0F465; + --bs-btn-disabled-border-color: #F0F465; +} + +.btn-mute { + --bs-btn-color: #fff; + --bs-btn-bg: #6c757d; + --bs-btn-border-color: #6c757d; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #5c636a; + --bs-btn-hover-border-color: #565e64; + --bs-btn-focus-shadow-rgb: 130, 138, 145; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #565e64; + --bs-btn-active-border-color: #51585e; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #6c757d; + --bs-btn-disabled-border-color: #6c757d; } -.form-floating { - position: relative; +.btn-outline-primary { + --bs-btn-color: #8c68cd; + --bs-btn-border-color: #8c68cd; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #8c68cd; + --bs-btn-hover-border-color: #8c68cd; + --bs-btn-focus-shadow-rgb: 140, 104, 205; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #8c68cd; + --bs-btn-active-border-color: #8c68cd; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #8c68cd; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #8c68cd; + --bs-gradient: none; } -.form-floating > .form-control, -.form-floating > .form-select { - height: calc(3.5rem + 2px); - line-height: 1.25; + +.btn-outline-secondary { + --bs-btn-color: #3c8cd6; + --bs-btn-border-color: #3c8cd6; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #3c8cd6; + --bs-btn-hover-border-color: #3c8cd6; + --bs-btn-focus-shadow-rgb: 60, 140, 214; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #3c8cd6; + --bs-btn-active-border-color: #3c8cd6; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #3c8cd6; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #3c8cd6; + --bs-gradient: none; } -.form-floating > label { - position: absolute; - top: 0; - left: 0; - height: 100%; - padding: 1rem 0.75rem; - pointer-events: none; - border: 1px solid transparent; - transform-origin: 0 0; - transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; + +.btn-outline-success { + --bs-btn-color: #198754; + --bs-btn-border-color: #198754; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #198754; + --bs-btn-hover-border-color: #198754; + --bs-btn-focus-shadow-rgb: 25, 135, 84; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #198754; + --bs-btn-active-border-color: #198754; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #198754; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #198754; + --bs-gradient: none; } -@media (prefers-reduced-motion: reduce) { - .form-floating > label { - transition: none; - } + +.btn-outline-info { + --bs-btn-color: #0dcaf0; + --bs-btn-border-color: #0dcaf0; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #0dcaf0; + --bs-btn-hover-border-color: #0dcaf0; + --bs-btn-focus-shadow-rgb: 13, 202, 240; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #0dcaf0; + --bs-btn-active-border-color: #0dcaf0; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #0dcaf0; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #0dcaf0; + --bs-gradient: none; } -.form-floating > .form-control { - padding: 1rem 0.75rem; + +.btn-outline-warning { + --bs-btn-color: #ffc107; + --bs-btn-border-color: #ffc107; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #ffc107; + --bs-btn-hover-border-color: #ffc107; + --bs-btn-focus-shadow-rgb: 255, 193, 7; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #ffc107; + --bs-btn-active-border-color: #ffc107; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #ffc107; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #ffc107; + --bs-gradient: none; } -.form-floating > .form-control::placeholder { - color: transparent; + +.btn-outline-danger { + --bs-btn-color: #dc3545; + --bs-btn-border-color: #dc3545; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #dc3545; + --bs-btn-hover-border-color: #dc3545; + --bs-btn-focus-shadow-rgb: 220, 53, 69; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #dc3545; + --bs-btn-active-border-color: #dc3545; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #dc3545; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #dc3545; + --bs-gradient: none; } -.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown) { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + +.btn-outline-light { + --bs-btn-color: #f8f9fa; + --bs-btn-border-color: #f8f9fa; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #f8f9fa; + --bs-btn-hover-border-color: #f8f9fa; + --bs-btn-focus-shadow-rgb: 248, 249, 250; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #f8f9fa; + --bs-btn-active-border-color: #f8f9fa; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #f8f9fa; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #f8f9fa; + --bs-gradient: none; } -.form-floating > .form-control:-webkit-autofill { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + +.btn-outline-dark { + --bs-btn-color: #212529; + --bs-btn-border-color: #212529; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #212529; + --bs-btn-hover-border-color: #212529; + --bs-btn-focus-shadow-rgb: 33, 37, 41; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #212529; + --bs-btn-active-border-color: #212529; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #212529; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #212529; + --bs-gradient: none; +} + +.btn-outline-accent { + --bs-btn-color: #9CEC5B; + --bs-btn-border-color: #9CEC5B; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #9CEC5B; + --bs-btn-hover-border-color: #9CEC5B; + --bs-btn-focus-shadow-rgb: 156, 236, 91; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #9CEC5B; + --bs-btn-active-border-color: #9CEC5B; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #9CEC5B; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #9CEC5B; + --bs-gradient: none; +} + +.btn-outline-cinereous { + --bs-btn-color: #837569; + --bs-btn-border-color: #837569; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #837569; + --bs-btn-hover-border-color: #837569; + --bs-btn-focus-shadow-rgb: 131, 117, 105; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #837569; + --bs-btn-active-border-color: #837569; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #837569; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #837569; + --bs-gradient: none; +} + +.btn-outline-verdigris { + --bs-btn-color: #50C5B7; + --bs-btn-border-color: #50C5B7; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #50C5B7; + --bs-btn-hover-border-color: #50C5B7; + --bs-btn-focus-shadow-rgb: 80, 197, 183; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #50C5B7; + --bs-btn-active-border-color: #50C5B7; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #50C5B7; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #50C5B7; + --bs-gradient: none; +} + +.btn-outline-icterine { + --bs-btn-color: #F0F465; + --bs-btn-border-color: #F0F465; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #F0F465; + --bs-btn-hover-border-color: #F0F465; + --bs-btn-focus-shadow-rgb: 240, 244, 101; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #F0F465; + --bs-btn-active-border-color: #F0F465; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #F0F465; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #F0F465; + --bs-gradient: none; +} + +.btn-outline-mute { + --bs-btn-color: #6c757d; + --bs-btn-border-color: #6c757d; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #6c757d; + --bs-btn-hover-border-color: #6c757d; + --bs-btn-focus-shadow-rgb: 108, 117, 125; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #6c757d; + --bs-btn-active-border-color: #6c757d; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #6c757d; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #6c757d; + --bs-gradient: none; } -.form-floating > .form-select { - padding-top: 1.625rem; - padding-bottom: 0.625rem; + +.btn-link { + --bs-btn-font-weight: 400; + --bs-btn-color: var(--bs-link-color); + --bs-btn-bg: transparent; + --bs-btn-border-color: transparent; + --bs-btn-hover-color: var(--bs-link-hover-color); + --bs-btn-hover-border-color: transparent; + --bs-btn-active-color: var(--bs-link-hover-color); + --bs-btn-active-border-color: transparent; + --bs-btn-disabled-color: #6c757d; + --bs-btn-disabled-border-color: transparent; + --bs-btn-box-shadow: 0 0 0 #000; + --bs-btn-focus-shadow-rgb: 119, 88, 174; + text-decoration: underline; } -.form-floating > .form-control:focus ~ label, -.form-floating > .form-control:not(:placeholder-shown) ~ label, -.form-floating > .form-select ~ label { - opacity: 0.65; - transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); +.btn-link:focus-visible { + color: var(--bs-btn-color); } -.form-floating > .form-control:-webkit-autofill ~ label { - opacity: 0.65; - transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); +.btn-link:hover { + color: var(--bs-btn-hover-color); } -.input-group { - position: relative; - display: flex; - flex-wrap: wrap; - align-items: stretch; - width: 100%; +.btn-lg, .btn-group-lg > .btn { + --bs-btn-padding-y: 0.5rem; + --bs-btn-padding-x: 1rem; + --bs-btn-font-size: 1.25rem; + --bs-btn-border-radius: var(--bs-border-radius-lg); } -.input-group > .form-control, -.input-group > .form-select { - position: relative; - flex: 1 1 auto; - width: 1%; - min-width: 0; + +.btn-sm, .btn-group-sm > .btn { + --bs-btn-padding-y: 0.25rem; + --bs-btn-padding-x: 0.5rem; + --bs-btn-font-size: 0.875rem; + --bs-btn-border-radius: var(--bs-border-radius-sm); } -.input-group > .form-control:focus, -.input-group > .form-select:focus { - z-index: 3; + +.fade { + transition: opacity 0.15s linear; } -.input-group .btn { - position: relative; - z-index: 2; +@media (prefers-reduced-motion: reduce) { + .fade { + transition: none; + } } -.input-group .btn:focus { - z-index: 3; +.fade:not(.show) { + opacity: 0; } -.input-group-text { - display: flex; - align-items: center; - padding: 0.375rem 0.75rem; - font-size: 1rem; - font-weight: 400; - line-height: 1.5; - color: #00305E; - text-align: center; - white-space: nowrap; - background-color: #e9ecef; - border: 1px solid #ced4da; - border-radius: 0.25rem; +.collapse:not(.show) { + display: none; } -.input-group-lg > .form-control, -.input-group-lg > .form-select, -.input-group-lg > .input-group-text, -.input-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - border-radius: 0.3rem; +.collapsing { + height: 0; + overflow: hidden; + transition: height 0.35s ease; } - -.input-group-sm > .form-control, -.input-group-sm > .form-select, -.input-group-sm > .input-group-text, -.input-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - border-radius: 0.2rem; +@media (prefers-reduced-motion: reduce) { + .collapsing { + transition: none; + } +} +.collapsing.collapse-horizontal { + width: 0; + height: auto; + transition: width 0.35s ease; +} +@media (prefers-reduced-motion: reduce) { + .collapsing.collapse-horizontal { + transition: none; + } } -.input-group-lg > .form-select, -.input-group-sm > .form-select { - padding-right: 3rem; +.dropup, +.dropend, +.dropdown, +.dropstart, +.dropup-center, +.dropdown-center { + position: relative; } -.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu), -.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; +.dropdown-toggle { + white-space: nowrap; } -.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu), -.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; +.dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; } -.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { - margin-left: -1px; - border-top-left-radius: 0; - border-bottom-left-radius: 0; +.dropdown-toggle:empty::after { + margin-left: 0; } -.valid-feedback { +.dropdown-menu { + --bs-dropdown-zindex: 1000; + --bs-dropdown-min-width: 10rem; + --bs-dropdown-padding-x: 0; + --bs-dropdown-padding-y: 0.5rem; + --bs-dropdown-spacer: 0.125rem; + --bs-dropdown-font-size: 1rem; + --bs-dropdown-color: var(--bs-body-color); + --bs-dropdown-bg: var(--bs-body-bg); + --bs-dropdown-border-color: var(--bs-border-color-translucent); + --bs-dropdown-border-radius: var(--bs-border-radius); + --bs-dropdown-border-width: var(--bs-border-width); + --bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width)); + --bs-dropdown-divider-bg: var(--bs-border-color-translucent); + --bs-dropdown-divider-margin-y: 0.5rem; + --bs-dropdown-box-shadow: var(--bs-box-shadow); + --bs-dropdown-link-color: var(--bs-body-color); + --bs-dropdown-link-hover-color: var(--bs-body-color); + --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg); + --bs-dropdown-link-active-color: #fff; + --bs-dropdown-link-active-bg: #8c68cd; + --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); + --bs-dropdown-item-padding-x: 1rem; + --bs-dropdown-item-padding-y: 0.25rem; + --bs-dropdown-header-color: #6c757d; + --bs-dropdown-header-padding-x: 1rem; + --bs-dropdown-header-padding-y: 0.5rem; + position: absolute; + z-index: var(--bs-dropdown-zindex); display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 0.875em; - color: #6EAB63; + min-width: var(--bs-dropdown-min-width); + padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x); + margin: 0; + font-size: var(--bs-dropdown-font-size); + color: var(--bs-dropdown-color); + text-align: left; + list-style: none; + background-color: var(--bs-dropdown-bg); + background-clip: padding-box; + border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color); + border-radius: var(--bs-dropdown-border-radius); } - -.valid-tooltip { - position: absolute; +.dropdown-menu[data-bs-popper] { top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: 0.1rem; - font-size: 0.875rem; - color: #000; - background-color: rgba(110, 171, 99, 0.9); - border-radius: 0.25rem; + left: 0; + margin-top: var(--bs-dropdown-spacer); } -.was-validated :valid ~ .valid-feedback, -.was-validated :valid ~ .valid-tooltip, -.is-valid ~ .valid-feedback, -.is-valid ~ .valid-tooltip { - display: block; +.dropdown-menu-start { + --bs-position: start; +} +.dropdown-menu-start[data-bs-popper] { + right: auto; + left: 0; } -.was-validated .form-control:valid, .form-control.is-valid { - border-color: #6EAB63; - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%236EAB63' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +.dropdown-menu-end { + --bs-position: end; } -.was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: #6EAB63; - box-shadow: 0 0 0 0.25rem rgba(110, 171, 99, 0.25); +.dropdown-menu-end[data-bs-popper] { + right: 0; + left: auto; } -.was-validated textarea.form-control:valid, textarea.form-control.is-valid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); +@media (min-width: 576px) { + .dropdown-menu-sm-start { + --bs-position: start; + } + .dropdown-menu-sm-start[data-bs-popper] { + right: auto; + left: 0; + } + .dropdown-menu-sm-end { + --bs-position: end; + } + .dropdown-menu-sm-end[data-bs-popper] { + right: 0; + left: auto; + } +} +@media (min-width: 768px) { + .dropdown-menu-md-start { + --bs-position: start; + } + .dropdown-menu-md-start[data-bs-popper] { + right: auto; + left: 0; + } + .dropdown-menu-md-end { + --bs-position: end; + } + .dropdown-menu-md-end[data-bs-popper] { + right: 0; + left: auto; + } +} +@media (min-width: 992px) { + .dropdown-menu-lg-start { + --bs-position: start; + } + .dropdown-menu-lg-start[data-bs-popper] { + right: auto; + left: 0; + } + .dropdown-menu-lg-end { + --bs-position: end; + } + .dropdown-menu-lg-end[data-bs-popper] { + right: 0; + left: auto; + } +} +@media (min-width: 1200px) { + .dropdown-menu-xl-start { + --bs-position: start; + } + .dropdown-menu-xl-start[data-bs-popper] { + right: auto; + left: 0; + } + .dropdown-menu-xl-end { + --bs-position: end; + } + .dropdown-menu-xl-end[data-bs-popper] { + right: 0; + left: auto; + } +} +@media (min-width: 1400px) { + .dropdown-menu-xxl-start { + --bs-position: start; + } + .dropdown-menu-xxl-start[data-bs-popper] { + right: auto; + left: 0; + } + .dropdown-menu-xxl-end { + --bs-position: end; + } + .dropdown-menu-xxl-end[data-bs-popper] { + right: 0; + left: auto; + } +} +.dropup .dropdown-menu[data-bs-popper] { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: var(--bs-dropdown-spacer); +} +.dropup .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; +} +.dropup .dropdown-toggle:empty::after { + margin-left: 0; } -.was-validated .form-select:valid, .form-select.is-valid { - border-color: #6EAB63; +.dropend .dropdown-menu[data-bs-popper] { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: var(--bs-dropdown-spacer); } -.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { - padding-right: 4.125rem; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%236EAB63' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); - background-position: right 0.75rem center, center right 2.25rem; - background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +.dropend .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; } -.was-validated .form-select:valid:focus, .form-select.is-valid:focus { - border-color: #6EAB63; - box-shadow: 0 0 0 0.25rem rgba(110, 171, 99, 0.25); +.dropend .dropdown-toggle:empty::after { + margin-left: 0; +} +.dropend .dropdown-toggle::after { + vertical-align: 0; +} + +.dropstart .dropdown-menu[data-bs-popper] { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: var(--bs-dropdown-spacer); +} +.dropstart .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; } - -.was-validated .form-check-input:valid, .form-check-input.is-valid { - border-color: #6EAB63; +.dropstart .dropdown-toggle::after { + display: none; } -.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { - background-color: #6EAB63; +.dropstart .dropdown-toggle::before { + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; } -.was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { - box-shadow: 0 0 0 0.25rem rgba(110, 171, 99, 0.25); +.dropstart .dropdown-toggle:empty::after { + margin-left: 0; } -.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: #6EAB63; +.dropstart .dropdown-toggle::before { + vertical-align: 0; } -.form-check-inline .form-check-input ~ .valid-feedback { - margin-left: 0.5em; +.dropdown-divider { + height: 0; + margin: var(--bs-dropdown-divider-margin-y) 0; + overflow: hidden; + border-top: 1px solid var(--bs-dropdown-divider-bg); + opacity: 1; } -.was-validated .input-group .form-control:valid, .input-group .form-control.is-valid, -.was-validated .input-group .form-select:valid, -.input-group .form-select.is-valid { - z-index: 1; +.dropdown-item { + display: block; + width: 100%; + padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); + clear: both; + font-weight: 400; + color: var(--bs-dropdown-link-color); + text-align: inherit; + text-decoration: none; + white-space: nowrap; + background-color: transparent; + border: 0; + border-radius: var(--bs-dropdown-item-border-radius, 0); } -.was-validated .input-group .form-control:valid:focus, .input-group .form-control.is-valid:focus, -.was-validated .input-group .form-select:valid:focus, -.input-group .form-select.is-valid:focus { - z-index: 3; +.dropdown-item:hover, .dropdown-item:focus { + color: var(--bs-dropdown-link-hover-color); + background-color: var(--bs-dropdown-link-hover-bg); } - -.invalid-feedback { - display: none; - width: 100%; - margin-top: 0.25rem; - font-size: 0.875em; - color: #C54C45; +.dropdown-item.active, .dropdown-item:active { + color: var(--bs-dropdown-link-active-color); + text-decoration: none; + background-color: var(--bs-dropdown-link-active-bg); } - -.invalid-tooltip { - position: absolute; - top: 100%; - z-index: 5; - display: none; - max-width: 100%; - padding: 0.25rem 0.5rem; - margin-top: 0.1rem; - font-size: 0.875rem; - color: #fff; - background-color: rgba(197, 76, 69, 0.9); - border-radius: 0.25rem; +.dropdown-item.disabled, .dropdown-item:disabled { + color: var(--bs-dropdown-link-disabled-color); + pointer-events: none; + background-color: transparent; } -.was-validated :invalid ~ .invalid-feedback, -.was-validated :invalid ~ .invalid-tooltip, -.is-invalid ~ .invalid-feedback, -.is-invalid ~ .invalid-tooltip { +.dropdown-menu.show { display: block; } -.was-validated .form-control:invalid, .form-control.is-invalid { - border-color: #C54C45; - padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23C54C45'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23C54C45' stroke='none'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-position: right calc(0.375em + 0.1875rem) center; - background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +.dropdown-header { + display: block; + padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x); + margin-bottom: 0; + font-size: 0.875rem; + color: var(--bs-dropdown-header-color); + white-space: nowrap; } -.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: #C54C45; - box-shadow: 0 0 0 0.25rem rgba(197, 76, 69, 0.25); + +.dropdown-item-text { + display: block; + padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x); + color: var(--bs-dropdown-link-color); } -.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { - padding-right: calc(1.5em + 0.75rem); - background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); +.dropdown-menu-dark { + --bs-dropdown-color: #dee2e6; + --bs-dropdown-bg: #343a40; + --bs-dropdown-border-color: var(--bs-border-color-translucent); + --bs-dropdown-box-shadow: ; + --bs-dropdown-link-color: #dee2e6; + --bs-dropdown-link-hover-color: #fff; + --bs-dropdown-divider-bg: var(--bs-border-color-translucent); + --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15); + --bs-dropdown-link-active-color: #fff; + --bs-dropdown-link-active-bg: #8c68cd; + --bs-dropdown-link-disabled-color: #adb5bd; + --bs-dropdown-header-color: #adb5bd; } -.was-validated .form-select:invalid, .form-select.is-invalid { - border-color: #C54C45; +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-flex; + vertical-align: middle; } -.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { - padding-right: 4.125rem; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23C54C45'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23C54C45' stroke='none'/%3e%3c/svg%3e"); - background-position: right 0.75rem center, center right 2.25rem; - background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + flex: 1 1 auto; } -.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { - border-color: #C54C45; - box-shadow: 0 0 0 0.25rem rgba(197, 76, 69, 0.25); +.btn-group > .btn-check:checked + .btn, +.btn-group > .btn-check:focus + .btn, +.btn-group > .btn:hover, +.btn-group > .btn:focus, +.btn-group > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn-check:checked + .btn, +.btn-group-vertical > .btn-check:focus + .btn, +.btn-group-vertical > .btn:hover, +.btn-group-vertical > .btn:focus, +.btn-group-vertical > .btn:active, +.btn-group-vertical > .btn.active { + z-index: 1; } -.was-validated .form-check-input:invalid, .form-check-input.is-invalid { - border-color: #C54C45; -} -.was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { - background-color: #C54C45; -} -.was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { - box-shadow: 0 0 0 0.25rem rgba(197, 76, 69, 0.25); +.btn-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } -.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: #C54C45; +.btn-toolbar .input-group { + width: auto; } -.form-check-inline .form-check-input ~ .invalid-feedback { - margin-left: 0.5em; +.btn-group { + border-radius: var(--bs-border-radius); } - -.was-validated .input-group .form-control:invalid, .input-group .form-control.is-invalid, -.was-validated .input-group .form-select:invalid, -.input-group .form-select.is-invalid { - z-index: 2; +.btn-group > :not(.btn-check:first-child) + .btn, +.btn-group > .btn-group:not(:first-child) { + margin-left: calc(var(--bs-border-width) * -1); } -.was-validated .input-group .form-control:invalid:focus, .input-group .form-control.is-invalid:focus, -.was-validated .input-group .form-select:invalid:focus, -.input-group .form-select.is-invalid:focus { - z-index: 3; +.btn-group > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group > .btn.dropdown-toggle-split:first-child, +.btn-group > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } - -.btn { - display: inline-block; - font-weight: 400; - line-height: 1.5; - color: #00305E; - text-align: center; - text-decoration: none; - vertical-align: middle; - cursor: pointer; - user-select: none; - background-color: transparent; - border: 1px solid transparent; - padding: 0.375rem 0.75rem; - font-size: 1rem; - border-radius: 0.25rem; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +.btn-group > .btn:nth-child(n+3), +.btn-group > :not(.btn-check) + .btn, +.btn-group > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } -@media (prefers-reduced-motion: reduce) { - .btn { - transition: none; - } + +.dropdown-toggle-split { + padding-right: 0.5625rem; + padding-left: 0.5625rem; } -.btn:hover { - color: #00305E; +.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { + margin-left: 0; } -.btn-check:focus + .btn, .btn:focus { - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.25); +.dropstart .dropdown-toggle-split::before { + margin-right: 0; } -.btn:disabled, .btn.disabled, fieldset:disabled .btn { - pointer-events: none; - opacity: 0.65; + +.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { + padding-right: 0.375rem; + padding-left: 0.375rem; } -.btn-primary { - color: #fff; - background-color: #4276A7; - border-color: #4276A7; +.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { + padding-right: 0.75rem; + padding-left: 0.75rem; } -.btn-primary:hover { - color: #fff; - background-color: #38648e; - border-color: #355e86; + +.btn-group-vertical { + flex-direction: column; + align-items: flex-start; + justify-content: center; } -.btn-check:focus + .btn-primary, .btn-primary:focus { - color: #fff; - background-color: #38648e; - border-color: #355e86; - box-shadow: 0 0 0 0.25rem rgba(94, 139, 180, 0.5); +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group { + width: 100%; } -.btn-check:checked + .btn-primary, .btn-check:active + .btn-primary, .btn-primary:active, .btn-primary.active, .show > .btn-primary.dropdown-toggle { - color: #fff; - background-color: #355e86; - border-color: #32597d; +.btn-group-vertical > .btn:not(:first-child), +.btn-group-vertical > .btn-group:not(:first-child) { + margin-top: calc(var(--bs-border-width) * -1); } -.btn-check:checked + .btn-primary:focus, .btn-check:active + .btn-primary:focus, .btn-primary:active:focus, .btn-primary.active:focus, .show > .btn-primary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(94, 139, 180, 0.5); +.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group-vertical > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; } -.btn-primary:disabled, .btn-primary.disabled { - color: #fff; - background-color: #4276A7; - border-color: #4276A7; +.btn-group-vertical > .btn ~ .btn, +.btn-group-vertical > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; } -.btn-secondary { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; +.nav { + --bs-nav-link-padding-x: 1rem; + --bs-nav-link-padding-y: 0.5rem; + --bs-nav-link-font-weight: ; + --bs-nav-link-color: var(--bs-link-color); + --bs-nav-link-hover-color: var(--bs-link-hover-color); + --bs-nav-link-disabled-color: var(--bs-secondary-color); + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; } -.btn-secondary:hover { - color: #fff; - background-color: #5c636a; - border-color: #565e64; + +.nav-link { + display: block; + padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x); + font-size: var(--bs-nav-link-font-size); + font-weight: var(--bs-nav-link-font-weight); + color: var(--bs-nav-link-color); + text-decoration: none; + background: none; + border: 0; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } -.btn-check:focus + .btn-secondary, .btn-secondary:focus { - color: #fff; - background-color: #5c636a; - border-color: #565e64; - box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5); +@media (prefers-reduced-motion: reduce) { + .nav-link { + transition: none; + } } -.btn-check:checked + .btn-secondary, .btn-check:active + .btn-secondary, .btn-secondary:active, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle { - color: #fff; - background-color: #565e64; - border-color: #51585e; +.nav-link:hover, .nav-link:focus { + color: var(--bs-nav-link-hover-color); } -.btn-check:checked + .btn-secondary:focus, .btn-check:active + .btn-secondary:focus, .btn-secondary:active:focus, .btn-secondary.active:focus, .show > .btn-secondary.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5); +.nav-link:focus-visible { + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(140, 104, 205, 0.25); } -.btn-secondary:disabled, .btn-secondary.disabled { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; +.nav-link.disabled, .nav-link:disabled { + color: var(--bs-nav-link-disabled-color); + pointer-events: none; + cursor: default; } -.btn-success { - color: #000; - background-color: #6EAB63; - border-color: #6EAB63; -} -.btn-success:hover { - color: #000; - background-color: #84b87a; - border-color: #7db373; +.nav-tabs { + --bs-nav-tabs-border-width: var(--bs-border-width); + --bs-nav-tabs-border-color: var(--bs-border-color); + --bs-nav-tabs-border-radius: var(--bs-border-radius); + --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color); + --bs-nav-tabs-link-active-color: var(--bs-emphasis-color); + --bs-nav-tabs-link-active-bg: var(--bs-body-bg); + --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg); + border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); } -.btn-check:focus + .btn-success, .btn-success:focus { - color: #000; - background-color: #84b87a; - border-color: #7db373; - box-shadow: 0 0 0 0.25rem rgba(94, 145, 84, 0.5); +.nav-tabs .nav-link { + margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); + border: var(--bs-nav-tabs-border-width) solid transparent; + border-top-left-radius: var(--bs-nav-tabs-border-radius); + border-top-right-radius: var(--bs-nav-tabs-border-radius); } -.btn-check:checked + .btn-success, .btn-check:active + .btn-success, .btn-success:active, .btn-success.active, .show > .btn-success.dropdown-toggle { - color: #000; - background-color: #8bbc82; - border-color: #7db373; +.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + isolation: isolate; + border-color: var(--bs-nav-tabs-link-hover-border-color); } -.btn-check:checked + .btn-success:focus, .btn-check:active + .btn-success:focus, .btn-success:active:focus, .btn-success.active:focus, .show > .btn-success.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(94, 145, 84, 0.5); +.nav-tabs .nav-link.active, +.nav-tabs .nav-item.show .nav-link { + color: var(--bs-nav-tabs-link-active-color); + background-color: var(--bs-nav-tabs-link-active-bg); + border-color: var(--bs-nav-tabs-link-active-border-color); } -.btn-success:disabled, .btn-success.disabled { - color: #000; - background-color: #6EAB63; - border-color: #6EAB63; +.nav-tabs .dropdown-menu { + margin-top: calc(-1 * var(--bs-nav-tabs-border-width)); + border-top-left-radius: 0; + border-top-right-radius: 0; } -.btn-info { - color: #000; - background-color: #6DA3B7; - border-color: #6DA3B7; -} -.btn-info:hover { - color: #000; - background-color: #83b1c2; - border-color: #7cacbe; -} -.btn-check:focus + .btn-info, .btn-info:focus { - color: #000; - background-color: #83b1c2; - border-color: #7cacbe; - box-shadow: 0 0 0 0.25rem rgba(93, 139, 156, 0.5); -} -.btn-check:checked + .btn-info, .btn-check:active + .btn-info, .btn-info:active, .btn-info.active, .show > .btn-info.dropdown-toggle { - color: #000; - background-color: #8ab5c5; - border-color: #7cacbe; +.nav-pills { + --bs-nav-pills-border-radius: var(--bs-border-radius); + --bs-nav-pills-link-active-color: #fff; + --bs-nav-pills-link-active-bg: #8c68cd; } -.btn-check:checked + .btn-info:focus, .btn-check:active + .btn-info:focus, .btn-info:active:focus, .btn-info.active:focus, .show > .btn-info.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(93, 139, 156, 0.5); +.nav-pills .nav-link { + border-radius: var(--bs-nav-pills-border-radius); } -.btn-info:disabled, .btn-info.disabled { - color: #000; - background-color: #6DA3B7; - border-color: #6DA3B7; +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: var(--bs-nav-pills-link-active-color); + background-color: var(--bs-nav-pills-link-active-bg); } -.btn-warning { - color: #000; - background-color: #D5804C; - border-color: #D5804C; +.nav-underline { + --bs-nav-underline-gap: 1rem; + --bs-nav-underline-border-width: 0.125rem; + --bs-nav-underline-link-active-color: var(--bs-emphasis-color); + gap: var(--bs-nav-underline-gap); } -.btn-warning:hover { - color: #000; - background-color: #db9367; - border-color: #d98d5e; +.nav-underline .nav-link { + padding-right: 0; + padding-left: 0; + border-bottom: var(--bs-nav-underline-border-width) solid transparent; } -.btn-check:focus + .btn-warning, .btn-warning:focus { - color: #000; - background-color: #db9367; - border-color: #d98d5e; - box-shadow: 0 0 0 0.25rem rgba(181, 109, 65, 0.5); +.nav-underline .nav-link:hover, .nav-underline .nav-link:focus { + border-bottom-color: currentcolor; } -.btn-check:checked + .btn-warning, .btn-check:active + .btn-warning, .btn-warning:active, .btn-warning.active, .show > .btn-warning.dropdown-toggle { - color: #000; - background-color: #dd9970; - border-color: #d98d5e; +.nav-underline .nav-link.active, +.nav-underline .show > .nav-link { + font-weight: 700; + color: var(--bs-nav-underline-link-active-color); + border-bottom-color: currentcolor; } -.btn-check:checked + .btn-warning:focus, .btn-check:active + .btn-warning:focus, .btn-warning:active:focus, .btn-warning.active:focus, .show > .btn-warning.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(181, 109, 65, 0.5); + +.nav-fill > .nav-link, +.nav-fill .nav-item { + flex: 1 1 auto; + text-align: center; } -.btn-warning:disabled, .btn-warning.disabled { - color: #000; - background-color: #D5804C; - border-color: #D5804C; + +.nav-justified > .nav-link, +.nav-justified .nav-item { + flex-basis: 0; + flex-grow: 1; + text-align: center; } -.btn-danger { - color: #fff; - background-color: #C54C45; - border-color: #C54C45; +.nav-fill .nav-item .nav-link, +.nav-justified .nav-item .nav-link { + width: 100%; } -.btn-danger:hover { - color: #fff; - background-color: #a7413b; - border-color: #9e3d37; + +.tab-content > .tab-pane { + display: none; } -.btn-check:focus + .btn-danger, .btn-danger:focus { - color: #fff; - background-color: #a7413b; - border-color: #9e3d37; - box-shadow: 0 0 0 0.25rem rgba(206, 103, 97, 0.5); +.tab-content > .active { + display: block; } -.btn-check:checked + .btn-danger, .btn-check:active + .btn-danger, .btn-danger:active, .btn-danger.active, .show > .btn-danger.dropdown-toggle { - color: #fff; - background-color: #9e3d37; - border-color: #943934; + +.navbar { + --bs-navbar-padding-x: 0; + --bs-navbar-padding-y: 0.5rem; + --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65); + --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8); + --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3); + --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-brand-padding-y: 0.3125rem; + --bs-navbar-brand-margin-end: 1rem; + --bs-navbar-brand-font-size: 1.25rem; + --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-nav-link-padding-x: 0.5rem; + --bs-navbar-toggler-padding-y: 0.25rem; + --bs-navbar-toggler-padding-x: 0.75rem; + --bs-navbar-toggler-font-size: 1.25rem; + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15); + --bs-navbar-toggler-border-radius: var(--bs-border-radius); + --bs-navbar-toggler-focus-width: 0.25rem; + --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x); +} +.navbar > .container, +.navbar > .container-fluid, +.navbar > .container-sm, +.navbar > .container-md, +.navbar > .container-lg, +.navbar > .container-xl, +.navbar > .container-xxl { + display: flex; + flex-wrap: inherit; + align-items: center; + justify-content: space-between; } -.btn-check:checked + .btn-danger:focus, .btn-check:active + .btn-danger:focus, .btn-danger:active:focus, .btn-danger.active:focus, .show > .btn-danger.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(206, 103, 97, 0.5); +.navbar-brand { + padding-top: var(--bs-navbar-brand-padding-y); + padding-bottom: var(--bs-navbar-brand-padding-y); + margin-right: var(--bs-navbar-brand-margin-end); + font-size: var(--bs-navbar-brand-font-size); + color: var(--bs-navbar-brand-color); + text-decoration: none; + white-space: nowrap; } -.btn-danger:disabled, .btn-danger.disabled { - color: #fff; - background-color: #C54C45; - border-color: #C54C45; +.navbar-brand:hover, .navbar-brand:focus { + color: var(--bs-navbar-brand-hover-color); } -.btn-light { - color: #000; - background-color: #F5FAFF; - border-color: #F5FAFF; -} -.btn-light:hover { - color: #000; - background-color: #f7fbff; - border-color: #f6fbff; +.navbar-nav { + --bs-nav-link-padding-x: 0; + --bs-nav-link-padding-y: 0.5rem; + --bs-nav-link-font-weight: ; + --bs-nav-link-color: var(--bs-navbar-color); + --bs-nav-link-hover-color: var(--bs-navbar-hover-color); + --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color); + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; } -.btn-check:focus + .btn-light, .btn-light:focus { - color: #000; - background-color: #f7fbff; - border-color: #f6fbff; - box-shadow: 0 0 0 0.25rem rgba(208, 213, 217, 0.5); +.navbar-nav .nav-link.active, .navbar-nav .nav-link.show { + color: var(--bs-navbar-active-color); } -.btn-check:checked + .btn-light, .btn-check:active + .btn-light, .btn-light:active, .btn-light.active, .show > .btn-light.dropdown-toggle { - color: #000; - background-color: #f7fbff; - border-color: #f6fbff; +.navbar-nav .dropdown-menu { + position: static; } -.btn-check:checked + .btn-light:focus, .btn-check:active + .btn-light:focus, .btn-light:active:focus, .btn-light.active:focus, .show > .btn-light.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(208, 213, 217, 0.5); + +.navbar-text { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + color: var(--bs-navbar-color); } -.btn-light:disabled, .btn-light.disabled { - color: #000; - background-color: #F5FAFF; - border-color: #F5FAFF; +.navbar-text a, +.navbar-text a:hover, +.navbar-text a:focus { + color: var(--bs-navbar-active-color); } -.btn-dark { - color: #fff; - background-color: #30475D; - border-color: #30475D; -} -.btn-dark:hover { - color: #fff; - background-color: #293c4f; - border-color: #26394a; +.navbar-collapse { + flex-basis: 100%; + flex-grow: 1; + align-items: center; } -.btn-check:focus + .btn-dark, .btn-dark:focus { - color: #fff; - background-color: #293c4f; - border-color: #26394a; - box-shadow: 0 0 0 0.25rem rgba(79, 99, 117, 0.5); + +.navbar-toggler { + padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x); + font-size: var(--bs-navbar-toggler-font-size); + line-height: 1; + color: var(--bs-navbar-color); + background-color: transparent; + border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color); + border-radius: var(--bs-navbar-toggler-border-radius); + transition: var(--bs-navbar-toggler-transition); } -.btn-check:checked + .btn-dark, .btn-check:active + .btn-dark, .btn-dark:active, .btn-dark.active, .show > .btn-dark.dropdown-toggle { - color: #fff; - background-color: #26394a; - border-color: #243546; +@media (prefers-reduced-motion: reduce) { + .navbar-toggler { + transition: none; + } } -.btn-check:checked + .btn-dark:focus, .btn-check:active + .btn-dark:focus, .btn-dark:active:focus, .btn-dark.active:focus, .show > .btn-dark.dropdown-toggle:focus { - box-shadow: 0 0 0 0.25rem rgba(79, 99, 117, 0.5); +.navbar-toggler:hover { + text-decoration: none; } -.btn-dark:disabled, .btn-dark.disabled { - color: #fff; - background-color: #30475D; - border-color: #30475D; +.navbar-toggler:focus { + text-decoration: none; + outline: 0; + box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width); } -.btn-outline-primary { - color: #4276A7; - border-color: #4276A7; -} -.btn-outline-primary:hover { - color: #fff; - background-color: #4276A7; - border-color: #4276A7; +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + background-image: var(--bs-navbar-toggler-icon-bg); + background-repeat: no-repeat; + background-position: center; + background-size: 100%; } -.btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus { - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.5); + +.navbar-nav-scroll { + max-height: var(--bs-scroll-height, 75vh); + overflow-y: auto; } -.btn-check:checked + .btn-outline-primary, .btn-check:active + .btn-outline-primary, .btn-outline-primary:active, .btn-outline-primary.active, .btn-outline-primary.dropdown-toggle.show { - color: #fff; - background-color: #4276A7; - border-color: #4276A7; + +@media (min-width: 576px) { + .navbar-expand-sm { + flex-wrap: nowrap; + justify-content: flex-start; + } + .navbar-expand-sm .navbar-nav { + flex-direction: row; + } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + .navbar-expand-sm .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-sm .navbar-toggler { + display: none; + } + .navbar-expand-sm .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + .navbar-expand-sm .offcanvas .offcanvas-header { + display: none; + } + .navbar-expand-sm .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } -.btn-check:checked + .btn-outline-primary:focus, .btn-check:active + .btn-outline-primary:focus, .btn-outline-primary:active:focus, .btn-outline-primary.active:focus, .btn-outline-primary.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.5); +@media (min-width: 768px) { + .navbar-expand-md { + flex-wrap: nowrap; + justify-content: flex-start; + } + .navbar-expand-md .navbar-nav { + flex-direction: row; + } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + .navbar-expand-md .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-md .navbar-toggler { + display: none; + } + .navbar-expand-md .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + .navbar-expand-md .offcanvas .offcanvas-header { + display: none; + } + .navbar-expand-md .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } -.btn-outline-primary:disabled, .btn-outline-primary.disabled { - color: #4276A7; - background-color: transparent; +@media (min-width: 992px) { + .navbar-expand-lg { + flex-wrap: nowrap; + justify-content: flex-start; + } + .navbar-expand-lg .navbar-nav { + flex-direction: row; + } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + .navbar-expand-lg .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-lg .navbar-toggler { + display: none; + } + .navbar-expand-lg .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + .navbar-expand-lg .offcanvas .offcanvas-header { + display: none; + } + .navbar-expand-lg .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } - -.btn-outline-secondary { - color: #6c757d; - border-color: #6c757d; +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-wrap: nowrap; + justify-content: flex-start; + } + .navbar-expand-xl .navbar-nav { + flex-direction: row; + } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + .navbar-expand-xl .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-xl .navbar-toggler { + display: none; + } + .navbar-expand-xl .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + .navbar-expand-xl .offcanvas .offcanvas-header { + display: none; + } + .navbar-expand-xl .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } -.btn-outline-secondary:hover { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; +@media (min-width: 1400px) { + .navbar-expand-xxl { + flex-wrap: nowrap; + justify-content: flex-start; + } + .navbar-expand-xxl .navbar-nav { + flex-direction: row; + } + .navbar-expand-xxl .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-xxl .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); + } + .navbar-expand-xxl .navbar-nav-scroll { + overflow: visible; + } + .navbar-expand-xxl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-xxl .navbar-toggler { + display: none; + } + .navbar-expand-xxl .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; + } + .navbar-expand-xxl .offcanvas .offcanvas-header { + display: none; + } + .navbar-expand-xxl .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + } } -.btn-check:focus + .btn-outline-secondary, .btn-outline-secondary:focus { - box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5); +.navbar-expand { + flex-wrap: nowrap; + justify-content: flex-start; } -.btn-check:checked + .btn-outline-secondary, .btn-check:active + .btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show { - color: #fff; - background-color: #6c757d; - border-color: #6c757d; +.navbar-expand .navbar-nav { + flex-direction: row; } -.btn-check:checked + .btn-outline-secondary:focus, .btn-check:active + .btn-outline-secondary:focus, .btn-outline-secondary:active:focus, .btn-outline-secondary.active:focus, .btn-outline-secondary.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5); +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; } -.btn-outline-secondary:disabled, .btn-outline-secondary.disabled { - color: #6c757d; - background-color: transparent; +.navbar-expand .navbar-nav .nav-link { + padding-right: var(--bs-navbar-nav-link-padding-x); + padding-left: var(--bs-navbar-nav-link-padding-x); } - -.btn-outline-success { - color: #6EAB63; - border-color: #6EAB63; +.navbar-expand .navbar-nav-scroll { + overflow: visible; } -.btn-outline-success:hover { - color: #000; - background-color: #6EAB63; - border-color: #6EAB63; +.navbar-expand .navbar-collapse { + display: flex !important; + flex-basis: auto; } -.btn-check:focus + .btn-outline-success, .btn-outline-success:focus { - box-shadow: 0 0 0 0.25rem rgba(110, 171, 99, 0.5); +.navbar-expand .navbar-toggler { + display: none; } -.btn-check:checked + .btn-outline-success, .btn-check:active + .btn-outline-success, .btn-outline-success:active, .btn-outline-success.active, .btn-outline-success.dropdown-toggle.show { - color: #000; - background-color: #6EAB63; - border-color: #6EAB63; +.navbar-expand .offcanvas { + position: static; + z-index: auto; + flex-grow: 1; + width: auto !important; + height: auto !important; + visibility: visible !important; + background-color: transparent !important; + border: 0 !important; + transform: none !important; + transition: none; } -.btn-check:checked + .btn-outline-success:focus, .btn-check:active + .btn-outline-success:focus, .btn-outline-success:active:focus, .btn-outline-success.active:focus, .btn-outline-success.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(110, 171, 99, 0.5); +.navbar-expand .offcanvas .offcanvas-header { + display: none; } -.btn-outline-success:disabled, .btn-outline-success.disabled { - color: #6EAB63; - background-color: transparent; +.navbar-expand .offcanvas .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; } -.btn-outline-info { - color: #6DA3B7; - border-color: #6DA3B7; -} -.btn-outline-info:hover { - color: #000; - background-color: #6DA3B7; - border-color: #6DA3B7; -} -.btn-check:focus + .btn-outline-info, .btn-outline-info:focus { - box-shadow: 0 0 0 0.25rem rgba(109, 163, 183, 0.5); -} -.btn-check:checked + .btn-outline-info, .btn-check:active + .btn-outline-info, .btn-outline-info:active, .btn-outline-info.active, .btn-outline-info.dropdown-toggle.show { - color: #000; - background-color: #6DA3B7; - border-color: #6DA3B7; -} -.btn-check:checked + .btn-outline-info:focus, .btn-check:active + .btn-outline-info:focus, .btn-outline-info:active:focus, .btn-outline-info.active:focus, .btn-outline-info.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(109, 163, 183, 0.5); -} -.btn-outline-info:disabled, .btn-outline-info.disabled { - color: #6DA3B7; - background-color: transparent; +.navbar-dark, +.navbar[data-bs-theme=dark] { + --bs-navbar-color: rgba(255, 255, 255, 0.55); + --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); + --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); + --bs-navbar-active-color: #fff; + --bs-navbar-brand-color: #fff; + --bs-navbar-brand-hover-color: #fff; + --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1); + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } -.btn-outline-warning { - color: #D5804C; - border-color: #D5804C; -} -.btn-outline-warning:hover { - color: #000; - background-color: #D5804C; - border-color: #D5804C; -} -.btn-check:focus + .btn-outline-warning, .btn-outline-warning:focus { - box-shadow: 0 0 0 0.25rem rgba(213, 128, 76, 0.5); -} -.btn-check:checked + .btn-outline-warning, .btn-check:active + .btn-outline-warning, .btn-outline-warning:active, .btn-outline-warning.active, .btn-outline-warning.dropdown-toggle.show { - color: #000; - background-color: #D5804C; - border-color: #D5804C; -} -.btn-check:checked + .btn-outline-warning:focus, .btn-check:active + .btn-outline-warning:focus, .btn-outline-warning:active:focus, .btn-outline-warning.active:focus, .btn-outline-warning.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(213, 128, 76, 0.5); -} -.btn-outline-warning:disabled, .btn-outline-warning.disabled { - color: #D5804C; - background-color: transparent; +[data-bs-theme=dark] .navbar-toggler-icon { + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } -.btn-outline-danger { - color: #C54C45; - border-color: #C54C45; +.card { + --bs-card-spacer-y: 1rem; + --bs-card-spacer-x: 1rem; + --bs-card-title-spacer-y: 0.5rem; + --bs-card-title-color: ; + --bs-card-subtitle-color: ; + --bs-card-border-width: var(--bs-border-width); + --bs-card-border-color: var(--bs-border-color-translucent); + --bs-card-border-radius: var(--bs-border-radius); + --bs-card-box-shadow: ; + --bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); + --bs-card-cap-padding-y: 0.5rem; + --bs-card-cap-padding-x: 1rem; + --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03); + --bs-card-cap-color: ; + --bs-card-height: ; + --bs-card-color: ; + --bs-card-bg: var(--bs-body-bg); + --bs-card-img-overlay-padding: 1rem; + --bs-card-group-margin: 0.75rem; + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + height: var(--bs-card-height); + color: var(--bs-body-color); + word-wrap: break-word; + background-color: var(--bs-card-bg); + background-clip: border-box; + border: var(--bs-card-border-width) solid var(--bs-card-border-color); + border-radius: var(--bs-card-border-radius); } -.btn-outline-danger:hover { - color: #fff; - background-color: #C54C45; - border-color: #C54C45; +.card > hr { + margin-right: 0; + margin-left: 0; } -.btn-check:focus + .btn-outline-danger, .btn-outline-danger:focus { - box-shadow: 0 0 0 0.25rem rgba(197, 76, 69, 0.5); +.card > .list-group { + border-top: inherit; + border-bottom: inherit; } -.btn-check:checked + .btn-outline-danger, .btn-check:active + .btn-outline-danger, .btn-outline-danger:active, .btn-outline-danger.active, .btn-outline-danger.dropdown-toggle.show { - color: #fff; - background-color: #C54C45; - border-color: #C54C45; +.card > .list-group:first-child { + border-top-width: 0; + border-top-left-radius: var(--bs-card-inner-border-radius); + border-top-right-radius: var(--bs-card-inner-border-radius); } -.btn-check:checked + .btn-outline-danger:focus, .btn-check:active + .btn-outline-danger:focus, .btn-outline-danger:active:focus, .btn-outline-danger.active:focus, .btn-outline-danger.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(197, 76, 69, 0.5); +.card > .list-group:last-child { + border-bottom-width: 0; + border-bottom-right-radius: var(--bs-card-inner-border-radius); + border-bottom-left-radius: var(--bs-card-inner-border-radius); } -.btn-outline-danger:disabled, .btn-outline-danger.disabled { - color: #C54C45; - background-color: transparent; +.card > .card-header + .list-group, +.card > .list-group + .card-footer { + border-top: 0; } -.btn-outline-light { - color: #F5FAFF; - border-color: #F5FAFF; -} -.btn-outline-light:hover { - color: #000; - background-color: #F5FAFF; - border-color: #F5FAFF; -} -.btn-check:focus + .btn-outline-light, .btn-outline-light:focus { - box-shadow: 0 0 0 0.25rem rgba(245, 250, 255, 0.5); -} -.btn-check:checked + .btn-outline-light, .btn-check:active + .btn-outline-light, .btn-outline-light:active, .btn-outline-light.active, .btn-outline-light.dropdown-toggle.show { - color: #000; - background-color: #F5FAFF; - border-color: #F5FAFF; -} -.btn-check:checked + .btn-outline-light:focus, .btn-check:active + .btn-outline-light:focus, .btn-outline-light:active:focus, .btn-outline-light.active:focus, .btn-outline-light.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(245, 250, 255, 0.5); -} -.btn-outline-light:disabled, .btn-outline-light.disabled { - color: #F5FAFF; - background-color: transparent; +.card-body { + flex: 1 1 auto; + padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x); + color: var(--bs-card-color); } -.btn-outline-dark { - color: #30475D; - border-color: #30475D; -} -.btn-outline-dark:hover { - color: #fff; - background-color: #30475D; - border-color: #30475D; -} -.btn-check:focus + .btn-outline-dark, .btn-outline-dark:focus { - box-shadow: 0 0 0 0.25rem rgba(48, 71, 93, 0.5); -} -.btn-check:checked + .btn-outline-dark, .btn-check:active + .btn-outline-dark, .btn-outline-dark:active, .btn-outline-dark.active, .btn-outline-dark.dropdown-toggle.show { - color: #fff; - background-color: #30475D; - border-color: #30475D; -} -.btn-check:checked + .btn-outline-dark:focus, .btn-check:active + .btn-outline-dark:focus, .btn-outline-dark:active:focus, .btn-outline-dark.active:focus, .btn-outline-dark.dropdown-toggle.show:focus { - box-shadow: 0 0 0 0.25rem rgba(48, 71, 93, 0.5); -} -.btn-outline-dark:disabled, .btn-outline-dark.disabled { - color: #30475D; - background-color: transparent; +.card-title { + margin-bottom: var(--bs-card-title-spacer-y); + color: var(--bs-card-title-color); } -.btn-link { - font-weight: 400; - color: #4276A7; - text-decoration: underline; -} -.btn-link:hover { - color: #355e86; -} -.btn-link:disabled, .btn-link.disabled { - color: #6c757d; +.card-subtitle { + margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); + margin-bottom: 0; + color: var(--bs-card-subtitle-color); } -.btn-lg, .btn-group-lg > .btn { - padding: 0.5rem 1rem; - font-size: 1.25rem; - border-radius: 0.3rem; +.card-text:last-child { + margin-bottom: 0; } -.btn-sm, .btn-group-sm > .btn { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - border-radius: 0.2rem; +.card-link + .card-link { + margin-left: var(--bs-card-spacer-x); } -.fade { - transition: opacity 0.15s linear; -} -@media (prefers-reduced-motion: reduce) { - .fade { - transition: none; - } -} -.fade:not(.show) { - opacity: 0; +.card-header { + padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); + margin-bottom: 0; + color: var(--bs-card-cap-color); + background-color: var(--bs-card-cap-bg); + border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color); } - -.collapse:not(.show) { - display: none; +.card-header:first-child { + border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0; } -.collapsing { - height: 0; - overflow: hidden; - transition: height 0.35s ease; -} -@media (prefers-reduced-motion: reduce) { - .collapsing { - transition: none; - } -} -.collapsing.collapse-horizontal { - width: 0; - height: auto; - transition: width 0.35s ease; -} -@media (prefers-reduced-motion: reduce) { - .collapsing.collapse-horizontal { - transition: none; - } +.card-footer { + padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x); + color: var(--bs-card-cap-color); + background-color: var(--bs-card-cap-bg); + border-top: var(--bs-card-border-width) solid var(--bs-card-border-color); } - -.dropup, -.dropend, -.dropdown, -.dropstart { - position: relative; +.card-footer:last-child { + border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius); } -.dropdown-toggle { - white-space: nowrap; -} -.dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid; - border-right: 0.3em solid transparent; +.card-header-tabs { + margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-bottom: calc(-1 * var(--bs-card-cap-padding-y)); + margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); border-bottom: 0; - border-left: 0.3em solid transparent; } -.dropdown-toggle:empty::after { - margin-left: 0; +.card-header-tabs .nav-link.active { + background-color: var(--bs-card-bg); + border-bottom-color: var(--bs-card-bg); } -.dropdown-menu { - position: absolute; - z-index: 1000; - display: none; - min-width: 10rem; - padding: 0.5rem 0; - margin: 0; - font-size: 1rem; - color: #00305E; - text-align: left; - list-style: none; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25rem; +.card-header-pills { + margin-right: calc(-0.5 * var(--bs-card-cap-padding-x)); + margin-left: calc(-0.5 * var(--bs-card-cap-padding-x)); } -.dropdown-menu[data-bs-popper] { - top: 100%; + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; left: 0; - margin-top: 0.125rem; + padding: var(--bs-card-img-overlay-padding); + border-radius: var(--bs-card-inner-border-radius); } -.dropdown-menu-start { - --bs-position: start; -} -.dropdown-menu-start[data-bs-popper] { - right: auto; - left: 0; +.card-img, +.card-img-top, +.card-img-bottom { + width: 100%; } -.dropdown-menu-end { - --bs-position: end; +.card-img, +.card-img-top { + border-top-left-radius: var(--bs-card-inner-border-radius); + border-top-right-radius: var(--bs-card-inner-border-radius); } -.dropdown-menu-end[data-bs-popper] { - right: 0; - left: auto; + +.card-img, +.card-img-bottom { + border-bottom-right-radius: var(--bs-card-inner-border-radius); + border-bottom-left-radius: var(--bs-card-inner-border-radius); } +.card-group > .card { + margin-bottom: var(--bs-card-group-margin); +} @media (min-width: 576px) { - .dropdown-menu-sm-start { - --bs-position: start; - } - .dropdown-menu-sm-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-sm-end { - --bs-position: end; - } - .dropdown-menu-sm-end[data-bs-popper] { - right: 0; - left: auto; + .card-group { + display: flex; + flex-flow: row wrap; } -} -@media (min-width: 768px) { - .dropdown-menu-md-start { - --bs-position: start; + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; } - .dropdown-menu-md-start[data-bs-popper] { - right: auto; - left: 0; + .card-group > .card + .card { + margin-left: 0; + border-left: 0; } - .dropdown-menu-md-end { - --bs-position: end; + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } - .dropdown-menu-md-end[data-bs-popper] { - right: 0; - left: auto; + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; } -} -@media (min-width: 992px) { - .dropdown-menu-lg-start { - --bs-position: start; + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; } - .dropdown-menu-lg-start[data-bs-popper] { - right: auto; - left: 0; + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } - .dropdown-menu-lg-end { - --bs-position: end; + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; } - .dropdown-menu-lg-end[data-bs-popper] { - right: 0; - left: auto; + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; } } -@media (min-width: 1200px) { - .dropdown-menu-xl-start { - --bs-position: start; - } - .dropdown-menu-xl-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-xl-end { - --bs-position: end; - } - .dropdown-menu-xl-end[data-bs-popper] { - right: 0; - left: auto; - } + +.accordion { + --bs-accordion-color: var(--bs-body-color); + --bs-accordion-bg: var(--bs-body-bg); + --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; + --bs-accordion-border-color: var(--bs-border-color); + --bs-accordion-border-width: var(--bs-border-width); + --bs-accordion-border-radius: var(--bs-border-radius); + --bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); + --bs-accordion-btn-padding-x: 1.25rem; + --bs-accordion-btn-padding-y: 1rem; + --bs-accordion-btn-color: var(--bs-body-color); + --bs-accordion-btn-bg: var(--bs-accordion-bg); + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-icon-width: 1.25rem; + --bs-accordion-btn-icon-transform: rotate(-180deg); + --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23382a52' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(140, 104, 205, 0.25); + --bs-accordion-body-padding-x: 1.25rem; + --bs-accordion-body-padding-y: 1rem; + --bs-accordion-active-color: var(--bs-primary-text-emphasis); + --bs-accordion-active-bg: var(--bs-primary-bg-subtle); } -@media (min-width: 1400px) { - .dropdown-menu-xxl-start { - --bs-position: start; - } - .dropdown-menu-xxl-start[data-bs-popper] { - right: auto; - left: 0; - } - .dropdown-menu-xxl-end { - --bs-position: end; - } - .dropdown-menu-xxl-end[data-bs-popper] { - right: 0; - left: auto; + +.accordion-button { + position: relative; + display: flex; + align-items: center; + width: 100%; + padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x); + font-size: 1rem; + color: var(--bs-accordion-btn-color); + text-align: left; + background-color: var(--bs-accordion-btn-bg); + border: 0; + border-radius: 0; + overflow-anchor: none; + transition: var(--bs-accordion-transition); +} +@media (prefers-reduced-motion: reduce) { + .accordion-button { + transition: none; } } -.dropup .dropdown-menu[data-bs-popper] { - top: auto; - bottom: 100%; - margin-top: 0; - margin-bottom: 0.125rem; +.accordion-button:not(.collapsed) { + color: var(--bs-accordion-active-color); + background-color: var(--bs-accordion-active-bg); + box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color); } -.dropup .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; +.accordion-button:not(.collapsed)::after { + background-image: var(--bs-accordion-btn-active-icon); + transform: var(--bs-accordion-btn-icon-transform); +} +.accordion-button::after { + flex-shrink: 0; + width: var(--bs-accordion-btn-icon-width); + height: var(--bs-accordion-btn-icon-width); + margin-left: auto; content: ""; + background-image: var(--bs-accordion-btn-icon); + background-repeat: no-repeat; + background-size: var(--bs-accordion-btn-icon-width); + transition: var(--bs-accordion-btn-icon-transition); +} +@media (prefers-reduced-motion: reduce) { + .accordion-button::after { + transition: none; + } +} +.accordion-button:hover { + z-index: 2; +} +.accordion-button:focus { + z-index: 3; + outline: 0; + box-shadow: var(--bs-accordion-btn-focus-box-shadow); +} + +.accordion-header { + margin-bottom: 0; +} + +.accordion-item { + color: var(--bs-accordion-color); + background-color: var(--bs-accordion-bg); + border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color); +} +.accordion-item:first-of-type { + border-top-left-radius: var(--bs-accordion-border-radius); + border-top-right-radius: var(--bs-accordion-border-radius); +} +.accordion-item:first-of-type > .accordion-header .accordion-button { + border-top-left-radius: var(--bs-accordion-inner-border-radius); + border-top-right-radius: var(--bs-accordion-inner-border-radius); +} +.accordion-item:not(:first-of-type) { border-top: 0; - border-right: 0.3em solid transparent; - border-bottom: 0.3em solid; - border-left: 0.3em solid transparent; } -.dropup .dropdown-toggle:empty::after { - margin-left: 0; +.accordion-item:last-of-type { + border-bottom-right-radius: var(--bs-accordion-border-radius); + border-bottom-left-radius: var(--bs-accordion-border-radius); +} +.accordion-item:last-of-type > .accordion-header .accordion-button.collapsed { + border-bottom-right-radius: var(--bs-accordion-inner-border-radius); + border-bottom-left-radius: var(--bs-accordion-inner-border-radius); +} +.accordion-item:last-of-type > .accordion-collapse { + border-bottom-right-radius: var(--bs-accordion-border-radius); + border-bottom-left-radius: var(--bs-accordion-border-radius); } -.dropend .dropdown-menu[data-bs-popper] { - top: 0; - right: auto; - left: 100%; - margin-top: 0; - margin-left: 0.125rem; +.accordion-body { + padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x); } -.dropend .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; + +.accordion-flush > .accordion-item { border-right: 0; - border-bottom: 0.3em solid transparent; - border-left: 0.3em solid; + border-left: 0; + border-radius: 0; } -.dropend .dropdown-toggle:empty::after { - margin-left: 0; +.accordion-flush > .accordion-item:first-child { + border-top: 0; } -.dropend .dropdown-toggle::after { - vertical-align: 0; +.accordion-flush > .accordion-item:last-child { + border-bottom: 0; } - -.dropstart .dropdown-menu[data-bs-popper] { - top: 0; - right: 100%; - left: auto; - margin-top: 0; - margin-right: 0.125rem; +.accordion-flush > .accordion-item > .accordion-header .accordion-button, .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed { + border-radius: 0; } -.dropstart .dropdown-toggle::after { - display: inline-block; - margin-left: 0.255em; - vertical-align: 0.255em; - content: ""; +.accordion-flush > .accordion-item > .accordion-collapse { + border-radius: 0; } -.dropstart .dropdown-toggle::after { - display: none; + +[data-bs-theme=dark] .accordion-button::after { + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23baa4e1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23baa4e1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } -.dropstart .dropdown-toggle::before { - display: inline-block; - margin-right: 0.255em; - vertical-align: 0.255em; - content: ""; - border-top: 0.3em solid transparent; - border-right: 0.3em solid; - border-bottom: 0.3em solid transparent; + +.breadcrumb { + --bs-breadcrumb-padding-x: 0; + --bs-breadcrumb-padding-y: 0; + --bs-breadcrumb-margin-bottom: 1rem; + --bs-breadcrumb-bg: ; + --bs-breadcrumb-border-radius: ; + --bs-breadcrumb-divider-color: var(--bs-secondary-color); + --bs-breadcrumb-item-padding-x: 0.5rem; + --bs-breadcrumb-item-active-color: var(--bs-secondary-color); + display: flex; + flex-wrap: wrap; + padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); + margin-bottom: var(--bs-breadcrumb-margin-bottom); + font-size: var(--bs-breadcrumb-font-size); + list-style: none; + background-color: var(--bs-breadcrumb-bg); + border-radius: var(--bs-breadcrumb-border-radius); } -.dropstart .dropdown-toggle:empty::after { - margin-left: 0; + +.breadcrumb-item + .breadcrumb-item { + padding-left: var(--bs-breadcrumb-item-padding-x); } -.dropstart .dropdown-toggle::before { - vertical-align: 0; +.breadcrumb-item + .breadcrumb-item::before { + float: left; + padding-right: var(--bs-breadcrumb-item-padding-x); + color: var(--bs-breadcrumb-divider-color); + content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; +} +.breadcrumb-item.active { + color: var(--bs-breadcrumb-item-active-color); } -.dropdown-divider { - height: 0; - margin: 0.5rem 0; - overflow: hidden; - border-top: 1px solid rgba(0, 0, 0, 0.15); +.pagination { + --bs-pagination-padding-x: 0.75rem; + --bs-pagination-padding-y: 0.375rem; + --bs-pagination-font-size: 1rem; + --bs-pagination-color: var(--bs-link-color); + --bs-pagination-bg: var(--bs-body-bg); + --bs-pagination-border-width: var(--bs-border-width); + --bs-pagination-border-color: var(--bs-border-color); + --bs-pagination-border-radius: var(--bs-border-radius); + --bs-pagination-hover-color: var(--bs-link-hover-color); + --bs-pagination-hover-bg: var(--bs-tertiary-bg); + --bs-pagination-hover-border-color: var(--bs-border-color); + --bs-pagination-focus-color: var(--bs-link-hover-color); + --bs-pagination-focus-bg: var(--bs-secondary-bg); + --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(140, 104, 205, 0.25); + --bs-pagination-active-color: #fff; + --bs-pagination-active-bg: #8c68cd; + --bs-pagination-active-border-color: #8c68cd; + --bs-pagination-disabled-color: var(--bs-secondary-color); + --bs-pagination-disabled-bg: var(--bs-secondary-bg); + --bs-pagination-disabled-border-color: var(--bs-border-color); + display: flex; + padding-left: 0; + list-style: none; } -.dropdown-item { +.page-link { + position: relative; display: block; - width: 100%; - padding: 0.25rem 1rem; - clear: both; - font-weight: 400; - color: #212529; - text-align: inherit; + padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x); + font-size: var(--bs-pagination-font-size); + color: var(--bs-pagination-color); text-decoration: none; - white-space: nowrap; - background-color: transparent; - border: 0; -} -.dropdown-item:hover, .dropdown-item:focus { - color: #1e2125; - background-color: #e9ecef; + background-color: var(--bs-pagination-bg); + border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color); + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } -.dropdown-item.active, .dropdown-item:active { - color: #fff; - text-decoration: none; - background-color: #4276A7; +@media (prefers-reduced-motion: reduce) { + .page-link { + transition: none; + } } -.dropdown-item.disabled, .dropdown-item:disabled { - color: #adb5bd; - pointer-events: none; - background-color: transparent; +.page-link:hover { + z-index: 2; + color: var(--bs-pagination-hover-color); + background-color: var(--bs-pagination-hover-bg); + border-color: var(--bs-pagination-hover-border-color); } - -.dropdown-menu.show { - display: block; +.page-link:focus { + z-index: 3; + color: var(--bs-pagination-focus-color); + background-color: var(--bs-pagination-focus-bg); + outline: 0; + box-shadow: var(--bs-pagination-focus-box-shadow); } - -.dropdown-header { - display: block; - padding: 0.5rem 1rem; - margin-bottom: 0; - font-size: 0.875rem; - color: #6c757d; - white-space: nowrap; +.page-link.active, .active > .page-link { + z-index: 3; + color: var(--bs-pagination-active-color); + background-color: var(--bs-pagination-active-bg); + border-color: var(--bs-pagination-active-border-color); } - -.dropdown-item-text { - display: block; - padding: 0.25rem 1rem; - color: #212529; +.page-link.disabled, .disabled > .page-link { + color: var(--bs-pagination-disabled-color); + pointer-events: none; + background-color: var(--bs-pagination-disabled-bg); + border-color: var(--bs-pagination-disabled-border-color); } -.dropdown-menu-dark { - color: #dee2e6; - background-color: #343a40; - border-color: rgba(0, 0, 0, 0.15); -} -.dropdown-menu-dark .dropdown-item { - color: #dee2e6; +.page-item:not(:first-child) .page-link { + margin-left: calc(var(--bs-border-width) * -1); } -.dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus { - color: #fff; - background-color: rgba(255, 255, 255, 0.15); +.page-item:first-child .page-link { + border-top-left-radius: var(--bs-pagination-border-radius); + border-bottom-left-radius: var(--bs-pagination-border-radius); } -.dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active { - color: #fff; - background-color: #4276A7; +.page-item:last-child .page-link { + border-top-right-radius: var(--bs-pagination-border-radius); + border-bottom-right-radius: var(--bs-pagination-border-radius); } -.dropdown-menu-dark .dropdown-item.disabled, .dropdown-menu-dark .dropdown-item:disabled { - color: #adb5bd; + +.pagination-lg { + --bs-pagination-padding-x: 1.5rem; + --bs-pagination-padding-y: 0.75rem; + --bs-pagination-font-size: 1.25rem; + --bs-pagination-border-radius: var(--bs-border-radius-lg); } -.dropdown-menu-dark .dropdown-divider { - border-color: rgba(0, 0, 0, 0.15); + +.pagination-sm { + --bs-pagination-padding-x: 0.5rem; + --bs-pagination-padding-y: 0.25rem; + --bs-pagination-font-size: 0.875rem; + --bs-pagination-border-radius: var(--bs-border-radius-sm); } -.dropdown-menu-dark .dropdown-item-text { - color: #dee2e6; + +.badge { + --bs-badge-padding-x: 0.65em; + --bs-badge-padding-y: 0.35em; + --bs-badge-font-size: 0.75em; + --bs-badge-font-weight: 700; + --bs-badge-color: #fff; + --bs-badge-border-radius: var(--bs-border-radius); + display: inline-block; + padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); + font-size: var(--bs-badge-font-size); + font-weight: var(--bs-badge-font-weight); + line-height: 1; + color: var(--bs-badge-color); + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: var(--bs-badge-border-radius); } -.dropdown-menu-dark .dropdown-header { - color: #adb5bd; +.badge:empty { + display: none; } -.btn-group, -.btn-group-vertical { +.btn .badge { position: relative; - display: inline-flex; - vertical-align: middle; + top: -1px; } -.btn-group > .btn, -.btn-group-vertical > .btn { + +.alert { + --bs-alert-bg: transparent; + --bs-alert-padding-x: 1rem; + --bs-alert-padding-y: 1rem; + --bs-alert-margin-bottom: 1rem; + --bs-alert-color: inherit; + --bs-alert-border-color: transparent; + --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color); + --bs-alert-border-radius: var(--bs-border-radius); + --bs-alert-link-color: inherit; position: relative; - flex: 1 1 auto; + padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); + margin-bottom: var(--bs-alert-margin-bottom); + color: var(--bs-alert-color); + background-color: var(--bs-alert-bg); + border: var(--bs-alert-border); + border-radius: var(--bs-alert-border-radius); } -.btn-group > .btn-check:checked + .btn, -.btn-group > .btn-check:focus + .btn, -.btn-group > .btn:hover, -.btn-group > .btn:focus, -.btn-group > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn-check:checked + .btn, -.btn-group-vertical > .btn-check:focus + .btn, -.btn-group-vertical > .btn:hover, -.btn-group-vertical > .btn:focus, -.btn-group-vertical > .btn:active, -.btn-group-vertical > .btn.active { - z-index: 1; + +.alert-heading { + color: inherit; } -.btn-toolbar { - display: flex; - flex-wrap: wrap; - justify-content: flex-start; +.alert-link { + font-weight: 700; + color: var(--bs-alert-link-color); } -.btn-toolbar .input-group { - width: auto; + +.alert-dismissible { + padding-right: 3rem; +} +.alert-dismissible .btn-close { + position: absolute; + top: 0; + right: 0; + z-index: 2; + padding: 1.25rem 1rem; } -.btn-group > .btn:not(:first-child), -.btn-group > .btn-group:not(:first-child) { - margin-left: -1px; +.alert-primary { + --bs-alert-color: var(--bs-primary-text-emphasis); + --bs-alert-bg: var(--bs-primary-bg-subtle); + --bs-alert-border-color: var(--bs-primary-border-subtle); + --bs-alert-link-color: var(--bs-primary-text-emphasis); } -.btn-group > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; + +.alert-secondary { + --bs-alert-color: var(--bs-secondary-text-emphasis); + --bs-alert-bg: var(--bs-secondary-bg-subtle); + --bs-alert-border-color: var(--bs-secondary-border-subtle); + --bs-alert-link-color: var(--bs-secondary-text-emphasis); } -.btn-group > .btn:nth-child(n+3), -.btn-group > :not(.btn-check) + .btn, -.btn-group > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; + +.alert-success { + --bs-alert-color: var(--bs-success-text-emphasis); + --bs-alert-bg: var(--bs-success-bg-subtle); + --bs-alert-border-color: var(--bs-success-border-subtle); + --bs-alert-link-color: var(--bs-success-text-emphasis); } -.dropdown-toggle-split { - padding-right: 0.5625rem; - padding-left: 0.5625rem; +.alert-info { + --bs-alert-color: var(--bs-info-text-emphasis); + --bs-alert-bg: var(--bs-info-bg-subtle); + --bs-alert-border-color: var(--bs-info-border-subtle); + --bs-alert-link-color: var(--bs-info-text-emphasis); } -.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after { - margin-left: 0; + +.alert-warning { + --bs-alert-color: var(--bs-warning-text-emphasis); + --bs-alert-bg: var(--bs-warning-bg-subtle); + --bs-alert-border-color: var(--bs-warning-border-subtle); + --bs-alert-link-color: var(--bs-warning-text-emphasis); } -.dropstart .dropdown-toggle-split::before { - margin-right: 0; + +.alert-danger { + --bs-alert-color: var(--bs-danger-text-emphasis); + --bs-alert-bg: var(--bs-danger-bg-subtle); + --bs-alert-border-color: var(--bs-danger-border-subtle); + --bs-alert-link-color: var(--bs-danger-text-emphasis); } -.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { - padding-right: 0.375rem; - padding-left: 0.375rem; +.alert-light { + --bs-alert-color: var(--bs-light-text-emphasis); + --bs-alert-bg: var(--bs-light-bg-subtle); + --bs-alert-border-color: var(--bs-light-border-subtle); + --bs-alert-link-color: var(--bs-light-text-emphasis); } -.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { - padding-right: 0.75rem; - padding-left: 0.75rem; +.alert-dark { + --bs-alert-color: var(--bs-dark-text-emphasis); + --bs-alert-bg: var(--bs-dark-bg-subtle); + --bs-alert-border-color: var(--bs-dark-border-subtle); + --bs-alert-link-color: var(--bs-dark-text-emphasis); } -.btn-group-vertical { - flex-direction: column; - align-items: flex-start; - justify-content: center; +.alert-accent { + --bs-alert-color: var(--bs-accent-text-emphasis); + --bs-alert-bg: var(--bs-accent-bg-subtle); + --bs-alert-border-color: var(--bs-accent-border-subtle); + --bs-alert-link-color: var(--bs-accent-text-emphasis); } -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group { - width: 100%; + +.alert-cinereous { + --bs-alert-color: var(--bs-cinereous-text-emphasis); + --bs-alert-bg: var(--bs-cinereous-bg-subtle); + --bs-alert-border-color: var(--bs-cinereous-border-subtle); + --bs-alert-link-color: var(--bs-cinereous-text-emphasis); } -.btn-group-vertical > .btn:not(:first-child), -.btn-group-vertical > .btn-group:not(:first-child) { - margin-top: -1px; + +.alert-verdigris { + --bs-alert-color: var(--bs-verdigris-text-emphasis); + --bs-alert-bg: var(--bs-verdigris-bg-subtle); + --bs-alert-border-color: var(--bs-verdigris-border-subtle); + --bs-alert-link-color: var(--bs-verdigris-text-emphasis); } -.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), -.btn-group-vertical > .btn-group:not(:last-child) > .btn { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; + +.alert-icterine { + --bs-alert-color: var(--bs-icterine-text-emphasis); + --bs-alert-bg: var(--bs-icterine-bg-subtle); + --bs-alert-border-color: var(--bs-icterine-border-subtle); + --bs-alert-link-color: var(--bs-icterine-text-emphasis); } -.btn-group-vertical > .btn ~ .btn, -.btn-group-vertical > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-top-right-radius: 0; + +.alert-mute { + --bs-alert-color: var(--bs-mute-text-emphasis); + --bs-alert-bg: var(--bs-mute-bg-subtle); + --bs-alert-border-color: var(--bs-mute-border-subtle); + --bs-alert-link-color: var(--bs-mute-text-emphasis); } -.nav { +@keyframes progress-bar-stripes { + 0% { + background-position-x: 1rem; + } +} +.progress, +.progress-stacked { + --bs-progress-height: 1rem; + --bs-progress-font-size: 0.75rem; + --bs-progress-bg: var(--bs-secondary-bg); + --bs-progress-border-radius: var(--bs-border-radius); + --bs-progress-box-shadow: var(--bs-box-shadow-inset); + --bs-progress-bar-color: #fff; + --bs-progress-bar-bg: #8c68cd; + --bs-progress-bar-transition: width 0.6s ease; display: flex; - flex-wrap: wrap; - padding-left: 0; - margin-bottom: 0; - list-style: none; + height: var(--bs-progress-height); + overflow: hidden; + font-size: var(--bs-progress-font-size); + background-color: var(--bs-progress-bg); + border-radius: var(--bs-progress-border-radius); } -.nav-link { - display: block; - padding: 0.5rem 1rem; - color: #4276A7; - text-decoration: none; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; +.progress-bar { + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + color: var(--bs-progress-bar-color); + text-align: center; + white-space: nowrap; + background-color: var(--bs-progress-bar-bg); + transition: var(--bs-progress-bar-transition); } @media (prefers-reduced-motion: reduce) { - .nav-link { + .progress-bar { transition: none; } } -.nav-link:hover, .nav-link:focus { - color: #355e86; -} -.nav-link.disabled { - color: #6c757d; - pointer-events: none; - cursor: default; -} -.nav-tabs { - border-bottom: 1px solid #dee2e6; -} -.nav-tabs .nav-link { - margin-bottom: -1px; - background: none; - border: 1px solid transparent; - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; -} -.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { - border-color: #e9ecef #e9ecef #dee2e6; - isolation: isolate; -} -.nav-tabs .nav-link.disabled { - color: #6c757d; - background-color: transparent; - border-color: transparent; -} -.nav-tabs .nav-link.active, -.nav-tabs .nav-item.show .nav-link { - color: #495057; - background-color: #fff; - border-color: #dee2e6 #dee2e6 #fff; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: var(--bs-progress-height) var(--bs-progress-height); } -.nav-pills .nav-link { - background: none; - border: 0; - border-radius: 0.25rem; -} -.nav-pills .nav-link.active, -.nav-pills .show > .nav-link { - color: #fff; - background-color: #4276A7; +.progress-stacked > .progress { + overflow: visible; } -.nav-fill > .nav-link, -.nav-fill .nav-item { - flex: 1 1 auto; - text-align: center; +.progress-stacked > .progress > .progress-bar { + width: 100%; } -.nav-justified > .nav-link, -.nav-justified .nav-item { - flex-basis: 0; - flex-grow: 1; - text-align: center; +.progress-bar-animated { + animation: 1s linear infinite progress-bar-stripes; +} +@media (prefers-reduced-motion: reduce) { + .progress-bar-animated { + animation: none; + } } -.nav-fill .nav-item .nav-link, -.nav-justified .nav-item .nav-link { - width: 100%; +.list-group { + --bs-list-group-color: var(--bs-body-color); + --bs-list-group-bg: var(--bs-body-bg); + --bs-list-group-border-color: var(--bs-border-color); + --bs-list-group-border-width: var(--bs-border-width); + --bs-list-group-border-radius: var(--bs-border-radius); + --bs-list-group-item-padding-x: 1rem; + --bs-list-group-item-padding-y: 0.5rem; + --bs-list-group-action-color: var(--bs-secondary-color); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-tertiary-bg); + --bs-list-group-action-active-color: var(--bs-body-color); + --bs-list-group-action-active-bg: var(--bs-secondary-bg); + --bs-list-group-disabled-color: var(--bs-secondary-color); + --bs-list-group-disabled-bg: var(--bs-body-bg); + --bs-list-group-active-color: #fff; + --bs-list-group-active-bg: #8c68cd; + --bs-list-group-active-border-color: #8c68cd; + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + border-radius: var(--bs-list-group-border-radius); } -.tab-content > .tab-pane { - display: none; +.list-group-numbered { + list-style-type: none; + counter-reset: section; } -.tab-content > .active { - display: block; +.list-group-numbered > .list-group-item::before { + content: counters(section, ".") ". "; + counter-increment: section; } -.navbar { - position: relative; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - padding-top: 0.5rem; - padding-bottom: 0.5rem; +.list-group-item-action { + width: 100%; + color: var(--bs-list-group-action-color); + text-align: inherit; } -.navbar > .container, -.navbar > .container-fluid, -.navbar > .container-sm, -.navbar > .container-md, -.navbar > .container-lg, -.navbar > .container-xl, -.navbar > .container-xxl { - display: flex; - flex-wrap: inherit; - align-items: center; - justify-content: space-between; +.list-group-item-action:hover, .list-group-item-action:focus { + z-index: 1; + color: var(--bs-list-group-action-hover-color); + text-decoration: none; + background-color: var(--bs-list-group-action-hover-bg); } -.navbar-brand { - padding-top: 0.3125rem; - padding-bottom: 0.3125rem; - margin-right: 1rem; - font-size: 1.25rem; +.list-group-item-action:active { + color: var(--bs-list-group-action-active-color); + background-color: var(--bs-list-group-action-active-bg); +} + +.list-group-item { + position: relative; + display: block; + padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x); + color: var(--bs-list-group-color); text-decoration: none; - white-space: nowrap; + background-color: var(--bs-list-group-bg); + border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color); } -.navbar-nav { - display: flex; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - list-style: none; +.list-group-item:first-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; } -.navbar-nav .nav-link { - padding-right: 0; - padding-left: 0; +.list-group-item:last-child { + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; } -.navbar-nav .dropdown-menu { - position: static; +.list-group-item.disabled, .list-group-item:disabled { + color: var(--bs-list-group-disabled-color); + pointer-events: none; + background-color: var(--bs-list-group-disabled-bg); } - -.navbar-text { - padding-top: 0.5rem; - padding-bottom: 0.5rem; +.list-group-item.active { + z-index: 2; + color: var(--bs-list-group-active-color); + background-color: var(--bs-list-group-active-bg); + border-color: var(--bs-list-group-active-border-color); } - -.navbar-collapse { - flex-basis: 100%; - flex-grow: 1; - align-items: center; +.list-group-item + .list-group-item { + border-top-width: 0; +} +.list-group-item + .list-group-item.active { + margin-top: calc(-1 * var(--bs-list-group-border-width)); + border-top-width: var(--bs-list-group-border-width); } -.navbar-toggler { - padding: 0.25rem 0.75rem; - font-size: 1.25rem; - line-height: 1; - background-color: transparent; - border: 1px solid transparent; - border-radius: 0.25rem; - transition: box-shadow 0.15s ease-in-out; +.list-group-horizontal { + flex-direction: row; } -@media (prefers-reduced-motion: reduce) { - .navbar-toggler { - transition: none; - } +.list-group-horizontal > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; } -.navbar-toggler:hover { - text-decoration: none; +.list-group-horizontal > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; } -.navbar-toggler:focus { - text-decoration: none; - outline: 0; - box-shadow: 0 0 0 0.25rem; +.list-group-horizontal > .list-group-item.active { + margin-top: 0; } - -.navbar-toggler-icon { - display: inline-block; - width: 1.5em; - height: 1.5em; - vertical-align: middle; - background-repeat: no-repeat; - background-position: center; - background-size: 100%; +.list-group-horizontal > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; } - -.navbar-nav-scroll { - max-height: var(--bs-scroll-height, 75vh); - overflow-y: auto; +.list-group-horizontal > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); } @media (min-width: 576px) { - .navbar-expand-sm { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-sm .navbar-nav { + .list-group-horizontal-sm { flex-direction: row; } - .navbar-expand-sm .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-sm .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-sm .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-sm .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-sm .navbar-toggler { - display: none; + .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; } - .navbar-expand-sm .offcanvas-header { - display: none; + .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; } - .navbar-expand-sm .offcanvas { - position: inherit; - bottom: 0; - z-index: 1000; - flex-grow: 1; - visibility: visible !important; - background-color: transparent; - border-right: 0; - border-left: 0; - transition: none; - transform: none; + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; } - .navbar-expand-sm .offcanvas-top, - .navbar-expand-sm .offcanvas-bottom { - height: auto; - border-top: 0; - border-bottom: 0; + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; } - .navbar-expand-sm .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 768px) { - .navbar-expand-md { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-md .navbar-nav { + .list-group-horizontal-md { flex-direction: row; } - .navbar-expand-md .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-md .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-md .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-md .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-md .navbar-toggler { - display: none; + .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; } - .navbar-expand-md .offcanvas-header { - display: none; + .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; } - .navbar-expand-md .offcanvas { - position: inherit; - bottom: 0; - z-index: 1000; - flex-grow: 1; - visibility: visible !important; - background-color: transparent; - border-right: 0; - border-left: 0; - transition: none; - transform: none; + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; } - .navbar-expand-md .offcanvas-top, - .navbar-expand-md .offcanvas-bottom { - height: auto; - border-top: 0; - border-bottom: 0; + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; } - .navbar-expand-md .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 992px) { - .navbar-expand-lg { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-lg .navbar-nav { + .list-group-horizontal-lg { flex-direction: row; } - .navbar-expand-lg .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-lg .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-lg .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-lg .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-lg .navbar-toggler { - display: none; + .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; } - .navbar-expand-lg .offcanvas-header { - display: none; + .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; } - .navbar-expand-lg .offcanvas { - position: inherit; - bottom: 0; - z-index: 1000; - flex-grow: 1; - visibility: visible !important; - background-color: transparent; - border-right: 0; - border-left: 0; - transition: none; - transform: none; + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; } - .navbar-expand-lg .offcanvas-top, - .navbar-expand-lg .offcanvas-bottom { - height: auto; - border-top: 0; - border-bottom: 0; + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; } - .navbar-expand-lg .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 1200px) { - .navbar-expand-xl { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-xl .navbar-nav { + .list-group-horizontal-xl { flex-direction: row; } - .navbar-expand-xl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xl .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-xl .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-xl .navbar-collapse { - display: flex !important; - flex-basis: auto; - } - .navbar-expand-xl .navbar-toggler { - display: none; + .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; } - .navbar-expand-xl .offcanvas-header { - display: none; + .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; } - .navbar-expand-xl .offcanvas { - position: inherit; - bottom: 0; - z-index: 1000; - flex-grow: 1; - visibility: visible !important; - background-color: transparent; - border-right: 0; - border-left: 0; - transition: none; - transform: none; + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; } - .navbar-expand-xl .offcanvas-top, - .navbar-expand-xl .offcanvas-bottom { - height: auto; - border-top: 0; - border-bottom: 0; + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; } - .navbar-expand-xl .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); } } @media (min-width: 1400px) { - .navbar-expand-xxl { - flex-wrap: nowrap; - justify-content: flex-start; - } - .navbar-expand-xxl .navbar-nav { - flex-direction: row; - } - .navbar-expand-xxl .navbar-nav .dropdown-menu { - position: absolute; - } - .navbar-expand-xxl .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; - } - .navbar-expand-xxl .navbar-nav-scroll { - overflow: visible; - } - .navbar-expand-xxl .navbar-collapse { - display: flex !important; - flex-basis: auto; + .list-group-horizontal-xxl { + flex-direction: row; } - .navbar-expand-xxl .navbar-toggler { - display: none; + .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) { + border-bottom-left-radius: var(--bs-list-group-border-radius); + border-top-right-radius: 0; } - .navbar-expand-xxl .offcanvas-header { - display: none; + .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) { + border-top-right-radius: var(--bs-list-group-border-radius); + border-bottom-left-radius: 0; } - .navbar-expand-xxl .offcanvas { - position: inherit; - bottom: 0; - z-index: 1000; - flex-grow: 1; - visibility: visible !important; - background-color: transparent; - border-right: 0; - border-left: 0; - transition: none; - transform: none; + .list-group-horizontal-xxl > .list-group-item.active { + margin-top: 0; } - .navbar-expand-xxl .offcanvas-top, - .navbar-expand-xxl .offcanvas-bottom { - height: auto; - border-top: 0; - border-bottom: 0; + .list-group-horizontal-xxl > .list-group-item + .list-group-item { + border-top-width: var(--bs-list-group-border-width); + border-left-width: 0; } - .navbar-expand-xxl .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; + .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { + margin-left: calc(-1 * var(--bs-list-group-border-width)); + border-left-width: var(--bs-list-group-border-width); } } -.navbar-expand { - flex-wrap: nowrap; - justify-content: flex-start; +.list-group-flush { + border-radius: 0; } -.navbar-expand .navbar-nav { - flex-direction: row; +.list-group-flush > .list-group-item { + border-width: 0 0 var(--bs-list-group-border-width); } -.navbar-expand .navbar-nav .dropdown-menu { - position: absolute; +.list-group-flush > .list-group-item:last-child { + border-bottom-width: 0; } -.navbar-expand .navbar-nav .nav-link { - padding-right: 0.5rem; - padding-left: 0.5rem; + +.list-group-item-primary { + --bs-list-group-color: var(--bs-primary-text-emphasis); + --bs-list-group-bg: var(--bs-primary-bg-subtle); + --bs-list-group-border-color: var(--bs-primary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-primary-border-subtle); + --bs-list-group-active-color: var(--bs-primary-bg-subtle); + --bs-list-group-active-bg: var(--bs-primary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-primary-text-emphasis); } -.navbar-expand .navbar-nav-scroll { - overflow: visible; + +.list-group-item-secondary { + --bs-list-group-color: var(--bs-secondary-text-emphasis); + --bs-list-group-bg: var(--bs-secondary-bg-subtle); + --bs-list-group-border-color: var(--bs-secondary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle); + --bs-list-group-active-color: var(--bs-secondary-bg-subtle); + --bs-list-group-active-bg: var(--bs-secondary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis); } -.navbar-expand .navbar-collapse { - display: flex !important; - flex-basis: auto; + +.list-group-item-success { + --bs-list-group-color: var(--bs-success-text-emphasis); + --bs-list-group-bg: var(--bs-success-bg-subtle); + --bs-list-group-border-color: var(--bs-success-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-success-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-success-border-subtle); + --bs-list-group-active-color: var(--bs-success-bg-subtle); + --bs-list-group-active-bg: var(--bs-success-text-emphasis); + --bs-list-group-active-border-color: var(--bs-success-text-emphasis); } -.navbar-expand .navbar-toggler { - display: none; + +.list-group-item-info { + --bs-list-group-color: var(--bs-info-text-emphasis); + --bs-list-group-bg: var(--bs-info-bg-subtle); + --bs-list-group-border-color: var(--bs-info-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-info-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-info-border-subtle); + --bs-list-group-active-color: var(--bs-info-bg-subtle); + --bs-list-group-active-bg: var(--bs-info-text-emphasis); + --bs-list-group-active-border-color: var(--bs-info-text-emphasis); } -.navbar-expand .offcanvas-header { - display: none; + +.list-group-item-warning { + --bs-list-group-color: var(--bs-warning-text-emphasis); + --bs-list-group-bg: var(--bs-warning-bg-subtle); + --bs-list-group-border-color: var(--bs-warning-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-warning-border-subtle); + --bs-list-group-active-color: var(--bs-warning-bg-subtle); + --bs-list-group-active-bg: var(--bs-warning-text-emphasis); + --bs-list-group-active-border-color: var(--bs-warning-text-emphasis); } -.navbar-expand .offcanvas { - position: inherit; - bottom: 0; - z-index: 1000; - flex-grow: 1; - visibility: visible !important; - background-color: transparent; - border-right: 0; - border-left: 0; - transition: none; - transform: none; + +.list-group-item-danger { + --bs-list-group-color: var(--bs-danger-text-emphasis); + --bs-list-group-bg: var(--bs-danger-bg-subtle); + --bs-list-group-border-color: var(--bs-danger-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-danger-border-subtle); + --bs-list-group-active-color: var(--bs-danger-bg-subtle); + --bs-list-group-active-bg: var(--bs-danger-text-emphasis); + --bs-list-group-active-border-color: var(--bs-danger-text-emphasis); } -.navbar-expand .offcanvas-top, -.navbar-expand .offcanvas-bottom { - height: auto; - border-top: 0; - border-bottom: 0; + +.list-group-item-light { + --bs-list-group-color: var(--bs-light-text-emphasis); + --bs-list-group-bg: var(--bs-light-bg-subtle); + --bs-list-group-border-color: var(--bs-light-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-light-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-light-border-subtle); + --bs-list-group-active-color: var(--bs-light-bg-subtle); + --bs-list-group-active-bg: var(--bs-light-text-emphasis); + --bs-list-group-active-border-color: var(--bs-light-text-emphasis); } -.navbar-expand .offcanvas-body { - display: flex; - flex-grow: 0; - padding: 0; - overflow-y: visible; + +.list-group-item-dark { + --bs-list-group-color: var(--bs-dark-text-emphasis); + --bs-list-group-bg: var(--bs-dark-bg-subtle); + --bs-list-group-border-color: var(--bs-dark-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-dark-border-subtle); + --bs-list-group-active-color: var(--bs-dark-bg-subtle); + --bs-list-group-active-bg: var(--bs-dark-text-emphasis); + --bs-list-group-active-border-color: var(--bs-dark-text-emphasis); +} + +.list-group-item-accent { + --bs-list-group-color: var(--bs-accent-text-emphasis); + --bs-list-group-bg: var(--bs-accent-bg-subtle); + --bs-list-group-border-color: var(--bs-accent-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-accent-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-accent-border-subtle); + --bs-list-group-active-color: var(--bs-accent-bg-subtle); + --bs-list-group-active-bg: var(--bs-accent-text-emphasis); + --bs-list-group-active-border-color: var(--bs-accent-text-emphasis); +} + +.list-group-item-cinereous { + --bs-list-group-color: var(--bs-cinereous-text-emphasis); + --bs-list-group-bg: var(--bs-cinereous-bg-subtle); + --bs-list-group-border-color: var(--bs-cinereous-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-cinereous-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-cinereous-border-subtle); + --bs-list-group-active-color: var(--bs-cinereous-bg-subtle); + --bs-list-group-active-bg: var(--bs-cinereous-text-emphasis); + --bs-list-group-active-border-color: var(--bs-cinereous-text-emphasis); +} + +.list-group-item-verdigris { + --bs-list-group-color: var(--bs-verdigris-text-emphasis); + --bs-list-group-bg: var(--bs-verdigris-bg-subtle); + --bs-list-group-border-color: var(--bs-verdigris-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-verdigris-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-verdigris-border-subtle); + --bs-list-group-active-color: var(--bs-verdigris-bg-subtle); + --bs-list-group-active-bg: var(--bs-verdigris-text-emphasis); + --bs-list-group-active-border-color: var(--bs-verdigris-text-emphasis); +} + +.list-group-item-icterine { + --bs-list-group-color: var(--bs-icterine-text-emphasis); + --bs-list-group-bg: var(--bs-icterine-bg-subtle); + --bs-list-group-border-color: var(--bs-icterine-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-icterine-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-icterine-border-subtle); + --bs-list-group-active-color: var(--bs-icterine-bg-subtle); + --bs-list-group-active-bg: var(--bs-icterine-text-emphasis); + --bs-list-group-active-border-color: var(--bs-icterine-text-emphasis); +} + +.list-group-item-mute { + --bs-list-group-color: var(--bs-mute-text-emphasis); + --bs-list-group-bg: var(--bs-mute-bg-subtle); + --bs-list-group-border-color: var(--bs-mute-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-mute-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-mute-border-subtle); + --bs-list-group-active-color: var(--bs-mute-bg-subtle); + --bs-list-group-active-bg: var(--bs-mute-text-emphasis); + --bs-list-group-active-border-color: var(--bs-mute-text-emphasis); } -.navbar-light .navbar-brand { - color: rgba(0, 0, 0, 0.9); +.btn-close { + --bs-btn-close-color: #000; + --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); + --bs-btn-close-opacity: 0.5; + --bs-btn-close-hover-opacity: 0.75; + --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(140, 104, 205, 0.25); + --bs-btn-close-focus-opacity: 1; + --bs-btn-close-disabled-opacity: 0.25; + --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); + box-sizing: content-box; + width: 1em; + height: 1em; + padding: 0.25em 0.25em; + color: var(--bs-btn-close-color); + background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; + border: 0; + border-radius: 0.375rem; + opacity: var(--bs-btn-close-opacity); +} +.btn-close:hover { + color: var(--bs-btn-close-color); + text-decoration: none; + opacity: var(--bs-btn-close-hover-opacity); +} +.btn-close:focus { + outline: 0; + box-shadow: var(--bs-btn-close-focus-shadow); + opacity: var(--bs-btn-close-focus-opacity); } -.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { - color: rgba(0, 0, 0, 0.9); +.btn-close:disabled, .btn-close.disabled { + pointer-events: none; + user-select: none; + opacity: var(--bs-btn-close-disabled-opacity); } -.navbar-light .navbar-nav .nav-link { - color: rgba(0, 0, 0, 0.55); + +.btn-close-white { + filter: var(--bs-btn-close-white-filter); +} + +[data-bs-theme=dark] .btn-close { + filter: var(--bs-btn-close-white-filter); +} + +.toast { + --bs-toast-zindex: 1090; + --bs-toast-padding-x: 0.75rem; + --bs-toast-padding-y: 0.5rem; + --bs-toast-spacing: 1.5rem; + --bs-toast-max-width: 350px; + --bs-toast-font-size: 0.875rem; + --bs-toast-color: ; + --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-border-width: var(--bs-border-width); + --bs-toast-border-color: var(--bs-border-color-translucent); + --bs-toast-border-radius: var(--bs-border-radius); + --bs-toast-box-shadow: var(--bs-box-shadow); + --bs-toast-header-color: var(--bs-secondary-color); + --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-header-border-color: var(--bs-border-color-translucent); + width: var(--bs-toast-max-width); + max-width: 100%; + font-size: var(--bs-toast-font-size); + color: var(--bs-toast-color); + pointer-events: auto; + background-color: var(--bs-toast-bg); + background-clip: padding-box; + border: var(--bs-toast-border-width) solid var(--bs-toast-border-color); + box-shadow: var(--bs-toast-box-shadow); + border-radius: var(--bs-toast-border-radius); } -.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { - color: rgba(0, 0, 0, 0.7); +.toast.showing { + opacity: 0; } -.navbar-light .navbar-nav .nav-link.disabled { - color: rgba(0, 0, 0, 0.3); +.toast:not(.show) { + display: none; } -.navbar-light .navbar-nav .show > .nav-link, -.navbar-light .navbar-nav .nav-link.active { - color: rgba(0, 0, 0, 0.9); + +.toast-container { + --bs-toast-zindex: 1090; + position: absolute; + z-index: var(--bs-toast-zindex); + width: max-content; + max-width: 100%; + pointer-events: none; } -.navbar-light .navbar-toggler { - color: rgba(0, 0, 0, 0.55); - border-color: rgba(0, 0, 0, 0.1); +.toast-container > :not(:last-child) { + margin-bottom: var(--bs-toast-spacing); } -.navbar-light .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + +.toast-header { + display: flex; + align-items: center; + padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x); + color: var(--bs-toast-header-color); + background-color: var(--bs-toast-header-bg); + background-clip: padding-box; + border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color); + border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); + border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width)); } -.navbar-light .navbar-text { - color: rgba(0, 0, 0, 0.55); +.toast-header .btn-close { + margin-right: calc(-0.5 * var(--bs-toast-padding-x)); + margin-left: var(--bs-toast-padding-x); } -.navbar-light .navbar-text a, -.navbar-light .navbar-text a:hover, -.navbar-light .navbar-text a:focus { - color: rgba(0, 0, 0, 0.9); + +.toast-body { + padding: var(--bs-toast-padding-x); + word-wrap: break-word; } -.navbar-dark .navbar-brand { - color: #fff; +.modal { + --bs-modal-zindex: 1055; + --bs-modal-width: 500px; + --bs-modal-padding: 1rem; + --bs-modal-margin: 0.5rem; + --bs-modal-color: ; + --bs-modal-bg: var(--bs-body-bg); + --bs-modal-border-color: var(--bs-border-color-translucent); + --bs-modal-border-width: var(--bs-border-width); + --bs-modal-border-radius: var(--bs-border-radius-lg); + --bs-modal-box-shadow: var(--bs-box-shadow-sm); + --bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width))); + --bs-modal-header-padding-x: 1rem; + --bs-modal-header-padding-y: 1rem; + --bs-modal-header-padding: 1rem 1rem; + --bs-modal-header-border-color: var(--bs-border-color); + --bs-modal-header-border-width: var(--bs-border-width); + --bs-modal-title-line-height: 1.5; + --bs-modal-footer-gap: 0.5rem; + --bs-modal-footer-bg: ; + --bs-modal-footer-border-color: var(--bs-border-color); + --bs-modal-footer-border-width: var(--bs-border-width); + position: fixed; + top: 0; + left: 0; + z-index: var(--bs-modal-zindex); + display: none; + width: 100%; + height: 100%; + overflow-x: hidden; + overflow-y: auto; + outline: 0; } -.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { - color: #fff; + +.modal-dialog { + position: relative; + width: auto; + margin: var(--bs-modal-margin); + pointer-events: none; } -.navbar-dark .navbar-nav .nav-link { - color: rgba(255, 255, 255, 0.55); +.modal.fade .modal-dialog { + transition: transform 0.3s ease-out; + transform: translate(0, -50px); } -.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { - color: rgba(255, 255, 255, 0.75); +@media (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + transition: none; + } } -.navbar-dark .navbar-nav .nav-link.disabled { - color: rgba(255, 255, 255, 0.25); +.modal.show .modal-dialog { + transform: none; } -.navbar-dark .navbar-nav .show > .nav-link, -.navbar-dark .navbar-nav .nav-link.active { - color: #fff; +.modal.modal-static .modal-dialog { + transform: scale(1.02); } -.navbar-dark .navbar-toggler { - color: rgba(255, 255, 255, 0.55); - border-color: rgba(255, 255, 255, 0.1); + +.modal-dialog-scrollable { + height: calc(100% - var(--bs-modal-margin) * 2); } -.navbar-dark .navbar-toggler-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +.modal-dialog-scrollable .modal-content { + max-height: 100%; + overflow: hidden; } -.navbar-dark .navbar-text { - color: rgba(255, 255, 255, 0.55); +.modal-dialog-scrollable .modal-body { + overflow-y: auto; } -.navbar-dark .navbar-text a, -.navbar-dark .navbar-text a:hover, -.navbar-dark .navbar-text a:focus { - color: #fff; + +.modal-dialog-centered { + display: flex; + align-items: center; + min-height: calc(100% - var(--bs-modal-margin) * 2); } -.card { +.modal-content { position: relative; display: flex; flex-direction: column; - min-width: 0; - word-wrap: break-word; - background-color: #fff; - background-clip: border-box; - border: 1px solid rgba(0, 0, 0, 0.125); - border-radius: 0.25rem; + width: 100%; + color: var(--bs-modal-color); + pointer-events: auto; + background-color: var(--bs-modal-bg); + background-clip: padding-box; + border: var(--bs-modal-border-width) solid var(--bs-modal-border-color); + border-radius: var(--bs-modal-border-radius); + outline: 0; } -.card > hr { - margin-right: 0; - margin-left: 0; + +.modal-backdrop { + --bs-backdrop-zindex: 1050; + --bs-backdrop-bg: #000; + --bs-backdrop-opacity: 0.5; + position: fixed; + top: 0; + left: 0; + z-index: var(--bs-backdrop-zindex); + width: 100vw; + height: 100vh; + background-color: var(--bs-backdrop-bg); } -.card > .list-group { - border-top: inherit; - border-bottom: inherit; +.modal-backdrop.fade { + opacity: 0; } -.card > .list-group:first-child { - border-top-width: 0; - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); +.modal-backdrop.show { + opacity: var(--bs-backdrop-opacity); } -.card > .list-group:last-child { - border-bottom-width: 0; - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); + +.modal-header { + display: flex; + flex-shrink: 0; + align-items: center; + padding: var(--bs-modal-header-padding); + border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color); + border-top-left-radius: var(--bs-modal-inner-border-radius); + border-top-right-radius: var(--bs-modal-inner-border-radius); +} +.modal-header .btn-close { + padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5); + margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) auto; +} + +.modal-title { + margin-bottom: 0; + line-height: var(--bs-modal-title-line-height); +} + +.modal-body { + position: relative; + flex: 1 1 auto; + padding: var(--bs-modal-padding); +} + +.modal-footer { + display: flex; + flex-shrink: 0; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5); + background-color: var(--bs-modal-footer-bg); + border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color); + border-bottom-right-radius: var(--bs-modal-inner-border-radius); + border-bottom-left-radius: var(--bs-modal-inner-border-radius); +} +.modal-footer > * { + margin: calc(var(--bs-modal-footer-gap) * 0.5); +} + +@media (min-width: 576px) { + .modal { + --bs-modal-margin: 1.75rem; + --bs-modal-box-shadow: var(--bs-box-shadow); + } + .modal-dialog { + max-width: var(--bs-modal-width); + margin-right: auto; + margin-left: auto; + } + .modal-sm { + --bs-modal-width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg, + .modal-xl { + --bs-modal-width: 800px; + } +} +@media (min-width: 1200px) { + .modal-xl { + --bs-modal-width: 1140px; + } +} +.modal-fullscreen { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; +} +.modal-fullscreen .modal-content { + height: 100%; + border: 0; + border-radius: 0; +} +.modal-fullscreen .modal-header, +.modal-fullscreen .modal-footer { + border-radius: 0; +} +.modal-fullscreen .modal-body { + overflow-y: auto; +} + +@media (max-width: 575.98px) { + .modal-fullscreen-sm-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + .modal-fullscreen-sm-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + .modal-fullscreen-sm-down .modal-header, + .modal-fullscreen-sm-down .modal-footer { + border-radius: 0; + } + .modal-fullscreen-sm-down .modal-body { + overflow-y: auto; + } } -.card > .card-header + .list-group, -.card > .list-group + .card-footer { - border-top: 0; +@media (max-width: 767.98px) { + .modal-fullscreen-md-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + .modal-fullscreen-md-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + .modal-fullscreen-md-down .modal-header, + .modal-fullscreen-md-down .modal-footer { + border-radius: 0; + } + .modal-fullscreen-md-down .modal-body { + overflow-y: auto; + } } - -.card-body { - flex: 1 1 auto; - padding: 1rem 1rem; +@media (max-width: 991.98px) { + .modal-fullscreen-lg-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + .modal-fullscreen-lg-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + .modal-fullscreen-lg-down .modal-header, + .modal-fullscreen-lg-down .modal-footer { + border-radius: 0; + } + .modal-fullscreen-lg-down .modal-body { + overflow-y: auto; + } } - -.card-title { - margin-bottom: 0.5rem; +@media (max-width: 1199.98px) { + .modal-fullscreen-xl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + .modal-fullscreen-xl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + .modal-fullscreen-xl-down .modal-header, + .modal-fullscreen-xl-down .modal-footer { + border-radius: 0; + } + .modal-fullscreen-xl-down .modal-body { + overflow-y: auto; + } } - -.card-subtitle { - margin-top: -0.25rem; - margin-bottom: 0; +@media (max-width: 1399.98px) { + .modal-fullscreen-xxl-down { + width: 100vw; + max-width: none; + height: 100%; + margin: 0; + } + .modal-fullscreen-xxl-down .modal-content { + height: 100%; + border: 0; + border-radius: 0; + } + .modal-fullscreen-xxl-down .modal-header, + .modal-fullscreen-xxl-down .modal-footer { + border-radius: 0; + } + .modal-fullscreen-xxl-down .modal-body { + overflow-y: auto; + } } - -.card-text:last-child { - margin-bottom: 0; +.tooltip { + --bs-tooltip-zindex: 1080; + --bs-tooltip-max-width: 200px; + --bs-tooltip-padding-x: 0.5rem; + --bs-tooltip-padding-y: 0.25rem; + --bs-tooltip-margin: ; + --bs-tooltip-font-size: 0.875rem; + --bs-tooltip-color: var(--bs-body-bg); + --bs-tooltip-bg: var(--bs-emphasis-color); + --bs-tooltip-border-radius: var(--bs-border-radius); + --bs-tooltip-opacity: 0.9; + --bs-tooltip-arrow-width: 0.8rem; + --bs-tooltip-arrow-height: 0.4rem; + z-index: var(--bs-tooltip-zindex); + display: block; + margin: var(--bs-tooltip-margin); + font-family: "Inconsolata", monospace; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + white-space: normal; + word-spacing: normal; + line-break: auto; + font-size: var(--bs-tooltip-font-size); + word-wrap: break-word; + opacity: 0; } - -.card-link + .card-link { - margin-left: 1rem; +.tooltip.show { + opacity: var(--bs-tooltip-opacity); } - -.card-header { - padding: 0.5rem 1rem; - margin-bottom: 0; - background-color: rgba(0, 0, 0, 0.03); - border-bottom: 1px solid rgba(0, 0, 0, 0.125); +.tooltip .tooltip-arrow { + display: block; + width: var(--bs-tooltip-arrow-width); + height: var(--bs-tooltip-arrow-height); } -.card-header:first-child { - border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; +.tooltip .tooltip-arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; } -.card-footer { - padding: 0.5rem 1rem; - background-color: rgba(0, 0, 0, 0.03); - border-top: 1px solid rgba(0, 0, 0, 0.125); +.bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { + bottom: calc(-1 * var(--bs-tooltip-arrow-height)); } -.card-footer:last-child { - border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); +.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { + top: -1px; + border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; + border-top-color: var(--bs-tooltip-bg); } -.card-header-tabs { - margin-right: -0.5rem; - margin-bottom: -0.5rem; - margin-left: -0.5rem; - border-bottom: 0; +/* rtl:begin:ignore */ +.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { + left: calc(-1 * var(--bs-tooltip-arrow-height)); + width: var(--bs-tooltip-arrow-height); + height: var(--bs-tooltip-arrow-width); } - -.card-header-pills { - margin-right: -0.5rem; - margin-left: -0.5rem; +.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before { + right: -1px; + border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0; + border-right-color: var(--bs-tooltip-bg); } -.card-img-overlay { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - padding: 1rem; - border-radius: calc(0.25rem - 1px); +/* rtl:end:ignore */ +.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { + top: calc(-1 * var(--bs-tooltip-arrow-height)); } - -.card-img, -.card-img-top, -.card-img-bottom { - width: 100%; +.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { + bottom: -1px; + border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); + border-bottom-color: var(--bs-tooltip-bg); } -.card-img, -.card-img-top { - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); +/* rtl:begin:ignore */ +.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { + right: calc(-1 * var(--bs-tooltip-arrow-height)); + width: var(--bs-tooltip-arrow-height); + height: var(--bs-tooltip-arrow-width); } - -.card-img, -.card-img-bottom { - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); +.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before { + left: -1px; + border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height); + border-left-color: var(--bs-tooltip-bg); } -.card-group > .card { - margin-bottom: 0.75rem; -} -@media (min-width: 576px) { - .card-group { - display: flex; - flex-flow: row wrap; - } - .card-group > .card { - flex: 1 0 0%; - margin-bottom: 0; - } - .card-group > .card + .card { - margin-left: 0; - border-left: 0; - } - .card-group > .card:not(:last-child) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { - border-top-right-radius: 0; - } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { - border-bottom-right-radius: 0; - } - .card-group > .card:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { - border-top-left-radius: 0; - } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { - border-bottom-left-radius: 0; - } +/* rtl:end:ignore */ +.tooltip-inner { + max-width: var(--bs-tooltip-max-width); + padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x); + color: var(--bs-tooltip-color); + text-align: center; + background-color: var(--bs-tooltip-bg); + border-radius: var(--bs-tooltip-border-radius); } -.accordion-button { - position: relative; - display: flex; - align-items: center; - width: 100%; - padding: 1rem 1.25rem; - font-size: 1rem; - color: #00305E; +.popover { + --bs-popover-zindex: 1070; + --bs-popover-max-width: 276px; + --bs-popover-font-size: 0.875rem; + --bs-popover-bg: var(--bs-body-bg); + --bs-popover-border-width: var(--bs-border-width); + --bs-popover-border-color: var(--bs-border-color-translucent); + --bs-popover-border-radius: var(--bs-border-radius-lg); + --bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width)); + --bs-popover-box-shadow: var(--bs-box-shadow); + --bs-popover-header-padding-x: 1rem; + --bs-popover-header-padding-y: 0.5rem; + --bs-popover-header-font-size: 1rem; + --bs-popover-header-color: inherit; + --bs-popover-header-bg: var(--bs-secondary-bg); + --bs-popover-body-padding-x: 1rem; + --bs-popover-body-padding-y: 1rem; + --bs-popover-body-color: var(--bs-body-color); + --bs-popover-arrow-width: 1rem; + --bs-popover-arrow-height: 0.5rem; + --bs-popover-arrow-border: var(--bs-popover-border-color); + z-index: var(--bs-popover-zindex); + display: block; + max-width: var(--bs-popover-max-width); + font-family: "Inconsolata", monospace; + font-style: normal; + font-weight: 400; + line-height: 1.5; text-align: left; - background-color: #fff; - border: 0; - border-radius: 0; - overflow-anchor: none; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; -} -@media (prefers-reduced-motion: reduce) { - .accordion-button { - transition: none; - } -} -.accordion-button:not(.collapsed) { - color: #3b6a96; - background-color: #ecf1f6; - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.125); + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + white-space: normal; + word-spacing: normal; + line-break: auto; + font-size: var(--bs-popover-font-size); + word-wrap: break-word; + background-color: var(--bs-popover-bg); + background-clip: padding-box; + border: var(--bs-popover-border-width) solid var(--bs-popover-border-color); + border-radius: var(--bs-popover-border-radius); } -.accordion-button:not(.collapsed)::after { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%233b6a96'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); - transform: rotate(-180deg); +.popover .popover-arrow { + display: block; + width: var(--bs-popover-arrow-width); + height: var(--bs-popover-arrow-height); } -.accordion-button::after { - flex-shrink: 0; - width: 1.25rem; - height: 1.25rem; - margin-left: auto; +.popover .popover-arrow::before, .popover .popover-arrow::after { + position: absolute; + display: block; content: ""; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2300305E'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); - background-repeat: no-repeat; - background-size: 1.25rem; - transition: transform 0.2s ease-in-out; + border-color: transparent; + border-style: solid; + border-width: 0; +} + +.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow { + bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } -@media (prefers-reduced-motion: reduce) { - .accordion-button::after { - transition: none; - } +.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { + border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } -.accordion-button:hover { - z-index: 2; +.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before { + bottom: 0; + border-top-color: var(--bs-popover-arrow-border); } -.accordion-button:focus { - z-index: 3; - border-color: #a1bbd3; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.25); +.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { + bottom: var(--bs-popover-border-width); + border-top-color: var(--bs-popover-bg); } -.accordion-header { - margin-bottom: 0; +/* rtl:begin:ignore */ +.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow { + left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + width: var(--bs-popover-arrow-height); + height: var(--bs-popover-arrow-width); } - -.accordion-item { - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.125); +.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { + border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0; } -.accordion-item:first-of-type { - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; +.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before { + left: 0; + border-right-color: var(--bs-popover-arrow-border); } -.accordion-item:first-of-type .accordion-button { - border-top-left-radius: calc(0.25rem - 1px); - border-top-right-radius: calc(0.25rem - 1px); +.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { + left: var(--bs-popover-border-width); + border-right-color: var(--bs-popover-bg); } -.accordion-item:not(:first-of-type) { - border-top: 0; + +/* rtl:end:ignore */ +.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow { + top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); } -.accordion-item:last-of-type { - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; +.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { + border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } -.accordion-item:last-of-type .accordion-button.collapsed { - border-bottom-right-radius: calc(0.25rem - 1px); - border-bottom-left-radius: calc(0.25rem - 1px); +.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before { + top: 0; + border-bottom-color: var(--bs-popover-arrow-border); } -.accordion-item:last-of-type .accordion-collapse { - border-bottom-right-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; +.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { + top: var(--bs-popover-border-width); + border-bottom-color: var(--bs-popover-bg); } - -.accordion-body { - padding: 1rem 1.25rem; +.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: var(--bs-popover-arrow-width); + margin-left: calc(-0.5 * var(--bs-popover-arrow-width)); + content: ""; + border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg); } -.accordion-flush .accordion-collapse { - border-width: 0; -} -.accordion-flush .accordion-item { - border-right: 0; - border-left: 0; - border-radius: 0; +/* rtl:begin:ignore */ +.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow { + right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width)); + width: var(--bs-popover-arrow-height); + height: var(--bs-popover-arrow-width); } -.accordion-flush .accordion-item:first-child { - border-top: 0; +.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { + border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height); } -.accordion-flush .accordion-item:last-child { - border-bottom: 0; +.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before { + right: 0; + border-left-color: var(--bs-popover-arrow-border); } -.accordion-flush .accordion-item .accordion-button { - border-radius: 0; +.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { + right: var(--bs-popover-border-width); + border-left-color: var(--bs-popover-bg); } -.breadcrumb { - display: flex; - flex-wrap: wrap; - padding: 0 0; - margin-bottom: 1rem; - list-style: none; +/* rtl:end:ignore */ +.popover-header { + padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x); + margin-bottom: 0; + font-size: var(--bs-popover-header-font-size); + color: var(--bs-popover-header-color); + background-color: var(--bs-popover-header-bg); + border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color); + border-top-left-radius: var(--bs-popover-inner-border-radius); + border-top-right-radius: var(--bs-popover-inner-border-radius); } - -.breadcrumb-item + .breadcrumb-item { - padding-left: 0.5rem; +.popover-header:empty { + display: none; } -.breadcrumb-item + .breadcrumb-item::before { - float: left; - padding-right: 0.5rem; - color: #6c757d; - content: var(--bs-breadcrumb-divider, "/") /* rtl: var(--bs-breadcrumb-divider, "/") */; + +.popover-body { + padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x); + color: var(--bs-popover-body-color); } -.breadcrumb-item.active { - color: #6c757d; + +.carousel { + position: relative; } -.pagination { - display: flex; - padding-left: 0; - list-style: none; +.carousel.pointer-event { + touch-action: pan-y; } -.page-link { +.carousel-inner { position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner::after { display: block; - color: #4276A7; - text-decoration: none; - background-color: #fff; - border: 1px solid #dee2e6; - transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + clear: both; + content: ""; +} + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + backface-visibility: hidden; + transition: transform 0.6s ease-in-out; } @media (prefers-reduced-motion: reduce) { - .page-link { + .carousel-item { transition: none; } } -.page-link:hover { - z-index: 2; - color: #355e86; - background-color: #e9ecef; - border-color: #dee2e6; -} -.page-link:focus { - z-index: 3; - color: #355e86; - background-color: #e9ecef; - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.25); -} -.page-item:not(:first-child) .page-link { - margin-left: -1px; -} -.page-item.active .page-link { - z-index: 3; - color: #fff; - background-color: #4276A7; - border-color: #4276A7; -} -.page-item.disabled .page-link { - color: #6c757d; - pointer-events: none; - background-color: #fff; - border-color: #dee2e6; +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; } -.page-link { - padding: 0.375rem 0.75rem; +.carousel-item-next:not(.carousel-item-start), +.active.carousel-item-end { + transform: translateX(100%); } -.page-item:first-child .page-link { - border-top-left-radius: 0.25rem; - border-bottom-left-radius: 0.25rem; -} -.page-item:last-child .page-link { - border-top-right-radius: 0.25rem; - border-bottom-right-radius: 0.25rem; +.carousel-item-prev:not(.carousel-item-end), +.active.carousel-item-start { + transform: translateX(-100%); } -.pagination-lg .page-link { - padding: 0.75rem 1.5rem; - font-size: 1.25rem; -} -.pagination-lg .page-item:first-child .page-link { - border-top-left-radius: 0.3rem; - border-bottom-left-radius: 0.3rem; -} -.pagination-lg .page-item:last-child .page-link { - border-top-right-radius: 0.3rem; - border-bottom-right-radius: 0.3rem; +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + transform: none; } - -.pagination-sm .page-link { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; +.carousel-fade .carousel-item.active, +.carousel-fade .carousel-item-next.carousel-item-start, +.carousel-fade .carousel-item-prev.carousel-item-end { + z-index: 1; + opacity: 1; } -.pagination-sm .page-item:first-child .page-link { - border-top-left-radius: 0.2rem; - border-bottom-left-radius: 0.2rem; +.carousel-fade .active.carousel-item-start, +.carousel-fade .active.carousel-item-end { + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; } -.pagination-sm .page-item:last-child .page-link { - border-top-right-radius: 0.2rem; - border-bottom-right-radius: 0.2rem; +@media (prefers-reduced-motion: reduce) { + .carousel-fade .active.carousel-item-start, + .carousel-fade .active.carousel-item-end { + transition: none; + } } -.badge { - display: inline-block; - padding: 0.35em 0.65em; - font-size: 0.75em; - font-weight: 700; - line-height: 1; +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + padding: 0; color: #fff; text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: 0.25rem; + background: none; + border: 0; + opacity: 0.5; + transition: opacity 0.15s ease; } -.badge:empty { - display: none; +@media (prefers-reduced-motion: reduce) { + .carousel-control-prev, + .carousel-control-next { + transition: none; + } +} +.carousel-control-prev:hover, .carousel-control-prev:focus, +.carousel-control-next:hover, +.carousel-control-next:focus { + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; } -.btn .badge { - position: relative; - top: -1px; +.carousel-control-prev { + left: 0; } -.alert { - position: relative; - padding: 1rem 1rem; - margin-bottom: 1rem; - border: 1px solid transparent; - border-radius: 0.25rem; +.carousel-control-next { + right: 0; } -.alert-heading { - color: inherit; +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 2rem; + height: 2rem; + background-repeat: no-repeat; + background-position: 50%; + background-size: 100% 100%; } -.alert-link { - font-weight: 700; +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")*/; } -.alert-dismissible { - padding-right: 3rem; +.carousel-control-next-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")*/; } -.alert-dismissible .btn-close { + +.carousel-indicators { position: absolute; - top: 0; right: 0; + bottom: 0; + left: 0; z-index: 2; - padding: 1.25rem 1rem; + display: flex; + justify-content: center; + padding: 0; + margin-right: 15%; + margin-bottom: 1rem; + margin-left: 15%; +} +.carousel-indicators [data-bs-target] { + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + padding: 0; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: 0.5; + transition: opacity 0.6s ease; } - -.alert-primary { - color: #284764; - background-color: #d9e4ed; - border-color: #c6d6e5; +@media (prefers-reduced-motion: reduce) { + .carousel-indicators [data-bs-target] { + transition: none; + } } -.alert-primary .alert-link { - color: #203950; +.carousel-indicators .active { + opacity: 1; } -.alert-secondary { - color: #41464b; - background-color: #e2e3e5; - border-color: #d3d6d8; -} -.alert-secondary .alert-link { - color: #34383c; +.carousel-caption { + position: absolute; + right: 15%; + bottom: 1.25rem; + left: 15%; + padding-top: 1.25rem; + padding-bottom: 1.25rem; + color: #fff; + text-align: center; } -.alert-success { - color: #42673b; - background-color: #e2eee0; - border-color: #d4e6d0; -} -.alert-success .alert-link { - color: #35522f; +.carousel-dark .carousel-control-prev-icon, +.carousel-dark .carousel-control-next-icon { + filter: invert(1) grayscale(100); } - -.alert-info { - color: #41626e; - background-color: #e2edf1; - border-color: #d3e3e9; +.carousel-dark .carousel-indicators [data-bs-target] { + background-color: #000; } -.alert-info .alert-link { - color: #344e58; +.carousel-dark .carousel-caption { + color: #000; } -.alert-warning { - color: #804d2e; - background-color: #f7e6db; - border-color: #f2d9c9; +[data-bs-theme=dark] .carousel .carousel-control-prev-icon, +[data-bs-theme=dark] .carousel .carousel-control-next-icon, [data-bs-theme=dark].carousel .carousel-control-prev-icon, +[data-bs-theme=dark].carousel .carousel-control-next-icon { + filter: invert(1) grayscale(100); } -.alert-warning .alert-link { - color: #663e25; +[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target], [data-bs-theme=dark].carousel .carousel-indicators [data-bs-target] { + background-color: #000; } - -.alert-danger { - color: #762e29; - background-color: #f3dbda; - border-color: #eec9c7; +[data-bs-theme=dark] .carousel .carousel-caption, [data-bs-theme=dark].carousel .carousel-caption { + color: #000; } -.alert-danger .alert-link { - color: #5e2521; + +.spinner-grow, +.spinner-border { + display: inline-block; + width: var(--bs-spinner-width); + height: var(--bs-spinner-height); + vertical-align: var(--bs-spinner-vertical-align); + border-radius: 50%; + animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name); } -.alert-light { - color: #626466; - background-color: #fdfeff; - border-color: #fcfeff; +@keyframes spinner-border { + to { + transform: rotate(360deg) /* rtl:ignore */; + } } -.alert-light .alert-link { - color: #4e5052; +.spinner-border { + --bs-spinner-width: 2rem; + --bs-spinner-height: 2rem; + --bs-spinner-vertical-align: -0.125em; + --bs-spinner-border-width: 0.25em; + --bs-spinner-animation-speed: 0.75s; + --bs-spinner-animation-name: spinner-border; + border: var(--bs-spinner-border-width) solid currentcolor; + border-right-color: transparent; } -.alert-dark { - color: #1d2b38; - background-color: #d6dadf; - border-color: #c1c8ce; -} -.alert-dark .alert-link { - color: #17222d; +.spinner-border-sm { + --bs-spinner-width: 1rem; + --bs-spinner-height: 1rem; + --bs-spinner-border-width: 0.2em; } -@keyframes progress-bar-stripes { +@keyframes spinner-grow { 0% { - background-position-x: 1rem; + transform: scale(0); + } + 50% { + opacity: 1; + transform: none; } } -.progress { - display: flex; - height: 1rem; - overflow: hidden; - font-size: 0.75rem; - background-color: #e9ecef; - border-radius: 0.25rem; +.spinner-grow { + --bs-spinner-width: 2rem; + --bs-spinner-height: 2rem; + --bs-spinner-vertical-align: -0.125em; + --bs-spinner-animation-speed: 0.75s; + --bs-spinner-animation-name: spinner-grow; + background-color: currentcolor; + opacity: 0; } -.progress-bar { - display: flex; - flex-direction: column; - justify-content: center; - overflow: hidden; - color: #fff; - text-align: center; - white-space: nowrap; - background-color: #4276A7; - transition: width 0.6s ease; +.spinner-grow-sm { + --bs-spinner-width: 1rem; + --bs-spinner-height: 1rem; } + @media (prefers-reduced-motion: reduce) { - .progress-bar { - transition: none; + .spinner-border, + .spinner-grow { + --bs-spinner-animation-speed: 1.5s; } } - -.progress-bar-striped { - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: 1rem 1rem; +.offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm { + --bs-offcanvas-zindex: 1045; + --bs-offcanvas-width: 400px; + --bs-offcanvas-height: 30vh; + --bs-offcanvas-padding-x: 1rem; + --bs-offcanvas-padding-y: 1rem; + --bs-offcanvas-color: var(--bs-body-color); + --bs-offcanvas-bg: var(--bs-body-bg); + --bs-offcanvas-border-width: var(--bs-border-width); + --bs-offcanvas-border-color: var(--bs-border-color-translucent); + --bs-offcanvas-box-shadow: var(--bs-box-shadow-sm); + --bs-offcanvas-transition: transform 0.3s ease-in-out; + --bs-offcanvas-title-line-height: 1.5; } -.progress-bar-animated { - animation: 1s linear infinite progress-bar-stripes; +@media (max-width: 575.98px) { + .offcanvas-sm { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} +@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-sm { + transition: none; + } } -@media (prefers-reduced-motion: reduce) { - .progress-bar-animated { - animation: none; +@media (max-width: 575.98px) { + .offcanvas-sm.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); } } - -.list-group { - display: flex; - flex-direction: column; - padding-left: 0; - margin-bottom: 0; - border-radius: 0.25rem; +@media (max-width: 575.98px) { + .offcanvas-sm.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } } - -.list-group-numbered { - list-style-type: none; - counter-reset: section; +@media (max-width: 575.98px) { + .offcanvas-sm.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } } -.list-group-numbered > li::before { - content: counters(section, ".") ". "; - counter-increment: section; +@media (max-width: 575.98px) { + .offcanvas-sm.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } } - -.list-group-item-action { - width: 100%; - color: #495057; - text-align: inherit; +@media (max-width: 575.98px) { + .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) { + transform: none; + } } -.list-group-item-action:hover, .list-group-item-action:focus { - z-index: 1; - color: #495057; - text-decoration: none; - background-color: #f8f9fa; +@media (max-width: 575.98px) { + .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show { + visibility: visible; + } } -.list-group-item-action:active { - color: #00305E; - background-color: #e9ecef; +@media (min-width: 576px) { + .offcanvas-sm { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + .offcanvas-sm .offcanvas-header { + display: none; + } + .offcanvas-sm .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } } -.list-group-item { - position: relative; - display: block; - padding: 0.5rem 1rem; - color: #212529; - text-decoration: none; - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.125); -} -.list-group-item:first-child { - border-top-left-radius: inherit; - border-top-right-radius: inherit; -} -.list-group-item:last-child { - border-bottom-right-radius: inherit; - border-bottom-left-radius: inherit; -} -.list-group-item.disabled, .list-group-item:disabled { - color: #6c757d; - pointer-events: none; - background-color: #fff; -} -.list-group-item.active { - z-index: 2; - color: #fff; - background-color: #4276A7; - border-color: #4276A7; -} -.list-group-item + .list-group-item { - border-top-width: 0; +@media (max-width: 767.98px) { + .offcanvas-md { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} +@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-md { + transition: none; + } } -.list-group-item + .list-group-item.active { - margin-top: -1px; - border-top-width: 1px; +@media (max-width: 767.98px) { + .offcanvas-md.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); + } } - -.list-group-horizontal { - flex-direction: row; +@media (max-width: 767.98px) { + .offcanvas-md.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); + } } -.list-group-horizontal > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; +@media (max-width: 767.98px) { + .offcanvas-md.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); + } } -.list-group-horizontal > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; +@media (max-width: 767.98px) { + .offcanvas-md.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); + } } -.list-group-horizontal > .list-group-item.active { - margin-top: 0; +@media (max-width: 767.98px) { + .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) { + transform: none; + } } -.list-group-horizontal > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; +@media (max-width: 767.98px) { + .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show { + visibility: visible; + } } -.list-group-horizontal > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; +@media (min-width: 768px) { + .offcanvas-md { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; + } + .offcanvas-md .offcanvas-header { + display: none; + } + .offcanvas-md .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; + } } -@media (min-width: 576px) { - .list-group-horizontal-sm { - flex-direction: row; - } - .list-group-horizontal-sm > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; +@media (max-width: 991.98px) { + .offcanvas-lg { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} +@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-lg { + transition: none; } - .list-group-horizontal-sm > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; +} +@media (max-width: 991.98px) { + .offcanvas-lg.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); } - .list-group-horizontal-sm > .list-group-item.active { - margin-top: 0; +} +@media (max-width: 991.98px) { + .offcanvas-lg.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); } - .list-group-horizontal-sm > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; +} +@media (max-width: 991.98px) { + .offcanvas-lg.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); } - .list-group-horizontal-sm > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; +} +@media (max-width: 991.98px) { + .offcanvas-lg.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); } } -@media (min-width: 768px) { - .list-group-horizontal-md { - flex-direction: row; +@media (max-width: 991.98px) { + .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) { + transform: none; } - .list-group-horizontal-md > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; +} +@media (max-width: 991.98px) { + .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show { + visibility: visible; } - .list-group-horizontal-md > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; +} +@media (min-width: 992px) { + .offcanvas-lg { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; } - .list-group-horizontal-md > .list-group-item.active { - margin-top: 0; + .offcanvas-lg .offcanvas-header { + display: none; } - .list-group-horizontal-md > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; + .offcanvas-lg .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; } - .list-group-horizontal-md > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; +} + +@media (max-width: 1199.98px) { + .offcanvas-xl { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} +@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-xl { + transition: none; } } -@media (min-width: 992px) { - .list-group-horizontal-lg { - flex-direction: row; +@media (max-width: 1199.98px) { + .offcanvas-xl.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); } - .list-group-horizontal-lg > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; +} +@media (max-width: 1199.98px) { + .offcanvas-xl.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); } - .list-group-horizontal-lg > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; +} +@media (max-width: 1199.98px) { + .offcanvas-xl.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); } - .list-group-horizontal-lg > .list-group-item.active { - margin-top: 0; +} +@media (max-width: 1199.98px) { + .offcanvas-xl.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); } - .list-group-horizontal-lg > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; +} +@media (max-width: 1199.98px) { + .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) { + transform: none; } - .list-group-horizontal-lg > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; +} +@media (max-width: 1199.98px) { + .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show { + visibility: visible; } } @media (min-width: 1200px) { - .list-group-horizontal-xl { - flex-direction: row; + .offcanvas-xl { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; } - .list-group-horizontal-xl > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; + .offcanvas-xl .offcanvas-header { + display: none; } - .list-group-horizontal-xl > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; + .offcanvas-xl .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; } - .list-group-horizontal-xl > .list-group-item.active { - margin-top: 0; +} + +@media (max-width: 1399.98px) { + .offcanvas-xxl { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); + } +} +@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) { + .offcanvas-xxl { + transition: none; } - .list-group-horizontal-xl > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; +} +@media (max-width: 1399.98px) { + .offcanvas-xxl.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); } - .list-group-horizontal-xl > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; +} +@media (max-width: 1399.98px) { + .offcanvas-xxl.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); } } -@media (min-width: 1400px) { - .list-group-horizontal-xxl { - flex-direction: row; +@media (max-width: 1399.98px) { + .offcanvas-xxl.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); } - .list-group-horizontal-xxl > .list-group-item:first-child { - border-bottom-left-radius: 0.25rem; - border-top-right-radius: 0; +} +@media (max-width: 1399.98px) { + .offcanvas-xxl.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); } - .list-group-horizontal-xxl > .list-group-item:last-child { - border-top-right-radius: 0.25rem; - border-bottom-left-radius: 0; +} +@media (max-width: 1399.98px) { + .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) { + transform: none; } - .list-group-horizontal-xxl > .list-group-item.active { - margin-top: 0; +} +@media (max-width: 1399.98px) { + .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show { + visibility: visible; } - .list-group-horizontal-xxl > .list-group-item + .list-group-item { - border-top-width: 1px; - border-left-width: 0; +} +@media (min-width: 1400px) { + .offcanvas-xxl { + --bs-offcanvas-height: auto; + --bs-offcanvas-border-width: 0; + background-color: transparent !important; } - .list-group-horizontal-xxl > .list-group-item + .list-group-item.active { - margin-left: -1px; - border-left-width: 1px; + .offcanvas-xxl .offcanvas-header { + display: none; + } + .offcanvas-xxl .offcanvas-body { + display: flex; + flex-grow: 0; + padding: 0; + overflow-y: visible; + background-color: transparent !important; } } -.list-group-flush { - border-radius: 0; -} -.list-group-flush > .list-group-item { - border-width: 0 0 1px; + +.offcanvas { + position: fixed; + bottom: 0; + z-index: var(--bs-offcanvas-zindex); + display: flex; + flex-direction: column; + max-width: 100%; + color: var(--bs-offcanvas-color); + visibility: hidden; + background-color: var(--bs-offcanvas-bg); + background-clip: padding-box; + outline: 0; + transition: var(--bs-offcanvas-transition); } -.list-group-flush > .list-group-item:last-child { - border-bottom-width: 0; +@media (prefers-reduced-motion: reduce) { + .offcanvas { + transition: none; + } } - -.list-group-item-primary { - color: #284764; - background-color: #d9e4ed; +.offcanvas.offcanvas-start { + top: 0; + left: 0; + width: var(--bs-offcanvas-width); + border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(-100%); } -.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { - color: #284764; - background-color: #c3cdd5; +.offcanvas.offcanvas-end { + top: 0; + right: 0; + width: var(--bs-offcanvas-width); + border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateX(100%); } -.list-group-item-primary.list-group-item-action.active { - color: #fff; - background-color: #284764; - border-color: #284764; +.offcanvas.offcanvas-top { + top: 0; + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(-100%); } - -.list-group-item-secondary { - color: #41464b; - background-color: #e2e3e5; +.offcanvas.offcanvas-bottom { + right: 0; + left: 0; + height: var(--bs-offcanvas-height); + max-height: 100%; + border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); + transform: translateY(100%); } -.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { - color: #41464b; - background-color: #cbccce; +.offcanvas.showing, .offcanvas.show:not(.hiding) { + transform: none; } -.list-group-item-secondary.list-group-item-action.active { - color: #fff; - background-color: #41464b; - border-color: #41464b; +.offcanvas.showing, .offcanvas.hiding, .offcanvas.show { + visibility: visible; } -.list-group-item-success { - color: #42673b; - background-color: #e2eee0; +.offcanvas-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; } -.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { - color: #42673b; - background-color: #cbd6ca; +.offcanvas-backdrop.fade { + opacity: 0; } -.list-group-item-success.list-group-item-action.active { - color: #fff; - background-color: #42673b; - border-color: #42673b; +.offcanvas-backdrop.show { + opacity: 0.5; } -.list-group-item-info { - color: #41626e; - background-color: #e2edf1; +.offcanvas-header { + display: flex; + align-items: center; + padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); } -.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { - color: #41626e; - background-color: #cbd5d9; +.offcanvas-header .btn-close { + padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5); + margin: calc(-0.5 * var(--bs-offcanvas-padding-y)) calc(-0.5 * var(--bs-offcanvas-padding-x)) calc(-0.5 * var(--bs-offcanvas-padding-y)) auto; } -.list-group-item-info.list-group-item-action.active { - color: #fff; - background-color: #41626e; - border-color: #41626e; + +.offcanvas-title { + margin-bottom: 0; + line-height: var(--bs-offcanvas-title-line-height); } -.list-group-item-warning { - color: #804d2e; - background-color: #f7e6db; +.offcanvas-body { + flex-grow: 1; + padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); + overflow-y: auto; } -.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { - color: #804d2e; - background-color: #decfc5; + +.placeholder { + display: inline-block; + min-height: 1em; + vertical-align: middle; + cursor: wait; + background-color: currentcolor; + opacity: 0.5; } -.list-group-item-warning.list-group-item-action.active { - color: #fff; - background-color: #804d2e; - border-color: #804d2e; +.placeholder.btn::before { + display: inline-block; + content: ""; } -.list-group-item-danger { - color: #762e29; - background-color: #f3dbda; +.placeholder-xs { + min-height: 0.6em; } -.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { - color: #762e29; - background-color: #dbc5c4; + +.placeholder-sm { + min-height: 0.8em; } -.list-group-item-danger.list-group-item-action.active { - color: #fff; - background-color: #762e29; - border-color: #762e29; + +.placeholder-lg { + min-height: 1.2em; } -.list-group-item-light { - color: #626466; - background-color: #fdfeff; +.placeholder-glow .placeholder { + animation: placeholder-glow 2s ease-in-out infinite; } -.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { - color: #626466; - background-color: #e4e5e6; + +@keyframes placeholder-glow { + 50% { + opacity: 0.2; + } } -.list-group-item-light.list-group-item-action.active { - color: #fff; - background-color: #626466; - border-color: #626466; +.placeholder-wave { + mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); + mask-size: 200% 100%; + animation: placeholder-wave 2s linear infinite; } -.list-group-item-dark { - color: #1d2b38; - background-color: #d6dadf; -} -.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { - color: #1d2b38; - background-color: #c1c4c9; +@keyframes placeholder-wave { + 100% { + mask-position: -200% 0%; + } } -.list-group-item-dark.list-group-item-action.active { - color: #fff; - background-color: #1d2b38; - border-color: #1d2b38; +.clearfix::after { + display: block; + clear: both; + content: ""; } -.btn-close { - box-sizing: content-box; - width: 1em; - height: 1em; - padding: 0.25em 0.25em; - color: #000; - background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat; - border: 0; - border-radius: 0.25rem; - opacity: 0.5; +.text-bg-primary { + color: #000 !important; + background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; } -.btn-close:hover { - color: #000; - text-decoration: none; - opacity: 0.75; -} -.btn-close:focus { - outline: 0; - box-shadow: 0 0 0 0.25rem rgba(66, 118, 167, 0.25); - opacity: 1; + +.text-bg-secondary { + color: #000 !important; + background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; } -.btn-close:disabled, .btn-close.disabled { - pointer-events: none; - user-select: none; - opacity: 0.25; + +.text-bg-success { + color: #fff !important; + background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; } -.btn-close-white { - filter: invert(1) grayscale(100%) brightness(200%); +.text-bg-info { + color: #000 !important; + background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal { - position: fixed; - top: 0; - left: 0; - z-index: 1055; - display: none; - width: 100%; - height: 100%; - overflow-x: hidden; - overflow-y: auto; - outline: 0; +.text-bg-warning { + color: #000 !important; + background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal-dialog { - position: relative; - width: auto; - margin: 0.5rem; - pointer-events: none; +.text-bg-danger { + color: #fff !important; + background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal.fade .modal-dialog { - transition: transform 0.3s ease-out; - transform: translate(0, -50px); + +.text-bg-light { + color: #000 !important; + background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; } -@media (prefers-reduced-motion: reduce) { - .modal.fade .modal-dialog { - transition: none; - } + +.text-bg-dark { + color: #fff !important; + background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal.show .modal-dialog { - transform: none; + +.text-bg-accent { + color: #000 !important; + background-color: RGBA(var(--bs-accent-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal.modal-static .modal-dialog { - transform: scale(1.02); + +.text-bg-cinereous { + color: #000 !important; + background-color: RGBA(var(--bs-cinereous-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal-dialog-scrollable { - height: calc(100% - 1rem); +.text-bg-verdigris { + color: #000 !important; + background-color: RGBA(var(--bs-verdigris-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal-dialog-scrollable .modal-content { - max-height: 100%; - overflow: hidden; + +.text-bg-icterine { + color: #000 !important; + background-color: RGBA(var(--bs-icterine-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal-dialog-scrollable .modal-body { - overflow-y: auto; + +.text-bg-mute { + color: #fff !important; + background-color: RGBA(var(--bs-mute-rgb), var(--bs-bg-opacity, 1)) !important; } -.modal-dialog-centered { - display: flex; - align-items: center; - min-height: calc(100% - 1rem); +.link-primary { + color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; +} +.link-primary:hover, .link-primary:focus { + color: RGBA(163, 134, 215, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(163, 134, 215, var(--bs-link-underline-opacity, 1)) !important; } -.modal-content { - position: relative; - display: flex; - flex-direction: column; - width: 100%; - pointer-events: auto; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; - outline: 0; +.link-secondary { + color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; +} +.link-secondary:hover, .link-secondary:focus { + color: RGBA(99, 163, 222, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(99, 163, 222, var(--bs-link-underline-opacity, 1)) !important; } -.modal-backdrop { - position: fixed; - top: 0; - left: 0; - z-index: 1050; - width: 100vw; - height: 100vh; - background-color: #000; +.link-success { + color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; } -.modal-backdrop.fade { - opacity: 0; +.link-success:hover, .link-success:focus { + color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; } -.modal-backdrop.show { - opacity: 0.5; + +.link-info { + color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; +} +.link-info:hover, .link-info:focus { + color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; } -.modal-header { - display: flex; - flex-shrink: 0; - align-items: center; - justify-content: space-between; - padding: 1rem 1rem; - border-bottom: 1px solid #dee2e6; - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); +.link-warning { + color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; } -.modal-header .btn-close { - padding: 0.5rem 0.5rem; - margin: -0.5rem -0.5rem -0.5rem auto; +.link-warning:hover, .link-warning:focus { + color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; } -.modal-title { - margin-bottom: 0; - line-height: 1.5; +.link-danger { + color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; +} +.link-danger:hover, .link-danger:focus { + color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; } -.modal-body { - position: relative; - flex: 1 1 auto; - padding: 1rem; +.link-light { + color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; +} +.link-light:hover, .link-light:focus { + color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; } -.modal-footer { - display: flex; - flex-wrap: wrap; - flex-shrink: 0; - align-items: center; - justify-content: flex-end; - padding: 0.75rem; - border-top: 1px solid #dee2e6; - border-bottom-right-radius: calc(0.3rem - 1px); - border-bottom-left-radius: calc(0.3rem - 1px); +.link-dark { + color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; } -.modal-footer > * { - margin: 0.25rem; +.link-dark:hover, .link-dark:focus { + color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; } -@media (min-width: 576px) { - .modal-dialog { - max-width: 500px; - margin: 1.75rem auto; - } - .modal-dialog-scrollable { - height: calc(100% - 3.5rem); - } - .modal-dialog-centered { - min-height: calc(100% - 3.5rem); - } - .modal-sm { - max-width: 300px; - } +.link-accent { + color: RGBA(var(--bs-accent-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-accent-rgb), var(--bs-link-underline-opacity, 1)) !important; } -@media (min-width: 992px) { - .modal-lg, - .modal-xl { - max-width: 800px; - } +.link-accent:hover, .link-accent:focus { + color: RGBA(176, 240, 124, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(176, 240, 124, var(--bs-link-underline-opacity, 1)) !important; } -@media (min-width: 1200px) { - .modal-xl { - max-width: 1140px; - } + +.link-cinereous { + color: RGBA(var(--bs-cinereous-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-cinereous-rgb), var(--bs-link-underline-opacity, 1)) !important; } -.modal-fullscreen { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; +.link-cinereous:hover, .link-cinereous:focus { + color: RGBA(156, 145, 135, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(156, 145, 135, var(--bs-link-underline-opacity, 1)) !important; } -.modal-fullscreen .modal-content { - height: 100%; - border: 0; - border-radius: 0; + +.link-verdigris { + color: RGBA(var(--bs-verdigris-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-verdigris-rgb), var(--bs-link-underline-opacity, 1)) !important; } -.modal-fullscreen .modal-header { - border-radius: 0; +.link-verdigris:hover, .link-verdigris:focus { + color: RGBA(115, 209, 197, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(115, 209, 197, var(--bs-link-underline-opacity, 1)) !important; } -.modal-fullscreen .modal-body { - overflow-y: auto; + +.link-icterine { + color: RGBA(var(--bs-icterine-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-icterine-rgb), var(--bs-link-underline-opacity, 1)) !important; } -.modal-fullscreen .modal-footer { - border-radius: 0; +.link-icterine:hover, .link-icterine:focus { + color: RGBA(243, 246, 132, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(243, 246, 132, var(--bs-link-underline-opacity, 1)) !important; } -@media (max-width: 575.98px) { - .modal-fullscreen-sm-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-sm-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-sm-down .modal-header { - border-radius: 0; - } - .modal-fullscreen-sm-down .modal-body { - overflow-y: auto; - } - .modal-fullscreen-sm-down .modal-footer { - border-radius: 0; - } +.link-mute { + color: RGBA(var(--bs-mute-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-mute-rgb), var(--bs-link-underline-opacity, 1)) !important; } -@media (max-width: 767.98px) { - .modal-fullscreen-md-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-md-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-md-down .modal-header { - border-radius: 0; - } - .modal-fullscreen-md-down .modal-body { - overflow-y: auto; - } - .modal-fullscreen-md-down .modal-footer { - border-radius: 0; - } +.link-mute:hover, .link-mute:focus { + color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; } -@media (max-width: 991.98px) { - .modal-fullscreen-lg-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-lg-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-lg-down .modal-header { - border-radius: 0; - } - .modal-fullscreen-lg-down .modal-body { - overflow-y: auto; - } - .modal-fullscreen-lg-down .modal-footer { - border-radius: 0; - } + +.link-body-emphasis { + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } -@media (max-width: 1199.98px) { - .modal-fullscreen-xl-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-xl-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-xl-down .modal-header { - border-radius: 0; - } - .modal-fullscreen-xl-down .modal-body { - overflow-y: auto; - } - .modal-fullscreen-xl-down .modal-footer { - border-radius: 0; - } +.link-body-emphasis:hover, .link-body-emphasis:focus { + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; } -@media (max-width: 1399.98px) { - .modal-fullscreen-xxl-down { - width: 100vw; - max-width: none; - height: 100%; - margin: 0; - } - .modal-fullscreen-xxl-down .modal-content { - height: 100%; - border: 0; - border-radius: 0; - } - .modal-fullscreen-xxl-down .modal-header { - border-radius: 0; - } - .modal-fullscreen-xxl-down .modal-body { - overflow-y: auto; - } - .modal-fullscreen-xxl-down .modal-footer { - border-radius: 0; - } + +.focus-ring:focus { + outline: 0; + box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); } -.tooltip { - position: absolute; - z-index: 1080; - display: block; - margin: 0; - font-family: "Source Sans Pro", sans-serif; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - opacity: 0; + +.icon-link { + display: inline-flex; + gap: 0.375rem; + align-items: center; + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-underline-offset: 0.25em; + backface-visibility: hidden; +} +.icon-link > .bi { + flex-shrink: 0; + width: 1em; + height: 1em; + fill: currentcolor; + transition: 0.2s ease-in-out transform; +} +@media (prefers-reduced-motion: reduce) { + .icon-link > .bi { + transition: none; + } } -.tooltip.show { - opacity: 0.9; + +.icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi { + transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); } -.tooltip .tooltip-arrow { - position: absolute; + +.ratio { + position: relative; + width: 100%; +} +.ratio::before { display: block; - width: 0.8rem; - height: 0.4rem; + padding-top: var(--bs-aspect-ratio); + content: ""; } -.tooltip .tooltip-arrow::before { +.ratio > * { position: absolute; - content: ""; - border-color: transparent; - border-style: solid; + top: 0; + left: 0; + width: 100%; + height: 100%; } -.bs-tooltip-top, .bs-tooltip-auto[data-popper-placement^=top] { - padding: 0.4rem 0; +.ratio-1x1 { + --bs-aspect-ratio: 100%; } -.bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { - bottom: 0; + +.ratio-4x3 { + --bs-aspect-ratio: 75%; } -.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { - top: -1px; - border-width: 0.4rem 0.4rem 0; - border-top-color: #000; + +.ratio-16x9 { + --bs-aspect-ratio: 56.25%; } -.bs-tooltip-end, .bs-tooltip-auto[data-popper-placement^=right] { - padding: 0 0.4rem; +.ratio-21x9 { + --bs-aspect-ratio: 42.8571428571%; } -.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { + +.fixed-top { + position: fixed; + top: 0; + right: 0; left: 0; - width: 0.4rem; - height: 0.8rem; -} -.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before { - right: -1px; - border-width: 0.4rem 0.4rem 0.4rem 0; - border-right-color: #000; + z-index: 1030; } -.bs-tooltip-bottom, .bs-tooltip-auto[data-popper-placement^=bottom] { - padding: 0.4rem 0; +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; } -.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { + +.sticky-top { + position: sticky; top: 0; + z-index: 1020; } -.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { - bottom: -1px; - border-width: 0 0.4rem 0.4rem; - border-bottom-color: #000; + +.sticky-bottom { + position: sticky; + bottom: 0; + z-index: 1020; } -.bs-tooltip-start, .bs-tooltip-auto[data-popper-placement^=left] { - padding: 0 0.4rem; +@media (min-width: 576px) { + .sticky-sm-top { + position: sticky; + top: 0; + z-index: 1020; + } + .sticky-sm-bottom { + position: sticky; + bottom: 0; + z-index: 1020; + } } -.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { - right: 0; - width: 0.4rem; - height: 0.8rem; +@media (min-width: 768px) { + .sticky-md-top { + position: sticky; + top: 0; + z-index: 1020; + } + .sticky-md-bottom { + position: sticky; + bottom: 0; + z-index: 1020; + } } -.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before { - left: -1px; - border-width: 0.4rem 0 0.4rem 0.4rem; - border-left-color: #000; +@media (min-width: 992px) { + .sticky-lg-top { + position: sticky; + top: 0; + z-index: 1020; + } + .sticky-lg-bottom { + position: sticky; + bottom: 0; + z-index: 1020; + } +} +@media (min-width: 1200px) { + .sticky-xl-top { + position: sticky; + top: 0; + z-index: 1020; + } + .sticky-xl-bottom { + position: sticky; + bottom: 0; + z-index: 1020; + } +} +@media (min-width: 1400px) { + .sticky-xxl-top { + position: sticky; + top: 0; + z-index: 1020; + } + .sticky-xxl-bottom { + position: sticky; + bottom: 0; + z-index: 1020; + } +} +.hstack { + display: flex; + flex-direction: row; + align-items: center; + align-self: stretch; } -.tooltip-inner { - max-width: 200px; - padding: 0.25rem 0.5rem; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 0.25rem; +.vstack { + display: flex; + flex: 1 1 auto; + flex-direction: column; + align-self: stretch; } -.popover { - position: absolute; - top: 0; - left: 0 /* rtl:ignore */; - z-index: 1070; - display: block; - max-width: 276px; - font-family: "Source Sans Pro", sans-serif; - font-style: normal; - font-weight: 400; - line-height: 1.5; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - white-space: normal; - line-break: auto; - font-size: 0.875rem; - word-wrap: break-word; - background-color: #fff; - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0.2); - border-radius: 0.3rem; +.visually-hidden, +.visually-hidden-focusable:not(:focus):not(:focus-within) { + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; } -.popover .popover-arrow { - position: absolute; - display: block; - width: 1rem; - height: 0.5rem; +.visually-hidden:not(caption), +.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) { + position: absolute !important; } -.popover .popover-arrow::before, .popover .popover-arrow::after { + +.stretched-link::after { position: absolute; - display: block; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; content: ""; - border-color: transparent; - border-style: solid; } -.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow { - bottom: calc(-0.5rem - 1px); +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before { - bottom: 0; - border-width: 0.5rem 0.5rem 0; - border-top-color: rgba(0, 0, 0, 0.25); + +.vr { + display: inline-block; + align-self: stretch; + width: var(--bs-border-width); + min-height: 1em; + background-color: currentcolor; + opacity: 0.25; } -.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after { - bottom: 1px; - border-width: 0.5rem 0.5rem 0; - border-top-color: #fff; + +.align-baseline { + vertical-align: baseline !important; } -.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow { - left: calc(-0.5rem - 1px); - width: 0.5rem; - height: 1rem; +.align-top { + vertical-align: top !important; } -.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before { - left: 0; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: rgba(0, 0, 0, 0.25); + +.align-middle { + vertical-align: middle !important; } -.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after { - left: 1px; - border-width: 0.5rem 0.5rem 0.5rem 0; - border-right-color: #fff; + +.align-bottom { + vertical-align: bottom !important; } -.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow { - top: calc(-0.5rem - 1px); +.align-text-bottom { + vertical-align: text-bottom !important; } -.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before { - top: 0; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: rgba(0, 0, 0, 0.25); + +.align-text-top { + vertical-align: text-top !important; } -.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after { - top: 1px; - border-width: 0 0.5rem 0.5rem 0.5rem; - border-bottom-color: #fff; + +.float-start { + float: left !important; } -.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before { - position: absolute; - top: 0; - left: 50%; - display: block; - width: 1rem; - margin-left: -0.5rem; - content: ""; - border-bottom: 1px solid #f0f0f0; + +.float-end { + float: right !important; } -.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow { - right: calc(-0.5rem - 1px); - width: 0.5rem; - height: 1rem; +.float-none { + float: none !important; } -.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before { - right: 0; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: rgba(0, 0, 0, 0.25); + +.object-fit-contain { + object-fit: contain !important; } -.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after { - right: 1px; - border-width: 0.5rem 0 0.5rem 0.5rem; - border-left-color: #fff; + +.object-fit-cover { + object-fit: cover !important; } -.popover-header { - padding: 0.5rem 1rem; - margin-bottom: 0; - font-size: 1rem; - background-color: #f0f0f0; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - border-top-left-radius: calc(0.3rem - 1px); - border-top-right-radius: calc(0.3rem - 1px); +.object-fit-fill { + object-fit: fill !important; } -.popover-header:empty { - display: none; + +.object-fit-scale { + object-fit: scale-down !important; +} + +.object-fit-none { + object-fit: none !important; } -.popover-body { - padding: 1rem 1rem; - color: #00305E; +.opacity-0 { + opacity: 0 !important; } -.carousel { - position: relative; +.opacity-25 { + opacity: 0.25 !important; } -.carousel.pointer-event { - touch-action: pan-y; +.opacity-50 { + opacity: 0.5 !important; } -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; +.opacity-75 { + opacity: 0.75 !important; } -.carousel-inner::after { - display: block; - clear: both; - content: ""; + +.opacity-100 { + opacity: 1 !important; } -.carousel-item { - position: relative; - display: none; - float: left; - width: 100%; - margin-right: -100%; - backface-visibility: hidden; - transition: transform 0.6s ease-in-out; +.overflow-auto { + overflow: auto !important; } -@media (prefers-reduced-motion: reduce) { - .carousel-item { - transition: none; - } + +.overflow-hidden { + overflow: hidden !important; } -.carousel-item.active, -.carousel-item-next, -.carousel-item-prev { - display: block; +.overflow-visible { + overflow: visible !important; } -/* rtl:begin:ignore */ -.carousel-item-next:not(.carousel-item-start), -.active.carousel-item-end { - transform: translateX(100%); +.overflow-scroll { + overflow: scroll !important; } -.carousel-item-prev:not(.carousel-item-end), -.active.carousel-item-start { - transform: translateX(-100%); +.overflow-x-auto { + overflow-x: auto !important; } -/* rtl:end:ignore */ -.carousel-fade .carousel-item { - opacity: 0; - transition-property: opacity; - transform: none; +.overflow-x-hidden { + overflow-x: hidden !important; } -.carousel-fade .carousel-item.active, -.carousel-fade .carousel-item-next.carousel-item-start, -.carousel-fade .carousel-item-prev.carousel-item-end { - z-index: 1; - opacity: 1; + +.overflow-x-visible { + overflow-x: visible !important; } -.carousel-fade .active.carousel-item-start, -.carousel-fade .active.carousel-item-end { - z-index: 0; - opacity: 0; - transition: opacity 0s 0.6s; + +.overflow-x-scroll { + overflow-x: scroll !important; } -@media (prefers-reduced-motion: reduce) { - .carousel-fade .active.carousel-item-start, - .carousel-fade .active.carousel-item-end { - transition: none; - } + +.overflow-y-auto { + overflow-y: auto !important; } -.carousel-control-prev, -.carousel-control-next { - position: absolute; - top: 0; - bottom: 0; - z-index: 1; - display: flex; - align-items: center; - justify-content: center; - width: 15%; - padding: 0; - color: #fff; - text-align: center; - background: none; - border: 0; - opacity: 0.5; - transition: opacity 0.15s ease; +.overflow-y-hidden { + overflow-y: hidden !important; } -@media (prefers-reduced-motion: reduce) { - .carousel-control-prev, - .carousel-control-next { - transition: none; - } + +.overflow-y-visible { + overflow-y: visible !important; } -.carousel-control-prev:hover, .carousel-control-prev:focus, -.carousel-control-next:hover, -.carousel-control-next:focus { - color: #fff; - text-decoration: none; - outline: 0; - opacity: 0.9; + +.overflow-y-scroll { + overflow-y: scroll !important; } -.carousel-control-prev { - left: 0; +.d-inline { + display: inline !important; } -.carousel-control-next { - right: 0; +.d-inline-block { + display: inline-block !important; } -.carousel-control-prev-icon, -.carousel-control-next-icon { - display: inline-block; - width: 2rem; - height: 2rem; - background-repeat: no-repeat; - background-position: 50%; - background-size: 100% 100%; +.d-block { + display: block !important; } -/* rtl:options: { - "autoRename": true, - "stringMap":[ { - "name" : "prev-next", - "search" : "prev", - "replace" : "next" - } ] -} */ -.carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e"); +.d-grid { + display: grid !important; } -.carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); +.d-inline-grid { + display: inline-grid !important; } -.carousel-indicators { - position: absolute; - right: 0; - bottom: 0; - left: 0; - z-index: 2; - display: flex; - justify-content: center; - padding: 0; - margin-right: 15%; - margin-bottom: 1rem; - margin-left: 15%; - list-style: none; +.d-table { + display: table !important; } -.carousel-indicators [data-bs-target] { - box-sizing: content-box; - flex: 0 1 auto; - width: 30px; - height: 3px; - padding: 0; - margin-right: 3px; - margin-left: 3px; - text-indent: -999px; - cursor: pointer; - background-color: #fff; - background-clip: padding-box; - border: 0; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - opacity: 0.5; - transition: opacity 0.6s ease; + +.d-table-row { + display: table-row !important; } -@media (prefers-reduced-motion: reduce) { - .carousel-indicators [data-bs-target] { - transition: none; - } + +.d-table-cell { + display: table-cell !important; } -.carousel-indicators .active { - opacity: 1; + +.d-flex { + display: flex !important; } -.carousel-caption { - position: absolute; - right: 15%; - bottom: 1.25rem; - left: 15%; - padding-top: 1.25rem; - padding-bottom: 1.25rem; - color: #fff; - text-align: center; +.d-inline-flex { + display: inline-flex !important; } -.carousel-dark .carousel-control-prev-icon, -.carousel-dark .carousel-control-next-icon { - filter: invert(1) grayscale(100); +.d-none { + display: none !important; } -.carousel-dark .carousel-indicators [data-bs-target] { - background-color: #000; + +.shadow { + box-shadow: var(--bs-box-shadow) !important; } -.carousel-dark .carousel-caption { - color: #000; + +.shadow-sm { + box-shadow: var(--bs-box-shadow-sm) !important; } -@keyframes spinner-border { - to { - transform: rotate(360deg) /* rtl:ignore */; - } +.shadow-lg { + box-shadow: var(--bs-box-shadow-lg) !important; } -.spinner-border { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: -0.125em; - border: 0.25em solid currentColor; - border-right-color: transparent; - border-radius: 50%; - animation: 0.75s linear infinite spinner-border; + +.shadow-none { + box-shadow: none !important; } -.spinner-border-sm { - width: 1rem; - height: 1rem; - border-width: 0.2em; +.focus-ring-primary { + --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); } -@keyframes spinner-grow { - 0% { - transform: scale(0); - } - 50% { - opacity: 1; - transform: none; - } +.focus-ring-secondary { + --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); } -.spinner-grow { - display: inline-block; - width: 2rem; - height: 2rem; - vertical-align: -0.125em; - background-color: currentColor; - border-radius: 50%; - opacity: 0; - animation: 0.75s linear infinite spinner-grow; + +.focus-ring-success { + --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); } -.spinner-grow-sm { - width: 1rem; - height: 1rem; +.focus-ring-info { + --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); } -@media (prefers-reduced-motion: reduce) { - .spinner-border, - .spinner-grow { - animation-duration: 1.5s; - } +.focus-ring-warning { + --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas { - position: fixed; - bottom: 0; - z-index: 1045; - display: flex; - flex-direction: column; - max-width: 100%; - visibility: hidden; - background-color: #fff; - background-clip: padding-box; - outline: 0; - transition: transform 0.3s ease-in-out; + +.focus-ring-danger { + --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); } -@media (prefers-reduced-motion: reduce) { - .offcanvas { - transition: none; - } + +.focus-ring-light { + --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas-backdrop { - position: fixed; - top: 0; - left: 0; - z-index: 1040; - width: 100vw; - height: 100vh; - background-color: #000; +.focus-ring-dark { + --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas-backdrop.fade { - opacity: 0; + +.focus-ring-accent { + --bs-focus-ring-color: rgba(var(--bs-accent-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas-backdrop.show { - opacity: 0.5; + +.focus-ring-cinereous { + --bs-focus-ring-color: rgba(var(--bs-cinereous-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 1rem 1rem; +.focus-ring-verdigris { + --bs-focus-ring-color: rgba(var(--bs-verdigris-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas-header .btn-close { - padding: 0.5rem 0.5rem; - margin-top: -0.5rem; - margin-right: -0.5rem; - margin-bottom: -0.5rem; + +.focus-ring-icterine { + --bs-focus-ring-color: rgba(var(--bs-icterine-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas-title { - margin-bottom: 0; - line-height: 1.5; +.focus-ring-mute { + --bs-focus-ring-color: rgba(var(--bs-mute-rgb), var(--bs-focus-ring-opacity)); } -.offcanvas-body { - flex-grow: 1; - padding: 1rem 1rem; - overflow-y: auto; +.position-static { + position: static !important; } -.offcanvas-start { - top: 0; - left: 0; - width: 400px; - border-right: 1px solid rgba(0, 0, 0, 0.2); - transform: translateX(-100%); +.position-relative { + position: relative !important; } -.offcanvas-end { - top: 0; - right: 0; - width: 400px; - border-left: 1px solid rgba(0, 0, 0, 0.2); - transform: translateX(100%); +.position-absolute { + position: absolute !important; } -.offcanvas-top { - top: 0; - right: 0; - left: 0; - height: 30vh; - max-height: 100%; - border-bottom: 1px solid rgba(0, 0, 0, 0.2); - transform: translateY(-100%); +.position-fixed { + position: fixed !important; } -.offcanvas-bottom { - right: 0; - left: 0; - height: 30vh; - max-height: 100%; - border-top: 1px solid rgba(0, 0, 0, 0.2); - transform: translateY(100%); +.position-sticky { + position: sticky !important; } -.offcanvas.show { - transform: none; +.top-0 { + top: 0 !important; } -.placeholder { - display: inline-block; - min-height: 1em; - vertical-align: middle; - cursor: wait; - background-color: currentColor; - opacity: 0.5; +.top-50 { + top: 50% !important; } -.placeholder.btn::before { - display: inline-block; - content: ""; + +.top-100 { + top: 100% !important; } -.placeholder-xs { - min-height: 0.6em; +.bottom-0 { + bottom: 0 !important; } -.placeholder-sm { - min-height: 0.8em; +.bottom-50 { + bottom: 50% !important; } -.placeholder-lg { - min-height: 1.2em; +.bottom-100 { + bottom: 100% !important; } -.placeholder-glow .placeholder { - animation: placeholder-glow 2s ease-in-out infinite; +.start-0 { + left: 0 !important; } -@keyframes placeholder-glow { - 50% { - opacity: 0.2; - } +.start-50 { + left: 50% !important; } -.placeholder-wave { - mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%); - mask-size: 200% 100%; - animation: placeholder-wave 2s linear infinite; + +.start-100 { + left: 100% !important; } -@keyframes placeholder-wave { - 100% { - mask-position: -200% 0%; - } +.end-0 { + right: 0 !important; } -.clearfix::after { - display: block; - clear: both; - content: ""; + +.end-50 { + right: 50% !important; } -.link-primary { - color: #4276A7; +.end-100 { + right: 100% !important; } -.link-primary:hover, .link-primary:focus { - color: #355e86; + +.translate-middle { + transform: translate(-50%, -50%) !important; } -.link-secondary { - color: #6c757d; +.translate-middle-x { + transform: translateX(-50%) !important; } -.link-secondary:hover, .link-secondary:focus { - color: #565e64; + +.translate-middle-y { + transform: translateY(-50%) !important; } -.link-success { - color: #6EAB63; +.border { + border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } -.link-success:hover, .link-success:focus { - color: #8bbc82; + +.border-0 { + border: 0 !important; } -.link-info { - color: #6DA3B7; +.border-top { + border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } -.link-info:hover, .link-info:focus { - color: #8ab5c5; + +.border-top-0 { + border-top: 0 !important; } -.link-warning { - color: #D5804C; +.border-end { + border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } -.link-warning:hover, .link-warning:focus { - color: #dd9970; + +.border-end-0 { + border-right: 0 !important; } -.link-danger { - color: #C54C45; +.border-bottom { + border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } -.link-danger:hover, .link-danger:focus { - color: #9e3d37; + +.border-bottom-0 { + border-bottom: 0 !important; } -.link-light { - color: #F5FAFF; +.border-start { + border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important; } -.link-light:hover, .link-light:focus { - color: #f7fbff; + +.border-start-0 { + border-left: 0 !important; } -.link-dark { - color: #30475D; +.border-primary { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important; } -.link-dark:hover, .link-dark:focus { - color: #26394a; + +.border-secondary { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; } -.ratio { - position: relative; - width: 100%; +.border-success { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important; } -.ratio::before { - display: block; - padding-top: var(--bs-aspect-ratio); - content: ""; + +.border-info { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important; } -.ratio > * { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; + +.border-warning { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important; } -.ratio-1x1 { - --bs-aspect-ratio: 100%; +.border-danger { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important; } -.ratio-4x3 { - --bs-aspect-ratio: 75%; +.border-light { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important; } -.ratio-16x9 { - --bs-aspect-ratio: 56.25%; +.border-dark { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } -.ratio-21x9 { - --bs-aspect-ratio: 42.8571428571%; +.border-black { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; } -.fixed-top { - position: fixed; - top: 0; - right: 0; - left: 0; - z-index: 1030; +.border-white { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } -.fixed-bottom { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 1030; +.border-primary-subtle { + border-color: var(--bs-primary-border-subtle) !important; } -.sticky-top { - position: sticky; - top: 0; - z-index: 1020; +.border-secondary-subtle { + border-color: var(--bs-secondary-border-subtle) !important; } -@media (min-width: 576px) { - .sticky-sm-top { - position: sticky; - top: 0; - z-index: 1020; - } +.border-success-subtle { + border-color: var(--bs-success-border-subtle) !important; } -@media (min-width: 768px) { - .sticky-md-top { - position: sticky; - top: 0; - z-index: 1020; - } + +.border-info-subtle { + border-color: var(--bs-info-border-subtle) !important; } -@media (min-width: 992px) { - .sticky-lg-top { - position: sticky; - top: 0; - z-index: 1020; - } + +.border-warning-subtle { + border-color: var(--bs-warning-border-subtle) !important; } -@media (min-width: 1200px) { - .sticky-xl-top { - position: sticky; - top: 0; - z-index: 1020; - } + +.border-danger-subtle { + border-color: var(--bs-danger-border-subtle) !important; } -@media (min-width: 1400px) { - .sticky-xxl-top { - position: sticky; - top: 0; - z-index: 1020; - } + +.border-light-subtle { + border-color: var(--bs-light-border-subtle) !important; } -.hstack { - display: flex; - flex-direction: row; - align-items: center; - align-self: stretch; + +.border-dark-subtle { + border-color: var(--bs-dark-border-subtle) !important; +} + +.border-accent { + border-color: #9CEC5B !important; +} + +.border-cinereous { + border-color: #837569 !important; } -.vstack { - display: flex; - flex: 1 1 auto; - flex-direction: column; - align-self: stretch; +.border-verdigris { + border-color: #50C5B7 !important; } -.visually-hidden, -.visually-hidden-focusable:not(:focus):not(:focus-within) { - position: absolute !important; - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - white-space: nowrap !important; - border: 0 !important; +.border-icterine { + border-color: #F0F465 !important; } -.stretched-link::after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1; - content: ""; +.border-mute { + border-color: #6c757d !important; } -.text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; +.border-1 { + border-width: 1px !important; } -.vr { - display: inline-block; - align-self: stretch; - width: 1px; - min-height: 1em; - background-color: currentColor; - opacity: 0.25; +.border-2 { + border-width: 2px !important; } -.align-baseline { - vertical-align: baseline !important; +.border-3 { + border-width: 3px !important; } -.align-top { - vertical-align: top !important; +.border-4 { + border-width: 4px !important; } -.align-middle { - vertical-align: middle !important; +.border-5 { + border-width: 5px !important; } -.align-bottom { - vertical-align: bottom !important; +.border-opacity-10 { + --bs-border-opacity: 0.1; } -.align-text-bottom { - vertical-align: text-bottom !important; +.border-opacity-25 { + --bs-border-opacity: 0.25; } -.align-text-top { - vertical-align: text-top !important; +.border-opacity-50 { + --bs-border-opacity: 0.5; } -.float-start { - float: left !important; +.border-opacity-75 { + --bs-border-opacity: 0.75; } -.float-end { - float: right !important; +.border-opacity-100 { + --bs-border-opacity: 1; } -.float-none { - float: none !important; +.w-25 { + width: 25% !important; } -.opacity-0 { - opacity: 0 !important; +.w-50 { + width: 50% !important; } -.opacity-25 { - opacity: 0.25 !important; +.w-75 { + width: 75% !important; } -.opacity-50 { - opacity: 0.5 !important; +.w-100 { + width: 100% !important; } -.opacity-75 { - opacity: 0.75 !important; +.w-auto { + width: auto !important; } -.opacity-100 { - opacity: 1 !important; +.mw-100 { + max-width: 100% !important; } -.overflow-auto { - overflow: auto !important; +.vw-100 { + width: 100vw !important; } -.overflow-hidden { - overflow: hidden !important; +.min-vw-100 { + min-width: 100vw !important; } -.overflow-visible { - overflow: visible !important; +.h-25 { + height: 25% !important; } -.overflow-scroll { - overflow: scroll !important; +.h-50 { + height: 50% !important; } -.d-inline { - display: inline !important; +.h-75 { + height: 75% !important; } -.d-inline-block { - display: inline-block !important; +.h-100 { + height: 100% !important; } -.d-block { - display: block !important; +.h-auto { + height: auto !important; } -.d-grid { - display: grid !important; +.mh-100 { + max-height: 100% !important; } -.d-table { - display: table !important; +.vh-100 { + height: 100vh !important; } -.d-table-row { - display: table-row !important; +.min-vh-100 { + min-height: 100vh !important; } -.d-table-cell { - display: table-cell !important; +.flex-fill { + flex: 1 1 auto !important; } -.d-flex { - display: flex !important; +.flex-row { + flex-direction: row !important; } -.d-inline-flex { - display: inline-flex !important; +.flex-column { + flex-direction: column !important; } -.d-none { - display: none !important; +.flex-row-reverse { + flex-direction: row-reverse !important; } -.shadow { - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; +.flex-column-reverse { + flex-direction: column-reverse !important; } -.shadow-sm { - box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; +.flex-grow-0 { + flex-grow: 0 !important; } -.shadow-lg { - box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; +.flex-grow-1 { + flex-grow: 1 !important; } -.shadow-none { - box-shadow: none !important; +.flex-shrink-0 { + flex-shrink: 0 !important; } -.position-static { - position: static !important; +.flex-shrink-1 { + flex-shrink: 1 !important; } -.position-relative { - position: relative !important; +.flex-wrap { + flex-wrap: wrap !important; } -.position-absolute { - position: absolute !important; +.flex-nowrap { + flex-wrap: nowrap !important; } -.position-fixed { - position: fixed !important; +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important; } -.position-sticky { - position: sticky !important; +.justify-content-start { + justify-content: flex-start !important; } -.top-0 { - top: 0 !important; +.justify-content-end { + justify-content: flex-end !important; } -.top-50 { - top: 50% !important; +.justify-content-center { + justify-content: center !important; } -.top-100 { - top: 100% !important; +.justify-content-between { + justify-content: space-between !important; } -.bottom-0 { - bottom: 0 !important; +.justify-content-around { + justify-content: space-around !important; } -.bottom-50 { - bottom: 50% !important; +.justify-content-evenly { + justify-content: space-evenly !important; } -.bottom-100 { - bottom: 100% !important; +.align-items-start { + align-items: flex-start !important; } -.start-0 { - left: 0 !important; +.align-items-end { + align-items: flex-end !important; } -.start-50 { - left: 50% !important; +.align-items-center { + align-items: center !important; } -.start-100 { - left: 100% !important; +.align-items-baseline { + align-items: baseline !important; } -.end-0 { - right: 0 !important; +.align-items-stretch { + align-items: stretch !important; } -.end-50 { - right: 50% !important; +.align-content-start { + align-content: flex-start !important; } -.end-100 { - right: 100% !important; +.align-content-end { + align-content: flex-end !important; } -.translate-middle { - transform: translate(-50%, -50%) !important; +.align-content-center { + align-content: center !important; } -.translate-middle-x { - transform: translateX(-50%) !important; +.align-content-between { + align-content: space-between !important; } -.translate-middle-y { - transform: translateY(-50%) !important; +.align-content-around { + align-content: space-around !important; } -.border { - border: 1px solid #dee2e6 !important; +.align-content-stretch { + align-content: stretch !important; } -.border-0 { - border: 0 !important; +.align-self-auto { + align-self: auto !important; } -.border-top { - border-top: 1px solid #dee2e6 !important; +.align-self-start { + align-self: flex-start !important; } -.border-top-0 { - border-top: 0 !important; +.align-self-end { + align-self: flex-end !important; +} + +.align-self-center { + align-self: center !important; +} + +.align-self-baseline { + align-self: baseline !important; } -.border-end { - border-right: 1px solid #dee2e6 !important; +.align-self-stretch { + align-self: stretch !important; } -.border-end-0 { - border-right: 0 !important; +.order-first { + order: -1 !important; } -.border-bottom { - border-bottom: 1px solid #dee2e6 !important; +.order-0 { + order: 0 !important; } -.border-bottom-0 { - border-bottom: 0 !important; +.order-1 { + order: 1 !important; } -.border-start { - border-left: 1px solid #dee2e6 !important; +.order-2 { + order: 2 !important; } -.border-start-0 { - border-left: 0 !important; +.order-3 { + order: 3 !important; } -.border-primary { - border-color: #4276A7 !important; +.order-4 { + order: 4 !important; } -.border-secondary { - border-color: #6c757d !important; +.order-5 { + order: 5 !important; } -.border-success { - border-color: #6EAB63 !important; +.order-last { + order: 6 !important; } -.border-info { - border-color: #6DA3B7 !important; +.m-0 { + margin: 0 !important; } -.border-warning { - border-color: #D5804C !important; +.m-1 { + margin: 0.25rem !important; } -.border-danger { - border-color: #C54C45 !important; +.m-2 { + margin: 0.5rem !important; } -.border-light { - border-color: #F5FAFF !important; +.m-3 { + margin: 1rem !important; } -.border-dark { - border-color: #30475D !important; +.m-4 { + margin: 1.5rem !important; } -.border-white { - border-color: #fff !important; +.m-5 { + margin: 3rem !important; } -.border-1 { - border-width: 1px !important; +.m-auto { + margin: auto !important; } -.border-2 { - border-width: 2px !important; +.mx-0 { + margin-right: 0 !important; + margin-left: 0 !important; } -.border-3 { - border-width: 3px !important; +.mx-1 { + margin-right: 0.25rem !important; + margin-left: 0.25rem !important; } -.border-4 { - border-width: 4px !important; +.mx-2 { + margin-right: 0.5rem !important; + margin-left: 0.5rem !important; } -.border-5 { - border-width: 5px !important; +.mx-3 { + margin-right: 1rem !important; + margin-left: 1rem !important; } -.w-25 { - width: 25% !important; +.mx-4 { + margin-right: 1.5rem !important; + margin-left: 1.5rem !important; } -.w-50 { - width: 50% !important; +.mx-5 { + margin-right: 3rem !important; + margin-left: 3rem !important; } -.w-75 { - width: 75% !important; +.mx-auto { + margin-right: auto !important; + margin-left: auto !important; } -.w-100 { - width: 100% !important; +.my-0 { + margin-top: 0 !important; + margin-bottom: 0 !important; } -.w-auto { - width: auto !important; +.my-1 { + margin-top: 0.25rem !important; + margin-bottom: 0.25rem !important; } -.mw-100 { - max-width: 100% !important; +.my-2 { + margin-top: 0.5rem !important; + margin-bottom: 0.5rem !important; } -.vw-100 { - width: 100vw !important; +.my-3 { + margin-top: 1rem !important; + margin-bottom: 1rem !important; } -.min-vw-100 { - min-width: 100vw !important; +.my-4 { + margin-top: 1.5rem !important; + margin-bottom: 1.5rem !important; } -.h-25 { - height: 25% !important; +.my-5 { + margin-top: 3rem !important; + margin-bottom: 3rem !important; } -.h-50 { - height: 50% !important; +.my-auto { + margin-top: auto !important; + margin-bottom: auto !important; } -.h-75 { - height: 75% !important; +.mt-0 { + margin-top: 0 !important; } -.h-100 { - height: 100% !important; +.mt-1 { + margin-top: 0.25rem !important; } -.h-auto { - height: auto !important; +.mt-2 { + margin-top: 0.5rem !important; } -.mh-100 { - max-height: 100% !important; +.mt-3 { + margin-top: 1rem !important; } -.vh-100 { - height: 100vh !important; +.mt-4 { + margin-top: 1.5rem !important; } -.min-vh-100 { - min-height: 100vh !important; +.mt-5 { + margin-top: 3rem !important; } -.flex-fill { - flex: 1 1 auto !important; +.mt-auto { + margin-top: auto !important; } -.flex-row { - flex-direction: row !important; +.me-0 { + margin-right: 0 !important; } -.flex-column { - flex-direction: column !important; +.me-1 { + margin-right: 0.25rem !important; } -.flex-row-reverse { - flex-direction: row-reverse !important; +.me-2 { + margin-right: 0.5rem !important; } -.flex-column-reverse { - flex-direction: column-reverse !important; +.me-3 { + margin-right: 1rem !important; } -.flex-grow-0 { - flex-grow: 0 !important; +.me-4 { + margin-right: 1.5rem !important; } -.flex-grow-1 { - flex-grow: 1 !important; +.me-5 { + margin-right: 3rem !important; } -.flex-shrink-0 { - flex-shrink: 0 !important; +.me-auto { + margin-right: auto !important; } -.flex-shrink-1 { - flex-shrink: 1 !important; +.mb-0 { + margin-bottom: 0 !important; } -.flex-wrap { - flex-wrap: wrap !important; +.mb-1 { + margin-bottom: 0.25rem !important; } -.flex-nowrap { - flex-wrap: nowrap !important; +.mb-2 { + margin-bottom: 0.5rem !important; } -.flex-wrap-reverse { - flex-wrap: wrap-reverse !important; +.mb-3 { + margin-bottom: 1rem !important; } -.gap-0 { - gap: 0 !important; +.mb-4 { + margin-bottom: 1.5rem !important; } -.gap-1 { - gap: 0.25rem !important; +.mb-5 { + margin-bottom: 3rem !important; } -.gap-2 { - gap: 0.5rem !important; +.mb-auto { + margin-bottom: auto !important; } -.gap-3 { - gap: 1rem !important; +.ms-0 { + margin-left: 0 !important; } -.gap-4 { - gap: 1.5rem !important; +.ms-1 { + margin-left: 0.25rem !important; } -.gap-5 { - gap: 3rem !important; +.ms-2 { + margin-left: 0.5rem !important; } -.justify-content-start { - justify-content: flex-start !important; +.ms-3 { + margin-left: 1rem !important; } -.justify-content-end { - justify-content: flex-end !important; +.ms-4 { + margin-left: 1.5rem !important; } -.justify-content-center { - justify-content: center !important; +.ms-5 { + margin-left: 3rem !important; } -.justify-content-between { - justify-content: space-between !important; +.ms-auto { + margin-left: auto !important; } -.justify-content-around { - justify-content: space-around !important; +.p-0 { + padding: 0 !important; } -.justify-content-evenly { - justify-content: space-evenly !important; +.p-1 { + padding: 0.25rem !important; } -.align-items-start { - align-items: flex-start !important; +.p-2 { + padding: 0.5rem !important; } -.align-items-end { - align-items: flex-end !important; +.p-3 { + padding: 1rem !important; } -.align-items-center { - align-items: center !important; +.p-4 { + padding: 1.5rem !important; +} + +.p-5 { + padding: 3rem !important; } -.align-items-baseline { - align-items: baseline !important; +.px-0 { + padding-right: 0 !important; + padding-left: 0 !important; } -.align-items-stretch { - align-items: stretch !important; +.px-1 { + padding-right: 0.25rem !important; + padding-left: 0.25rem !important; } -.align-content-start { - align-content: flex-start !important; +.px-2 { + padding-right: 0.5rem !important; + padding-left: 0.5rem !important; } -.align-content-end { - align-content: flex-end !important; +.px-3 { + padding-right: 1rem !important; + padding-left: 1rem !important; } -.align-content-center { - align-content: center !important; +.px-4 { + padding-right: 1.5rem !important; + padding-left: 1.5rem !important; } -.align-content-between { - align-content: space-between !important; +.px-5 { + padding-right: 3rem !important; + padding-left: 3rem !important; } -.align-content-around { - align-content: space-around !important; +.py-0 { + padding-top: 0 !important; + padding-bottom: 0 !important; } -.align-content-stretch { - align-content: stretch !important; +.py-1 { + padding-top: 0.25rem !important; + padding-bottom: 0.25rem !important; } -.align-self-auto { - align-self: auto !important; +.py-2 { + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } -.align-self-start { - align-self: flex-start !important; +.py-3 { + padding-top: 1rem !important; + padding-bottom: 1rem !important; } -.align-self-end { - align-self: flex-end !important; +.py-4 { + padding-top: 1.5rem !important; + padding-bottom: 1.5rem !important; } -.align-self-center { - align-self: center !important; +.py-5 { + padding-top: 3rem !important; + padding-bottom: 3rem !important; } -.align-self-baseline { - align-self: baseline !important; +.pt-0 { + padding-top: 0 !important; } -.align-self-stretch { - align-self: stretch !important; +.pt-1 { + padding-top: 0.25rem !important; } -.order-first { - order: -1 !important; +.pt-2 { + padding-top: 0.5rem !important; } -.order-0 { - order: 0 !important; +.pt-3 { + padding-top: 1rem !important; } -.order-1 { - order: 1 !important; +.pt-4 { + padding-top: 1.5rem !important; } -.order-2 { - order: 2 !important; +.pt-5 { + padding-top: 3rem !important; } -.order-3 { - order: 3 !important; +.pe-0 { + padding-right: 0 !important; } -.order-4 { - order: 4 !important; +.pe-1 { + padding-right: 0.25rem !important; } -.order-5 { - order: 5 !important; +.pe-2 { + padding-right: 0.5rem !important; } -.order-last { - order: 6 !important; +.pe-3 { + padding-right: 1rem !important; } -.m-0 { - margin: 0 !important; +.pe-4 { + padding-right: 1.5rem !important; } -.m-1 { - margin: 0.25rem !important; +.pe-5 { + padding-right: 3rem !important; } -.m-2 { - margin: 0.5rem !important; +.pb-0 { + padding-bottom: 0 !important; } -.m-3 { - margin: 1rem !important; +.pb-1 { + padding-bottom: 0.25rem !important; } -.m-4 { - margin: 1.5rem !important; +.pb-2 { + padding-bottom: 0.5rem !important; } -.m-5 { - margin: 3rem !important; +.pb-3 { + padding-bottom: 1rem !important; } -.m-auto { - margin: auto !important; +.pb-4 { + padding-bottom: 1.5rem !important; } -.mx-0 { - margin-right: 0 !important; - margin-left: 0 !important; +.pb-5 { + padding-bottom: 3rem !important; } -.mx-1 { - margin-right: 0.25rem !important; - margin-left: 0.25rem !important; +.ps-0 { + padding-left: 0 !important; } -.mx-2 { - margin-right: 0.5rem !important; - margin-left: 0.5rem !important; +.ps-1 { + padding-left: 0.25rem !important; } -.mx-3 { - margin-right: 1rem !important; - margin-left: 1rem !important; +.ps-2 { + padding-left: 0.5rem !important; } -.mx-4 { - margin-right: 1.5rem !important; - margin-left: 1.5rem !important; +.ps-3 { + padding-left: 1rem !important; } -.mx-5 { - margin-right: 3rem !important; - margin-left: 3rem !important; +.ps-4 { + padding-left: 1.5rem !important; } -.mx-auto { - margin-right: auto !important; - margin-left: auto !important; +.ps-5 { + padding-left: 3rem !important; } -.my-0 { - margin-top: 0 !important; - margin-bottom: 0 !important; +.gap-0 { + gap: 0 !important; } -.my-1 { - margin-top: 0.25rem !important; - margin-bottom: 0.25rem !important; +.gap-1 { + gap: 0.25rem !important; } -.my-2 { - margin-top: 0.5rem !important; - margin-bottom: 0.5rem !important; +.gap-2 { + gap: 0.5rem !important; } -.my-3 { - margin-top: 1rem !important; - margin-bottom: 1rem !important; +.gap-3 { + gap: 1rem !important; } -.my-4 { - margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; +.gap-4 { + gap: 1.5rem !important; } -.my-5 { - margin-top: 3rem !important; - margin-bottom: 3rem !important; +.gap-5 { + gap: 3rem !important; } -.my-auto { - margin-top: auto !important; - margin-bottom: auto !important; +.row-gap-0 { + row-gap: 0 !important; } -.mt-0 { - margin-top: 0 !important; +.row-gap-1 { + row-gap: 0.25rem !important; } -.mt-1 { - margin-top: 0.25rem !important; +.row-gap-2 { + row-gap: 0.5rem !important; } -.mt-2 { - margin-top: 0.5rem !important; +.row-gap-3 { + row-gap: 1rem !important; } -.mt-3 { - margin-top: 1rem !important; +.row-gap-4 { + row-gap: 1.5rem !important; } -.mt-4 { - margin-top: 1.5rem !important; +.row-gap-5 { + row-gap: 3rem !important; } -.mt-5 { - margin-top: 3rem !important; +.column-gap-0 { + column-gap: 0 !important; } -.mt-auto { - margin-top: auto !important; +.column-gap-1 { + column-gap: 0.25rem !important; } -.me-0 { - margin-right: 0 !important; +.column-gap-2 { + column-gap: 0.5rem !important; } -.me-1 { - margin-right: 0.25rem !important; +.column-gap-3 { + column-gap: 1rem !important; } -.me-2 { - margin-right: 0.5rem !important; +.column-gap-4 { + column-gap: 1.5rem !important; } -.me-3 { - margin-right: 1rem !important; +.column-gap-5 { + column-gap: 3rem !important; } -.me-4 { - margin-right: 1.5rem !important; +.font-monospace { + font-family: var(--bs-font-monospace) !important; } -.me-5 { - margin-right: 3rem !important; +.fs-1 { + font-size: calc(1.375rem + 1.5vw) !important; } -.me-auto { - margin-right: auto !important; +.fs-2 { + font-size: calc(1.325rem + 0.9vw) !important; } -.mb-0 { - margin-bottom: 0 !important; +.fs-3 { + font-size: calc(1.3rem + 0.6vw) !important; } -.mb-1 { - margin-bottom: 0.25rem !important; +.fs-4 { + font-size: calc(1.275rem + 0.3vw) !important; } -.mb-2 { - margin-bottom: 0.5rem !important; +.fs-5 { + font-size: 1.25rem !important; } -.mb-3 { - margin-bottom: 1rem !important; +.fs-6 { + font-size: 1rem !important; } -.mb-4 { - margin-bottom: 1.5rem !important; +.fst-italic { + font-style: italic !important; } -.mb-5 { - margin-bottom: 3rem !important; +.fst-normal { + font-style: normal !important; } -.mb-auto { - margin-bottom: auto !important; +.fw-lighter { + font-weight: lighter !important; } -.ms-0 { - margin-left: 0 !important; +.fw-light { + font-weight: 300 !important; } -.ms-1 { - margin-left: 0.25rem !important; +.fw-normal { + font-weight: 400 !important; } -.ms-2 { - margin-left: 0.5rem !important; +.fw-medium { + font-weight: 500 !important; } -.ms-3 { - margin-left: 1rem !important; +.fw-semibold { + font-weight: 600 !important; } -.ms-4 { - margin-left: 1.5rem !important; +.fw-bold { + font-weight: 700 !important; } -.ms-5 { - margin-left: 3rem !important; +.fw-bolder { + font-weight: bolder !important; } -.ms-auto { - margin-left: auto !important; +.lh-1 { + line-height: 1 !important; } -.p-0 { - padding: 0 !important; +.lh-sm { + line-height: 1.25 !important; } -.p-1 { - padding: 0.25rem !important; +.lh-base { + line-height: 1.5 !important; } -.p-2 { - padding: 0.5rem !important; +.lh-lg { + line-height: 2 !important; } -.p-3 { - padding: 1rem !important; +.text-start { + text-align: left !important; } -.p-4 { - padding: 1.5rem !important; +.text-end { + text-align: right !important; } -.p-5 { - padding: 3rem !important; +.text-center { + text-align: center !important; } -.px-0 { - padding-right: 0 !important; - padding-left: 0 !important; +.text-decoration-none { + text-decoration: none !important; } -.px-1 { - padding-right: 0.25rem !important; - padding-left: 0.25rem !important; +.text-decoration-underline { + text-decoration: underline !important; } -.px-2 { - padding-right: 0.5rem !important; - padding-left: 0.5rem !important; +.text-decoration-line-through { + text-decoration: line-through !important; } -.px-3 { - padding-right: 1rem !important; - padding-left: 1rem !important; +.text-lowercase { + text-transform: lowercase !important; } -.px-4 { - padding-right: 1.5rem !important; - padding-left: 1.5rem !important; +.text-uppercase { + text-transform: uppercase !important; } -.px-5 { - padding-right: 3rem !important; - padding-left: 3rem !important; +.text-capitalize { + text-transform: capitalize !important; } -.py-0 { - padding-top: 0 !important; - padding-bottom: 0 !important; +.text-wrap { + white-space: normal !important; } -.py-1 { - padding-top: 0.25rem !important; - padding-bottom: 0.25rem !important; +.text-nowrap { + white-space: nowrap !important; } -.py-2 { - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; +/* rtl:begin:remove */ +.text-break { + word-wrap: break-word !important; + word-break: break-word !important; } -.py-3 { - padding-top: 1rem !important; - padding-bottom: 1rem !important; +/* rtl:end:remove */ +.text-primary { + --bs-text-opacity: 1; + color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; } -.py-4 { - padding-top: 1.5rem !important; - padding-bottom: 1.5rem !important; +.text-secondary { + --bs-text-opacity: 1; + color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; } -.py-5 { - padding-top: 3rem !important; - padding-bottom: 3rem !important; +.text-success { + --bs-text-opacity: 1; + color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; } -.pt-0 { - padding-top: 0 !important; +.text-info { + --bs-text-opacity: 1; + color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; } -.pt-1 { - padding-top: 0.25rem !important; +.text-warning { + --bs-text-opacity: 1; + color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; } -.pt-2 { - padding-top: 0.5rem !important; +.text-danger { + --bs-text-opacity: 1; + color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; } -.pt-3 { - padding-top: 1rem !important; +.text-light { + --bs-text-opacity: 1; + color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; } -.pt-4 { - padding-top: 1.5rem !important; +.text-dark { + --bs-text-opacity: 1; + color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; } -.pt-5 { - padding-top: 3rem !important; +.text-black { + --bs-text-opacity: 1; + color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; } -.pe-0 { - padding-right: 0 !important; +.text-white { + --bs-text-opacity: 1; + color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; } -.pe-1 { - padding-right: 0.25rem !important; +.text-body { + --bs-text-opacity: 1; + color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; } -.pe-2 { - padding-right: 0.5rem !important; +.text-accent { + --bs-text-opacity: 1; + color: rgba(var(--bs-accent-rgb), var(--bs-text-opacity)) !important; } -.pe-3 { - padding-right: 1rem !important; +.text-cinereous { + --bs-text-opacity: 1; + color: rgba(var(--bs-cinereous-rgb), var(--bs-text-opacity)) !important; } -.pe-4 { - padding-right: 1.5rem !important; +.text-verdigris { + --bs-text-opacity: 1; + color: rgba(var(--bs-verdigris-rgb), var(--bs-text-opacity)) !important; } -.pe-5 { - padding-right: 3rem !important; +.text-icterine { + --bs-text-opacity: 1; + color: rgba(var(--bs-icterine-rgb), var(--bs-text-opacity)) !important; } -.pb-0 { - padding-bottom: 0 !important; +.text-mute { + --bs-text-opacity: 1; + color: rgba(var(--bs-mute-rgb), var(--bs-text-opacity)) !important; } -.pb-1 { - padding-bottom: 0.25rem !important; +.text-muted { + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } -.pb-2 { - padding-bottom: 0.5rem !important; +.text-black-50 { + --bs-text-opacity: 1; + color: rgba(0, 0, 0, 0.5) !important; } -.pb-3 { - padding-bottom: 1rem !important; +.text-white-50 { + --bs-text-opacity: 1; + color: rgba(255, 255, 255, 0.5) !important; } -.pb-4 { - padding-bottom: 1.5rem !important; +.text-body-secondary { + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; } -.pb-5 { - padding-bottom: 3rem !important; +.text-body-tertiary { + --bs-text-opacity: 1; + color: var(--bs-tertiary-color) !important; } -.ps-0 { - padding-left: 0 !important; +.text-body-emphasis { + --bs-text-opacity: 1; + color: var(--bs-emphasis-color) !important; } -.ps-1 { - padding-left: 0.25rem !important; +.text-reset { + --bs-text-opacity: 1; + color: inherit !important; } -.ps-2 { - padding-left: 0.5rem !important; +.text-opacity-25 { + --bs-text-opacity: 0.25; } -.ps-3 { - padding-left: 1rem !important; +.text-opacity-50 { + --bs-text-opacity: 0.5; } -.ps-4 { - padding-left: 1.5rem !important; +.text-opacity-75 { + --bs-text-opacity: 0.75; } -.ps-5 { - padding-left: 3rem !important; +.text-opacity-100 { + --bs-text-opacity: 1; } -.font-monospace { - font-family: var(--bs-font-monospace) !important; +.text-primary-emphasis { + color: var(--bs-primary-text-emphasis) !important; } -.fs-1 { - font-size: calc(1.375rem + 1.5vw) !important; +.text-secondary-emphasis { + color: var(--bs-secondary-text-emphasis) !important; } -.fs-2 { - font-size: calc(1.325rem + 0.9vw) !important; +.text-success-emphasis { + color: var(--bs-success-text-emphasis) !important; } -.fs-3 { - font-size: calc(1.3rem + 0.6vw) !important; +.text-info-emphasis { + color: var(--bs-info-text-emphasis) !important; } -.fs-4 { - font-size: calc(1.275rem + 0.3vw) !important; +.text-warning-emphasis { + color: var(--bs-warning-text-emphasis) !important; } -.fs-5 { - font-size: 1.25rem !important; +.text-danger-emphasis { + color: var(--bs-danger-text-emphasis) !important; } -.fs-6 { - font-size: 1rem !important; +.text-light-emphasis { + color: var(--bs-light-text-emphasis) !important; } -.fst-italic { - font-style: italic !important; +.text-dark-emphasis { + color: var(--bs-dark-text-emphasis) !important; } -.fst-normal { - font-style: normal !important; +.text-accent { + color: #9CEC5B !important; } -.fw-light { - font-weight: 300 !important; +.text-cinereous { + color: #837569 !important; } -.fw-lighter { - font-weight: lighter !important; +.text-verdigris { + color: #50C5B7 !important; } -.fw-normal { - font-weight: 400 !important; +.text-icterine { + color: #F0F465 !important; } -.fw-bold { - font-weight: 700 !important; +.text-mute { + color: #6c757d !important; } -.fw-bolder { - font-weight: bolder !important; +.link-opacity-10 { + --bs-link-opacity: 0.1; } -.lh-1 { - line-height: 1 !important; +.link-opacity-10-hover:hover { + --bs-link-opacity: 0.1; } -.lh-sm { - line-height: 1.25 !important; +.link-opacity-25 { + --bs-link-opacity: 0.25; } -.lh-base { - line-height: 1.5 !important; +.link-opacity-25-hover:hover { + --bs-link-opacity: 0.25; } -.lh-lg { - line-height: 2 !important; +.link-opacity-50 { + --bs-link-opacity: 0.5; +} + +.link-opacity-50-hover:hover { + --bs-link-opacity: 0.5; } -.text-start { - text-align: left !important; +.link-opacity-75 { + --bs-link-opacity: 0.75; } -.text-end { - text-align: right !important; +.link-opacity-75-hover:hover { + --bs-link-opacity: 0.75; } -.text-center { - text-align: center !important; +.link-opacity-100 { + --bs-link-opacity: 1; } -.text-decoration-none { - text-decoration: none !important; +.link-opacity-100-hover:hover { + --bs-link-opacity: 1; } -.text-decoration-underline { - text-decoration: underline !important; +.link-offset-1 { + text-underline-offset: 0.125em !important; } -.text-decoration-line-through { - text-decoration: line-through !important; +.link-offset-1-hover:hover { + text-underline-offset: 0.125em !important; } -.text-lowercase { - text-transform: lowercase !important; +.link-offset-2 { + text-underline-offset: 0.25em !important; } -.text-uppercase { - text-transform: uppercase !important; +.link-offset-2-hover:hover { + text-underline-offset: 0.25em !important; } -.text-capitalize { - text-transform: capitalize !important; +.link-offset-3 { + text-underline-offset: 0.375em !important; } -.text-wrap { - white-space: normal !important; +.link-offset-3-hover:hover { + text-underline-offset: 0.375em !important; } -.text-nowrap { - white-space: nowrap !important; +.link-underline-primary { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; } -/* rtl:begin:remove */ -.text-break { - word-wrap: break-word !important; - word-break: break-word !important; +.link-underline-secondary { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; } -/* rtl:end:remove */ -.text-primary { - --bs-text-opacity: 1; - color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important; +.link-underline-success { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; } -.text-secondary { - --bs-text-opacity: 1; - color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important; +.link-underline-info { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; } -.text-success { - --bs-text-opacity: 1; - color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important; +.link-underline-warning { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; } -.text-info { - --bs-text-opacity: 1; - color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important; +.link-underline-danger { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; } -.text-warning { - --bs-text-opacity: 1; - color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important; +.link-underline-light { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; } -.text-danger { - --bs-text-opacity: 1; - color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important; +.link-underline-dark { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; } -.text-light { - --bs-text-opacity: 1; - color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important; +.link-underline { + --bs-link-underline-opacity: 1; + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; } -.text-dark { - --bs-text-opacity: 1; - color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important; +.link-underline-opacity-0 { + --bs-link-underline-opacity: 0; } -.text-black { - --bs-text-opacity: 1; - color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important; +.link-underline-opacity-0-hover:hover { + --bs-link-underline-opacity: 0; } -.text-white { - --bs-text-opacity: 1; - color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important; +.link-underline-opacity-10 { + --bs-link-underline-opacity: 0.1; } -.text-body { - --bs-text-opacity: 1; - color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important; +.link-underline-opacity-10-hover:hover { + --bs-link-underline-opacity: 0.1; } -.text-muted { - --bs-text-opacity: 1; - color: #6c757d !important; +.link-underline-opacity-25 { + --bs-link-underline-opacity: 0.25; } -.text-black-50 { - --bs-text-opacity: 1; - color: rgba(0, 0, 0, 0.5) !important; +.link-underline-opacity-25-hover:hover { + --bs-link-underline-opacity: 0.25; } -.text-white-50 { - --bs-text-opacity: 1; - color: rgba(255, 255, 255, 0.5) !important; +.link-underline-opacity-50 { + --bs-link-underline-opacity: 0.5; } -.text-reset { - --bs-text-opacity: 1; - color: inherit !important; +.link-underline-opacity-50-hover:hover { + --bs-link-underline-opacity: 0.5; } -.text-opacity-25 { - --bs-text-opacity: 0.25; +.link-underline-opacity-75 { + --bs-link-underline-opacity: 0.75; } -.text-opacity-50 { - --bs-text-opacity: 0.5; +.link-underline-opacity-75-hover:hover { + --bs-link-underline-opacity: 0.75; } -.text-opacity-75 { - --bs-text-opacity: 0.75; +.link-underline-opacity-100 { + --bs-link-underline-opacity: 1; } -.text-opacity-100 { - --bs-text-opacity: 1; +.link-underline-opacity-100-hover:hover { + --bs-link-underline-opacity: 1; } .bg-primary { @@ -14850,19 +17441,29 @@ textarea.form-control-lg { background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; } -.bg-black { +.bg-accent { + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-accent-rgb), var(--bs-bg-opacity)) !important; +} + +.bg-cinereous { --bs-bg-opacity: 1; - background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; + background-color: rgba(var(--bs-cinereous-rgb), var(--bs-bg-opacity)) !important; } -.bg-white { +.bg-verdigris { --bs-bg-opacity: 1; - background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; + background-color: rgba(var(--bs-verdigris-rgb), var(--bs-bg-opacity)) !important; } -.bg-body { +.bg-icterine { --bs-bg-opacity: 1; - background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; + background-color: rgba(var(--bs-icterine-rgb), var(--bs-bg-opacity)) !important; +} + +.bg-mute { + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-mute-rgb), var(--bs-bg-opacity)) !important; } .bg-transparent { @@ -14870,6 +17471,16 @@ textarea.form-control-lg { background-color: transparent !important; } +.bg-body-secondary { + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; +} + +.bg-body-tertiary { + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; +} + .bg-opacity-10 { --bs-bg-opacity: 0.1; } @@ -14890,6 +17501,58 @@ textarea.form-control-lg { --bs-bg-opacity: 1; } +.bg-primary-subtle { + background-color: var(--bs-primary-bg-subtle) !important; +} + +.bg-secondary-subtle { + background-color: var(--bs-secondary-bg-subtle) !important; +} + +.bg-success-subtle { + background-color: var(--bs-success-bg-subtle) !important; +} + +.bg-info-subtle { + background-color: var(--bs-info-bg-subtle) !important; +} + +.bg-warning-subtle { + background-color: var(--bs-warning-bg-subtle) !important; +} + +.bg-danger-subtle { + background-color: var(--bs-danger-bg-subtle) !important; +} + +.bg-light-subtle { + background-color: var(--bs-light-bg-subtle) !important; +} + +.bg-dark-subtle { + background-color: var(--bs-dark-bg-subtle) !important; +} + +.bg-accent { + background-color: #9CEC5B !important; +} + +.bg-cinereous { + background-color: #837569 !important; +} + +.bg-verdigris { + background-color: #50C5B7 !important; +} + +.bg-icterine { + background-color: #F0F465 !important; +} + +.bg-mute { + background-color: #6c757d !important; +} + .bg-gradient { background-image: var(--bs-gradient) !important; } @@ -14915,7 +17578,7 @@ textarea.form-control-lg { } .rounded { - border-radius: 0.25rem !important; + border-radius: var(--bs-border-radius) !important; } .rounded-0 { @@ -14923,15 +17586,23 @@ textarea.form-control-lg { } .rounded-1 { - border-radius: 0.2rem !important; + border-radius: var(--bs-border-radius-sm) !important; } .rounded-2 { - border-radius: 0.25rem !important; + border-radius: var(--bs-border-radius) !important; } .rounded-3 { - border-radius: 0.3rem !important; + border-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-4 { + border-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-5 { + border-radius: var(--bs-border-radius-xxl) !important; } .rounded-circle { @@ -14939,27 +17610,187 @@ textarea.form-control-lg { } .rounded-pill { - border-radius: 50rem !important; + border-radius: var(--bs-border-radius-pill) !important; } .rounded-top { - border-top-left-radius: 0.25rem !important; - border-top-right-radius: 0.25rem !important; + border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; +} + +.rounded-top-0 { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; +} + +.rounded-top-1 { + border-top-left-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-top-2 { + border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; +} + +.rounded-top-3 { + border-top-left-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-top-4 { + border-top-left-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-top-5 { + border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-top-circle { + border-top-left-radius: 50% !important; + border-top-right-radius: 50% !important; +} + +.rounded-top-pill { + border-top-left-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; } .rounded-end { - border-top-right-radius: 0.25rem !important; - border-bottom-right-radius: 0.25rem !important; + border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; +} + +.rounded-end-0 { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} + +.rounded-end-1 { + border-top-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-end-2 { + border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; +} + +.rounded-end-3 { + border-top-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-end-4 { + border-top-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-end-5 { + border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-end-circle { + border-top-right-radius: 50% !important; + border-bottom-right-radius: 50% !important; +} + +.rounded-end-pill { + border-top-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; } .rounded-bottom { - border-bottom-right-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; + border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; +} + +.rounded-bottom-0 { + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important; +} + +.rounded-bottom-1 { + border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-bottom-2 { + border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; +} + +.rounded-bottom-3 { + border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-bottom-4 { + border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-bottom-5 { + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-bottom-circle { + border-bottom-right-radius: 50% !important; + border-bottom-left-radius: 50% !important; +} + +.rounded-bottom-pill { + border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; } .rounded-start { - border-bottom-left-radius: 0.25rem !important; - border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; +} + +.rounded-start-0 { + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important; +} + +.rounded-start-1 { + border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-start-2 { + border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; +} + +.rounded-start-3 { + border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-start-4 { + border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-start-5 { + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-start-circle { + border-bottom-left-radius: 50% !important; + border-top-left-radius: 50% !important; +} + +.rounded-start-pill { + border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; } .visible { @@ -14970,6 +17801,26 @@ textarea.form-control-lg { visibility: hidden !important; } +.z-n1 { + z-index: -1 !important; +} + +.z-0 { + z-index: 0 !important; +} + +.z-1 { + z-index: 1 !important; +} + +.z-2 { + z-index: 2 !important; +} + +.z-3 { + z-index: 3 !important; +} + @media (min-width: 576px) { .float-sm-start { float: left !important; @@ -14980,6 +17831,21 @@ textarea.form-control-lg { .float-sm-none { float: none !important; } + .object-fit-sm-contain { + object-fit: contain !important; + } + .object-fit-sm-cover { + object-fit: cover !important; + } + .object-fit-sm-fill { + object-fit: fill !important; + } + .object-fit-sm-scale { + object-fit: scale-down !important; + } + .object-fit-sm-none { + object-fit: none !important; + } .d-sm-inline { display: inline !important; } @@ -14992,6 +17858,9 @@ textarea.form-control-lg { .d-sm-grid { display: grid !important; } + .d-sm-inline-grid { + display: inline-grid !important; + } .d-sm-table { display: table !important; } @@ -15046,24 +17915,6 @@ textarea.form-control-lg { .flex-sm-wrap-reverse { flex-wrap: wrap-reverse !important; } - .gap-sm-0 { - gap: 0 !important; - } - .gap-sm-1 { - gap: 0.25rem !important; - } - .gap-sm-2 { - gap: 0.5rem !important; - } - .gap-sm-3 { - gap: 1rem !important; - } - .gap-sm-4 { - gap: 1.5rem !important; - } - .gap-sm-5 { - gap: 3rem !important; - } .justify-content-sm-start { justify-content: flex-start !important; } @@ -15456,6 +18307,60 @@ textarea.form-control-lg { .ps-sm-5 { padding-left: 3rem !important; } + .gap-sm-0 { + gap: 0 !important; + } + .gap-sm-1 { + gap: 0.25rem !important; + } + .gap-sm-2 { + gap: 0.5rem !important; + } + .gap-sm-3 { + gap: 1rem !important; + } + .gap-sm-4 { + gap: 1.5rem !important; + } + .gap-sm-5 { + gap: 3rem !important; + } + .row-gap-sm-0 { + row-gap: 0 !important; + } + .row-gap-sm-1 { + row-gap: 0.25rem !important; + } + .row-gap-sm-2 { + row-gap: 0.5rem !important; + } + .row-gap-sm-3 { + row-gap: 1rem !important; + } + .row-gap-sm-4 { + row-gap: 1.5rem !important; + } + .row-gap-sm-5 { + row-gap: 3rem !important; + } + .column-gap-sm-0 { + column-gap: 0 !important; + } + .column-gap-sm-1 { + column-gap: 0.25rem !important; + } + .column-gap-sm-2 { + column-gap: 0.5rem !important; + } + .column-gap-sm-3 { + column-gap: 1rem !important; + } + .column-gap-sm-4 { + column-gap: 1.5rem !important; + } + .column-gap-sm-5 { + column-gap: 3rem !important; + } .text-sm-start { text-align: left !important; } @@ -15476,6 +18381,21 @@ textarea.form-control-lg { .float-md-none { float: none !important; } + .object-fit-md-contain { + object-fit: contain !important; + } + .object-fit-md-cover { + object-fit: cover !important; + } + .object-fit-md-fill { + object-fit: fill !important; + } + .object-fit-md-scale { + object-fit: scale-down !important; + } + .object-fit-md-none { + object-fit: none !important; + } .d-md-inline { display: inline !important; } @@ -15488,6 +18408,9 @@ textarea.form-control-lg { .d-md-grid { display: grid !important; } + .d-md-inline-grid { + display: inline-grid !important; + } .d-md-table { display: table !important; } @@ -15542,24 +18465,6 @@ textarea.form-control-lg { .flex-md-wrap-reverse { flex-wrap: wrap-reverse !important; } - .gap-md-0 { - gap: 0 !important; - } - .gap-md-1 { - gap: 0.25rem !important; - } - .gap-md-2 { - gap: 0.5rem !important; - } - .gap-md-3 { - gap: 1rem !important; - } - .gap-md-4 { - gap: 1.5rem !important; - } - .gap-md-5 { - gap: 3rem !important; - } .justify-content-md-start { justify-content: flex-start !important; } @@ -15952,6 +18857,60 @@ textarea.form-control-lg { .ps-md-5 { padding-left: 3rem !important; } + .gap-md-0 { + gap: 0 !important; + } + .gap-md-1 { + gap: 0.25rem !important; + } + .gap-md-2 { + gap: 0.5rem !important; + } + .gap-md-3 { + gap: 1rem !important; + } + .gap-md-4 { + gap: 1.5rem !important; + } + .gap-md-5 { + gap: 3rem !important; + } + .row-gap-md-0 { + row-gap: 0 !important; + } + .row-gap-md-1 { + row-gap: 0.25rem !important; + } + .row-gap-md-2 { + row-gap: 0.5rem !important; + } + .row-gap-md-3 { + row-gap: 1rem !important; + } + .row-gap-md-4 { + row-gap: 1.5rem !important; + } + .row-gap-md-5 { + row-gap: 3rem !important; + } + .column-gap-md-0 { + column-gap: 0 !important; + } + .column-gap-md-1 { + column-gap: 0.25rem !important; + } + .column-gap-md-2 { + column-gap: 0.5rem !important; + } + .column-gap-md-3 { + column-gap: 1rem !important; + } + .column-gap-md-4 { + column-gap: 1.5rem !important; + } + .column-gap-md-5 { + column-gap: 3rem !important; + } .text-md-start { text-align: left !important; } @@ -15972,6 +18931,21 @@ textarea.form-control-lg { .float-lg-none { float: none !important; } + .object-fit-lg-contain { + object-fit: contain !important; + } + .object-fit-lg-cover { + object-fit: cover !important; + } + .object-fit-lg-fill { + object-fit: fill !important; + } + .object-fit-lg-scale { + object-fit: scale-down !important; + } + .object-fit-lg-none { + object-fit: none !important; + } .d-lg-inline { display: inline !important; } @@ -15984,6 +18958,9 @@ textarea.form-control-lg { .d-lg-grid { display: grid !important; } + .d-lg-inline-grid { + display: inline-grid !important; + } .d-lg-table { display: table !important; } @@ -16038,24 +19015,6 @@ textarea.form-control-lg { .flex-lg-wrap-reverse { flex-wrap: wrap-reverse !important; } - .gap-lg-0 { - gap: 0 !important; - } - .gap-lg-1 { - gap: 0.25rem !important; - } - .gap-lg-2 { - gap: 0.5rem !important; - } - .gap-lg-3 { - gap: 1rem !important; - } - .gap-lg-4 { - gap: 1.5rem !important; - } - .gap-lg-5 { - gap: 3rem !important; - } .justify-content-lg-start { justify-content: flex-start !important; } @@ -16448,6 +19407,60 @@ textarea.form-control-lg { .ps-lg-5 { padding-left: 3rem !important; } + .gap-lg-0 { + gap: 0 !important; + } + .gap-lg-1 { + gap: 0.25rem !important; + } + .gap-lg-2 { + gap: 0.5rem !important; + } + .gap-lg-3 { + gap: 1rem !important; + } + .gap-lg-4 { + gap: 1.5rem !important; + } + .gap-lg-5 { + gap: 3rem !important; + } + .row-gap-lg-0 { + row-gap: 0 !important; + } + .row-gap-lg-1 { + row-gap: 0.25rem !important; + } + .row-gap-lg-2 { + row-gap: 0.5rem !important; + } + .row-gap-lg-3 { + row-gap: 1rem !important; + } + .row-gap-lg-4 { + row-gap: 1.5rem !important; + } + .row-gap-lg-5 { + row-gap: 3rem !important; + } + .column-gap-lg-0 { + column-gap: 0 !important; + } + .column-gap-lg-1 { + column-gap: 0.25rem !important; + } + .column-gap-lg-2 { + column-gap: 0.5rem !important; + } + .column-gap-lg-3 { + column-gap: 1rem !important; + } + .column-gap-lg-4 { + column-gap: 1.5rem !important; + } + .column-gap-lg-5 { + column-gap: 3rem !important; + } .text-lg-start { text-align: left !important; } @@ -16468,6 +19481,21 @@ textarea.form-control-lg { .float-xl-none { float: none !important; } + .object-fit-xl-contain { + object-fit: contain !important; + } + .object-fit-xl-cover { + object-fit: cover !important; + } + .object-fit-xl-fill { + object-fit: fill !important; + } + .object-fit-xl-scale { + object-fit: scale-down !important; + } + .object-fit-xl-none { + object-fit: none !important; + } .d-xl-inline { display: inline !important; } @@ -16480,6 +19508,9 @@ textarea.form-control-lg { .d-xl-grid { display: grid !important; } + .d-xl-inline-grid { + display: inline-grid !important; + } .d-xl-table { display: table !important; } @@ -16534,24 +19565,6 @@ textarea.form-control-lg { .flex-xl-wrap-reverse { flex-wrap: wrap-reverse !important; } - .gap-xl-0 { - gap: 0 !important; - } - .gap-xl-1 { - gap: 0.25rem !important; - } - .gap-xl-2 { - gap: 0.5rem !important; - } - .gap-xl-3 { - gap: 1rem !important; - } - .gap-xl-4 { - gap: 1.5rem !important; - } - .gap-xl-5 { - gap: 3rem !important; - } .justify-content-xl-start { justify-content: flex-start !important; } @@ -16944,6 +19957,60 @@ textarea.form-control-lg { .ps-xl-5 { padding-left: 3rem !important; } + .gap-xl-0 { + gap: 0 !important; + } + .gap-xl-1 { + gap: 0.25rem !important; + } + .gap-xl-2 { + gap: 0.5rem !important; + } + .gap-xl-3 { + gap: 1rem !important; + } + .gap-xl-4 { + gap: 1.5rem !important; + } + .gap-xl-5 { + gap: 3rem !important; + } + .row-gap-xl-0 { + row-gap: 0 !important; + } + .row-gap-xl-1 { + row-gap: 0.25rem !important; + } + .row-gap-xl-2 { + row-gap: 0.5rem !important; + } + .row-gap-xl-3 { + row-gap: 1rem !important; + } + .row-gap-xl-4 { + row-gap: 1.5rem !important; + } + .row-gap-xl-5 { + row-gap: 3rem !important; + } + .column-gap-xl-0 { + column-gap: 0 !important; + } + .column-gap-xl-1 { + column-gap: 0.25rem !important; + } + .column-gap-xl-2 { + column-gap: 0.5rem !important; + } + .column-gap-xl-3 { + column-gap: 1rem !important; + } + .column-gap-xl-4 { + column-gap: 1.5rem !important; + } + .column-gap-xl-5 { + column-gap: 3rem !important; + } .text-xl-start { text-align: left !important; } @@ -16964,6 +20031,21 @@ textarea.form-control-lg { .float-xxl-none { float: none !important; } + .object-fit-xxl-contain { + object-fit: contain !important; + } + .object-fit-xxl-cover { + object-fit: cover !important; + } + .object-fit-xxl-fill { + object-fit: fill !important; + } + .object-fit-xxl-scale { + object-fit: scale-down !important; + } + .object-fit-xxl-none { + object-fit: none !important; + } .d-xxl-inline { display: inline !important; } @@ -16976,6 +20058,9 @@ textarea.form-control-lg { .d-xxl-grid { display: grid !important; } + .d-xxl-inline-grid { + display: inline-grid !important; + } .d-xxl-table { display: table !important; } @@ -17030,24 +20115,6 @@ textarea.form-control-lg { .flex-xxl-wrap-reverse { flex-wrap: wrap-reverse !important; } - .gap-xxl-0 { - gap: 0 !important; - } - .gap-xxl-1 { - gap: 0.25rem !important; - } - .gap-xxl-2 { - gap: 0.5rem !important; - } - .gap-xxl-3 { - gap: 1rem !important; - } - .gap-xxl-4 { - gap: 1.5rem !important; - } - .gap-xxl-5 { - gap: 3rem !important; - } .justify-content-xxl-start { justify-content: flex-start !important; } @@ -17440,6 +20507,60 @@ textarea.form-control-lg { .ps-xxl-5 { padding-left: 3rem !important; } + .gap-xxl-0 { + gap: 0 !important; + } + .gap-xxl-1 { + gap: 0.25rem !important; + } + .gap-xxl-2 { + gap: 0.5rem !important; + } + .gap-xxl-3 { + gap: 1rem !important; + } + .gap-xxl-4 { + gap: 1.5rem !important; + } + .gap-xxl-5 { + gap: 3rem !important; + } + .row-gap-xxl-0 { + row-gap: 0 !important; + } + .row-gap-xxl-1 { + row-gap: 0.25rem !important; + } + .row-gap-xxl-2 { + row-gap: 0.5rem !important; + } + .row-gap-xxl-3 { + row-gap: 1rem !important; + } + .row-gap-xxl-4 { + row-gap: 1.5rem !important; + } + .row-gap-xxl-5 { + row-gap: 3rem !important; + } + .column-gap-xxl-0 { + column-gap: 0 !important; + } + .column-gap-xxl-1 { + column-gap: 0.25rem !important; + } + .column-gap-xxl-2 { + column-gap: 0.5rem !important; + } + .column-gap-xxl-3 { + column-gap: 1rem !important; + } + .column-gap-xxl-4 { + column-gap: 1.5rem !important; + } + .column-gap-xxl-5 { + column-gap: 3rem !important; + } .text-xxl-start { text-align: left !important; } @@ -17477,6 +20598,9 @@ textarea.form-control-lg { .d-print-grid { display: grid !important; } + .d-print-inline-grid { + display: inline-grid !important; + } .d-print-table { display: table !important; } @@ -17510,290 +20634,417 @@ textarea.form-control-lg { --scroll-radius: 0px; --scroll-track: var(--bs-light); --scroll-thumb-color: var(--bs-primary); + --bb-offcanvas-horizontal-width-lg: 768px !important; } -html, -body { +.btn-xsm, .btn-group-xsm > .btn { + --bs-btn-padding-y: 0.2rem; + --bs-btn-padding-x: 0.3rem; + --bs-btn-font-size: 0.7rem; + --bs-btn-border-radius: 0.25rem; +} + +.loading-container { width: 100%; height: 100%; + display: flex; + align-content: center; + justify-content: center; + align-items: center; + flex-direction: column; +} +.loading-container .svg-container { + width: 8rem; + height: 8rem; + display: flex; + align-content: center; + justify-content: center; + align-items: center; +} +.loading-container .loading-progress { + position: absolute; + display: block; + width: 8rem; + height: 8rem; +} +.loading-container .loading-progress circle { + fill: none; + stroke: #6f42c1; + stroke-width: 0.2rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); +} +.loading-container .loading-progress circle:last-child { + stroke: #8c68cd; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; +} +.loading-container .loading-logo { + position: absolute; + fill: #8c68cd; + height: 70px; +} +.loading-container .loading-progress-text { + font-weight: bold; +} +.loading-container .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); } -.spinner-border { - margin-top: 3rem; - color: white; +::-webkit-scrollbar { + width: 4px; + height: 4px; } -#logo-container, #animation-container { - width: 100%; - height: 100%; - background: var(--bs-primary) !important; - fill: white; - stroke: white; +::-webkit-scrollbar-button { + width: 0px; + height: 0px; +} + +::-webkit-scrollbar-thumb { + background: #7d59bd; + border: 0px none #ffffff; + border-radius: 0.375rem; +} + +::-webkit-scrollbar-thumb:hover { + background: #8c68cd; +} + +::-webkit-scrollbar-thumb:active { + background: #8c68cd; +} + +::-webkit-scrollbar-track { + background: #212529; + border: 0px none #ffffff; + border-radius: 0.375rem; +} + +::-webkit-scrollbar-track:hover { + background: #1a1d20; +} + +::-webkit-scrollbar-track:active { + background: #1a1d20; +} + +::-webkit-scrollbar-corner { + background: transparent; +} + +.table-container { + max-height: 100%; + overflow-y: scroll; + flex-grow: 1; + position: relative; +} +.table-container .loading { position: absolute; + z-index: 2; top: 0; - left: 0; - bottom: 0; right: 0; + bottom: 0; + left: 0; + background: rgba(var(--bs-gray-900-rgb), 0.3); display: flex; align-items: center; justify-content: center; - flex-direction: column; -} -#logo-container svg, #animation-container svg { - max-width: 200px; -} - -#svg-definitions { - display: none; } -.logo-viewbox { - width: 25%; - display: block; - fill: var(--bs-black); - stroke: var(--bs-black); +.sticky-header { + position: sticky; + top: 0; + background-color: var(--bs-body-bg) !important; + z-index: 1; } -#splash-viewbox { - stroke-miterlimit: 5; - stroke-dashArray: 2000; - stroke-dashoffset: 2000; - fill-opacity: 0; +/*tr { + height: 61px; + max-height: 61px; +}*/ +td { + vertical-align: middle; } -.fade-out { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: fade-out-animation; - animation-duration: 1s; - animation-delay: 3s; +.contained { + display: -webkit-box; + max-height: calc(61px - 1rem); + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; } -.animation-phase-1 { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 0.2s, 2.2s, 1.2s; +@-webkit-keyframes moving-gradient { + 0% { + background-position: -250px 0; + } + 100% { + background-position: 250px 0; + } } - -.animation-phase-2 { - animation-timing-function: ease-in-out; +tr.row-placeholder td::after { + content: " "; + display: block; + height: 12px; + background: linear-gradient(to right, rgba(var(--bs-body-color-rgb), 0.3) 20%, rgba(var(--bs-secondary-bg-rgb), 0.7) 50%, rgba(var(--bs-body-color-rgb), 0.3) 80%); + background-size: 500px 100px; + animation-name: moving-gradient; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-timing-function: linear; animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 0.4s, 2.4s, 1.4s; } -.animation-phase-3 { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 0.6s, 2.6s, 1.6s; +.monaco-normal { + height: 500px !important; } -.animation-phase-4 { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 0.8s, 2.8s, 1.8s; +.monaco-small { + height: 300px !important; } -.animation-phase-5 { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 1s, 3s, 2s; +.monaco-extra-small { + height: 100px !important; } -.animation-phase-6 { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 1.2s, 3.2s, 2.2s; +.graph-canvas { + cursor: grab; } - -.animation-phase-7 { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 1.4s, 3.4s, 2.4s; +.graph-canvas.grabbing { + cursor: grabbing; } -.animation-phase-8 { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: stroke-draw-animation, stroke-fade-out-animation, fade-in-animation; - animation-duration: 1.5s, 0.75s, 0.75s; - animation-delay: 1.6s, 3.6s, 2.6s; +.graph-container, .graph-canvas { + --stroke-color: #6c757d; + --fill-color: #1a1d20; + display: flex; + flex-direction: column; } - -@keyframes stroke-draw-animation { - to { - stroke-dashOffset: 0; - } +.graph-container .graph-controls, .graph-canvas .graph-controls { + display: flex; + justify-content: flex-end; + gap: 1rem; + padding-right: 1rem; } -@keyframes stroke-fade-out-animation { - to { - stroke-opacity: 0; - } +.graph-container .graph-controls .btn, .graph-canvas .graph-controls .btn { + --bs-btn-color: #6c757d; + --bs-btn-border-color: #6c757d; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #6c757d; + --bs-btn-hover-border-color: #6c757d; + --bs-btn-focus-shadow-rgb: 108, 117, 125; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #6c757d; + --bs-btn-active-border-color: #6c757d; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #6c757d; + --bs-btn-disabled-bg: transparent; + --bs-btn-disabled-border-color: #6c757d; + --bs-gradient: none; + color: var(--bs-btn-color); + fill: var(--bs-btn-color); + border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); } -@keyframes fade-in-animation { - to { - fill-opacity: 1; - } +.graph-container .graph-controls .btn:hover, .graph-canvas .graph-controls .btn:hover { + color: var(--bs-btn-hover-color); + fill: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); } -@keyframes fade-out-animation { - to { - opacity: 0; - } + +#default-node-gradient stop[offset="0"] { + stop-color: #6c757d; } -* { - padding: 0; - margin: 0; +#default-node-gradient stop[offset="1"] { + stop-color: #4c5258; } -body { - scrollbar-color: var(--scroll-thumb-color, grey) var(--scroll-track, transparent); - scrollbar-width: thin; +#catch-node-gradient stop[offset="0"] { + stop-color: #ff821d; } -body::-webkit-scrollbar { - width: var(--scroll-size, 10px); - height: var(--scroll-size, 10px); +#catch-node-gradient stop[offset="1"] { + stop-color: #b35b14; } -body::-webkit-scrollbar-track { - background-color: var(--scroll-track, transparent); - border-radius: var(--scroll-track-radius, var(--scroll-radius)); + +#raise-node-gradient stop[offset="0"] { + stop-color: #dc3545; } -body::-webkit-scrollbar-thumb { - background-color: var(--scroll-thumb-color, grey); - background-image: var(--scroll-thumb, none); - border-radius: var(--scroll-thumb-radius, var(--scroll-radius)); +#raise-node-gradient stop[offset="1"] { + stop-color: #9a2530; } -#blazor-error-ui { - background: var(--bs-danger); - color: var(--bs-light); - bottom: 0; - display: none; - left: 0; - padding: 0.6rem 1.25rem 0.7rem 1.25rem; - position: fixed; - width: 100%; - z-index: 1000; +#call-node-gradient stop[offset="0"] { + stop-color: #440154; } -#blazor-error-ui a { - color: var(--bs-light); - text-decoration: none; +#call-node-gradient stop[offset="1"] { + stop-color: #30013b; } -#blazor-error-ui .reload { - font-weight: 600; + +#do-node-gradient stop[offset="0"] { + stop-color: #481f70; } -#blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; +#do-node-gradient stop[offset="1"] { + stop-color: #32164e; } -.form-control.invalid { - border-color: red; +#fork-node-gradient stop[offset="0"] { + stop-color: #443983; } - -input[type=time]::-webkit-datetime-edit-ampm-field { - display: none; +#fork-node-gradient stop[offset="1"] { + stop-color: #30285c; } -input[type=time]::-webkit-clear-button { - -webkit-appearance: none; - -moz-appearance: none; - -o-appearance: none; - -ms-appearance: none; - appearance: none; - margin: -10px; +#for-node-gradient stop[offset="0"] { + stop-color: #3b528b; +} +#for-node-gradient stop[offset="1"] { + stop-color: #293961; } -.cursor-pointer { - cursor: pointer; +#listen-node-gradient stop[offset="0"] { + stop-color: #31688e; +} +#listen-node-gradient stop[offset="1"] { + stop-color: #224963; } -.flex-grow { - flex-grow: 1; +#run-node-gradient stop[offset="0"] { + stop-color: #287c8e; +} +#run-node-gradient stop[offset="1"] { + stop-color: #1c5763; } -.default.group .port { - width: 0px !important; - height: 0px !important; - margin: 0px !important; +#set-node-gradient stop[offset="0"] { + stop-color: #21918c; +} +#set-node-gradient stop[offset="1"] { + stop-color: #176662; } -.monaco-normal { - height: 500px !important; +#switch-node-gradient stop[offset="0"] { + stop-color: #20a486; +} +#switch-node-gradient stop[offset="1"] { + stop-color: #16735e; } -.monaco-small { - height: 300px !important; +#try-catch-node-gradient stop[offset="0"] { + stop-color: #35b779; +} +#try-catch-node-gradient stop[offset="1"] { + stop-color: #258055; } -.monaco-extra-small { - height: 100px !important; +#try-node-gradient stop[offset="0"] { + stop-color: #5ec962; +} +#try-node-gradient stop[offset="1"] { + stop-color: #428d45; } -.bi { - width: 16px; - height: 16px; - margin-right: 8px; - display: inline-flex; - justify-content: flex-end; - align-items: center; +#emit-node-gradient stop[offset="0"] { + stop-color: #90d743; +} +#emit-node-gradient stop[offset="1"] { + stop-color: #65972f; } -.toast-container { - z-index: 1 !important; - position: fixed !important; - width: 25rem !important; - top: 0px !important; - right: 0px !important; +#wait-node-gradient stop[offset="0"] { + stop-color: #c8e020; +} +#wait-node-gradient stop[offset="1"] { + stop-color: #8c9d16; } -.end-node circle { +.node, .cluster { + cursor: pointer; + --gradient-url: url(#default-node-gradient); +} +.node.default-task-node, .cluster.default-task-node { + --gradient-url: url(#default-node-gradient); +} +.node.catch-task-node, .cluster.catch-task-node { + --gradient-url: url(#catch-node-gradient); +} +.node.raise-task-node, .cluster.raise-task-node { + --gradient-url: url(#raise-node-gradient); +} +.node.call-task-node, .cluster.call-task-node { + --gradient-url: url(#call-node-gradient); +} +.node.do-task-node, .cluster.do-task-node { + --gradient-url: url(#do-node-gradient); +} +.node.fork-task-node, .cluster.fork-task-node { + --gradient-url: url(#fork-node-gradient); +} +.node.for-task-node, .cluster.for-task-node { + --gradient-url: url(#for-node-gradient); +} +.node.listen-task-node, .cluster.listen-task-node { + --gradient-url: url(#listen-node-gradient); +} +.node.run-task-node, .cluster.run-task-node { + --gradient-url: url(#run-node-gradient); +} +.node.set-task-node, .cluster.set-task-node { + --gradient-url: url(#set-node-gradient); +} +.node.switch-task-node, .cluster.switch-task-node { + --gradient-url: url(#switch-node-gradient); +} +.node.try-catch-task-node, .cluster.try-catch-task-node { + --gradient-url: url(#try-catch-node-gradient); +} +.node.try-task-node, .cluster.try-task-node { + --gradient-url: url(#try-node-gradient); +} +.node.emit-task-node, .cluster.emit-task-node { + --gradient-url: url(#emit-node-gradient); +} +.node.wait-task-node, .cluster.wait-task-node { + --gradient-url: url(#wait-node-gradient); +} +.node .node-rectangle, .cluster .node-rectangle { + stroke: var(--gradient-url); stroke-width: 3px; } - -.btn-outline-dark:hover { - fill: white; +.node .node-cartouche, .cluster .node-cartouche { + fill: var(--gradient-url); } - -.mh-50 { - max-height: 50% !important; +.node .label-content, .cluster .label-content { + width: 100%; + height: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } - -.overflow-y-scroll { - overflow-y: scroll; +.node .label-content h3, .node .label-content .h3, .cluster .label-content h3, .cluster .label-content .h3 { + margin-bottom: 0.25rem; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } - -.tab-content { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; +.node .label-content p, .cluster .label-content p { + margin: 0; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} +.node .label-content pre, .cluster .label-content pre { + margin: 0; + height: 35px; + text-overflow: ellipsis; + overflow-x: hidden; + overflow-y: auto; +} +.node.shape-cartouche .label-content, .cluster.shape-cartouche .label-content { + text-align: left; + padding: 5px 5px 5px 65px; } - .node.active rect, .cluster.active rect { fill: rgba(var(--bs-info-rgb), 0.2); } @@ -17818,126 +21069,227 @@ input[type=time]::-webkit-clear-button { .node .activity-badge.activity-badge--faulted circle, .cluster .activity-badge.activity-badge--faulted circle { fill: var(--bs-danger); } -.node .add-state .add-state__label, .cluster .add-state .add-state__label { - font-size: 12px; - font-weight: bold; +.node.legend .node-rectangle, .cluster.legend .node-rectangle { + fill: var(--gradient-url); } - -.toolbar .btn { +.node.legend div, .cluster.legend div { + width: 100%; + height: 100%; display: flex; align-items: center; - justify-content: flex-start; + justify-content: center; + color: white; } -.edges { - pointer-events: none; +.symbol { + fill: var(--bs-body-color); } -.edges .used-for-compensation path { - stroke-dasharray: 4; + +.start-node circle { + stroke-width: 2px; } -.ghost { - opacity: 0.3; - pointer-events: none; +.end-node circle { + stroke-width: 5px; } -.drop-destination rect, -.drop-destination circle, -.drop-destination ellipse { - stroke: red; - stroke-width: 2px; - stroke-dasharray: 2; +.edge-label .label-content { + text-align: center; + background-color: #1a1d20; } -.card-metric { - box-shadow: 2px 2px 10px var(--bs-gray-200); - margin: 5px; - padding: 10px; - background-color: var(--bs-white); - border-radius: 5px; - transition: 0.3s linear all; +.horizontal-collapse { + border-right: 1px solid var(--bs-dark-bg-subtle); + overflow: scroll; +} +.horizontal-collapse:nth-last-child(1) { + border: none; +} +.horizontal-collapse .title { + display: flex; + gap: 0.5rem; +} +.horizontal-collapse .title i { + margin-left: auto; +} +.horizontal-collapse:nth-child(1) .title { + padding-left: 0 !important; +} +.horizontal-collapse.collapsed { + overflow: hidden; +} +.horizontal-collapse.collapsed .title { + width: 22px; + height: 100%; + flex-direction: column; +} +.horizontal-collapse.collapsed .title .label { + display: block; + transform: rotate(90deg) translate(3px); + transform-origin: center; +} +.horizontal-collapse.collapsed .title i { + margin-top: auto; } -.card-metric:hover { - box-shadow: 4px 4px 20px var(--bs-gray-200); - transition: 0.3s linear all; +.workflow-instance-details h6, .workflow-instance-details .h6 { + font-weight: bold; +} +.workflow-instance-details .label { + font-weight: lighter; } -.card-metric.info { - background-color: var(--bs-info); - color: var(--bs-white); +html, body { + font-family: "Inconsolata", monospace; + width: 100%; + height: 100%; } -.card-metric.danger { - background-color: var(--bs-danger); - color: var(--bs-white); +#app { + width: 100%; + height: 100%; } -.card-metric.success { - background-color: var(--bs-success); - color: var(--bs-white); +h1:focus, .h1:focus { + outline: none; } -.card-metric.secondary { - background-color: var(--bs-secondary); - color: var(--bs-white); +#blazor-error-ui { + background: #2b3035; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; } -.card-metric.warning { - background-color: var(--bs-warning); - color: var(--bs-white); +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; } -.card-metric i { - font-size: 5em; - opacity: 0.2; +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; } -.card-metric .card-metric-value { - font-size: 32px; - display: block; +.blazor-error-boundary::after { + content: "An error has occurred."; } -.card-metric .card-metric-name { - font-style: italic; - text-transform: capitalize; - opacity: 0.5; - display: block; - font-size: 18px; +.flex-grow { + flex-grow: 1; } -table .details { - text-align: right; +.svg-definitions, #svg-definitions { + width: 0 !important; + height: 0 !important; + margin: 0 !important; + padding: 0 !important; + position: absolute; + top: -100px; + left: -100px; } -.upload-page { - gap: var(--bs-gutter-x); +.logo { + fill: #8c68cd; + height: 75px; } -/*Breadcrumb*/ -.breadcrumb a { - text-decoration: none; - font-size: 0.9em; - display: flex; - align-items: center; - justify-content: flex-start; +.logo-large { + height: 300px; +} + +.logo-typing { + font-family: Rajdhani; + color: #8c68cd; + /* + background-image: -webkit-linear-gradient(0deg, $primary-dark 0%, $primary-bg-subtle-dark 100%); + background-clip: text; + -webkit-background-clip: text; + text-fill-color: transparent; + -webkit-text-fill-color: transparent; + */ +} + +.logo-typing-large { + font-size: 8rem; + margin-right: 0px; } -.breadcrumb a .bi { - margin-right: 4px; + +.vh-30 { + height: 30vh !important; +} + +.offcanvas { + margin-top: 1px; +} + +.cursor-pointer { + cursor: pointer; +} + +.modal-confirmation .modal-footer { + border-top: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color) !important; +} + +.modal .modal-header { + background-color: inherit !important; + color: inherit !important; + justify-content: space-between; +} + +.h-100-px { + height: 100px; } -.vh-90 { - height: 90vh; +.h-200-px { + height: 200px; +} + +.h-300-px { + height: 300px; +} + +.h-400-px { + height: 400px; +} + +.h-500-px { + height: 500px; +} + +input[type=search]::-webkit-search-cancel-button { + -webkit-appearance: none; + height: 16px; + width: 16px; + margin-left: 0.4em; + mask-image: url("data:image/svg+xml;utf8,"); + cursor: pointer; + background-color: #c6b4e6; +} + +.breadcrumb { + margin-bottom: 0; } -.vh-85 { - height: 85vh; +.collapsible-instances .content { + max-height: calc(100% - 80px) !important; } -.pxh-150 { - height: 150px; +.bb-offcanvas-lg { + --bs-offcanvas-width: 80vw; } -.form-select.bg-secondary { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23FFFFFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e") !important; +.monaco-editor.logs-container { + height: 300px; + overflow: scroll; + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); } diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/app.min.css b/src/dashboard/Synapse.Dashboard/wwwroot/css/app.min.css index a3f6d14d9..e20b0c0a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/css/app.min.css +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/app.min.css @@ -1,8 +1,13 @@ -@charset "UTF-8";@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;1,400&display=swap");@font-face{font-display:block;font-family:"bootstrap-icons";src:url("/lib/bootstrap-icons/font/fonts//bootstrap-icons.woff2?8d200481aa7f02a2d63a331fc782cfaf") format("woff2"),url("/lib/bootstrap-icons/font/fonts//bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("woff");}.bi::before,[class^=bi-]::before,[class*=" bi-"]::before{display:inline-block;font-family:"bootstrap-icons"!important;font-style:normal;font-weight:normal!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}.bi-123::before{content:"";}.bi-alarm-fill::before{content:"";}.bi-alarm::before{content:"";}.bi-align-bottom::before{content:"";}.bi-align-center::before{content:"";}.bi-align-end::before{content:"";}.bi-align-middle::before{content:"";}.bi-align-start::before{content:"";}.bi-align-top::before{content:"";}.bi-alt::before{content:"";}.bi-app-indicator::before{content:"";}.bi-app::before{content:"";}.bi-archive-fill::before{content:"";}.bi-archive::before{content:"";}.bi-arrow-90deg-down::before{content:"";}.bi-arrow-90deg-left::before{content:"";}.bi-arrow-90deg-right::before{content:"";}.bi-arrow-90deg-up::before{content:"";}.bi-arrow-bar-down::before{content:"";}.bi-arrow-bar-left::before{content:"";}.bi-arrow-bar-right::before{content:"";}.bi-arrow-bar-up::before{content:"";}.bi-arrow-clockwise::before{content:"";}.bi-arrow-counterclockwise::before{content:"";}.bi-arrow-down-circle-fill::before{content:"";}.bi-arrow-down-circle::before{content:"";}.bi-arrow-down-left-circle-fill::before{content:"";}.bi-arrow-down-left-circle::before{content:"";}.bi-arrow-down-left-square-fill::before{content:"";}.bi-arrow-down-left-square::before{content:"";}.bi-arrow-down-left::before{content:"";}.bi-arrow-down-right-circle-fill::before{content:"";}.bi-arrow-down-right-circle::before{content:"";}.bi-arrow-down-right-square-fill::before{content:"";}.bi-arrow-down-right-square::before{content:"";}.bi-arrow-down-right::before{content:"";}.bi-arrow-down-short::before{content:"";}.bi-arrow-down-square-fill::before{content:"";}.bi-arrow-down-square::before{content:"";}.bi-arrow-down-up::before{content:"";}.bi-arrow-down::before{content:"";}.bi-arrow-left-circle-fill::before{content:"";}.bi-arrow-left-circle::before{content:"";}.bi-arrow-left-right::before{content:"";}.bi-arrow-left-short::before{content:"";}.bi-arrow-left-square-fill::before{content:"";}.bi-arrow-left-square::before{content:"";}.bi-arrow-left::before{content:"";}.bi-arrow-repeat::before{content:"";}.bi-arrow-return-left::before{content:"";}.bi-arrow-return-right::before{content:"";}.bi-arrow-right-circle-fill::before{content:"";}.bi-arrow-right-circle::before{content:"";}.bi-arrow-right-short::before{content:"";}.bi-arrow-right-square-fill::before{content:"";}.bi-arrow-right-square::before{content:"";}.bi-arrow-right::before{content:"";}.bi-arrow-up-circle-fill::before{content:"";}.bi-arrow-up-circle::before{content:"";}.bi-arrow-up-left-circle-fill::before{content:"";}.bi-arrow-up-left-circle::before{content:"";}.bi-arrow-up-left-square-fill::before{content:"";}.bi-arrow-up-left-square::before{content:"";}.bi-arrow-up-left::before{content:"";}.bi-arrow-up-right-circle-fill::before{content:"";}.bi-arrow-up-right-circle::before{content:"";}.bi-arrow-up-right-square-fill::before{content:"";}.bi-arrow-up-right-square::before{content:"";}.bi-arrow-up-right::before{content:"";}.bi-arrow-up-short::before{content:"";}.bi-arrow-up-square-fill::before{content:"";}.bi-arrow-up-square::before{content:"";}.bi-arrow-up::before{content:"";}.bi-arrows-angle-contract::before{content:"";}.bi-arrows-angle-expand::before{content:"";}.bi-arrows-collapse::before{content:"";}.bi-arrows-expand::before{content:"";}.bi-arrows-fullscreen::before{content:"";}.bi-arrows-move::before{content:"";}.bi-aspect-ratio-fill::before{content:"";}.bi-aspect-ratio::before{content:"";}.bi-asterisk::before{content:"";}.bi-at::before{content:"";}.bi-award-fill::before{content:"";}.bi-award::before{content:"";}.bi-back::before{content:"";}.bi-backspace-fill::before{content:"";}.bi-backspace-reverse-fill::before{content:"";}.bi-backspace-reverse::before{content:"";}.bi-backspace::before{content:"";}.bi-badge-3d-fill::before{content:"";}.bi-badge-3d::before{content:"";}.bi-badge-4k-fill::before{content:"";}.bi-badge-4k::before{content:"";}.bi-badge-8k-fill::before{content:"";}.bi-badge-8k::before{content:"";}.bi-badge-ad-fill::before{content:"";}.bi-badge-ad::before{content:"";}.bi-badge-ar-fill::before{content:"";}.bi-badge-ar::before{content:"";}.bi-badge-cc-fill::before{content:"";}.bi-badge-cc::before{content:"";}.bi-badge-hd-fill::before{content:"";}.bi-badge-hd::before{content:"";}.bi-badge-tm-fill::before{content:"";}.bi-badge-tm::before{content:"";}.bi-badge-vo-fill::before{content:"";}.bi-badge-vo::before{content:"";}.bi-badge-vr-fill::before{content:"";}.bi-badge-vr::before{content:"";}.bi-badge-wc-fill::before{content:"";}.bi-badge-wc::before{content:"";}.bi-bag-check-fill::before{content:"";}.bi-bag-check::before{content:"";}.bi-bag-dash-fill::before{content:"";}.bi-bag-dash::before{content:"";}.bi-bag-fill::before{content:"";}.bi-bag-plus-fill::before{content:"";}.bi-bag-plus::before{content:"";}.bi-bag-x-fill::before{content:"";}.bi-bag-x::before{content:"";}.bi-bag::before{content:"";}.bi-bar-chart-fill::before{content:"";}.bi-bar-chart-line-fill::before{content:"";}.bi-bar-chart-line::before{content:"";}.bi-bar-chart-steps::before{content:"";}.bi-bar-chart::before{content:"";}.bi-basket-fill::before{content:"";}.bi-basket::before{content:"";}.bi-basket2-fill::before{content:"";}.bi-basket2::before{content:"";}.bi-basket3-fill::before{content:"";}.bi-basket3::before{content:"";}.bi-battery-charging::before{content:"";}.bi-battery-full::before{content:"";}.bi-battery-half::before{content:"";}.bi-battery::before{content:"";}.bi-bell-fill::before{content:"";}.bi-bell::before{content:"";}.bi-bezier::before{content:"";}.bi-bezier2::before{content:"";}.bi-bicycle::before{content:"";}.bi-binoculars-fill::before{content:"";}.bi-binoculars::before{content:"";}.bi-blockquote-left::before{content:"";}.bi-blockquote-right::before{content:"";}.bi-book-fill::before{content:"";}.bi-book-half::before{content:"";}.bi-book::before{content:"";}.bi-bookmark-check-fill::before{content:"";}.bi-bookmark-check::before{content:"";}.bi-bookmark-dash-fill::before{content:"";}.bi-bookmark-dash::before{content:"";}.bi-bookmark-fill::before{content:"";}.bi-bookmark-heart-fill::before{content:"";}.bi-bookmark-heart::before{content:"";}.bi-bookmark-plus-fill::before{content:"";}.bi-bookmark-plus::before{content:"";}.bi-bookmark-star-fill::before{content:"";}.bi-bookmark-star::before{content:"";}.bi-bookmark-x-fill::before{content:"";}.bi-bookmark-x::before{content:"";}.bi-bookmark::before{content:"";}.bi-bookmarks-fill::before{content:"";}.bi-bookmarks::before{content:"";}.bi-bookshelf::before{content:"";}.bi-bootstrap-fill::before{content:"";}.bi-bootstrap-reboot::before{content:"";}.bi-bootstrap::before{content:"";}.bi-border-all::before{content:"";}.bi-border-bottom::before{content:"";}.bi-border-center::before{content:"";}.bi-border-inner::before{content:"";}.bi-border-left::before{content:"";}.bi-border-middle::before{content:"";}.bi-border-outer::before{content:"";}.bi-border-right::before{content:"";}.bi-border-style::before{content:"";}.bi-border-top::before{content:"";}.bi-border-width::before{content:"";}.bi-border::before{content:"";}.bi-bounding-box-circles::before{content:"";}.bi-bounding-box::before{content:"";}.bi-box-arrow-down-left::before{content:"";}.bi-box-arrow-down-right::before{content:"";}.bi-box-arrow-down::before{content:"";}.bi-box-arrow-in-down-left::before{content:"";}.bi-box-arrow-in-down-right::before{content:"";}.bi-box-arrow-in-down::before{content:"";}.bi-box-arrow-in-left::before{content:"";}.bi-box-arrow-in-right::before{content:"";}.bi-box-arrow-in-up-left::before{content:"";}.bi-box-arrow-in-up-right::before{content:"";}.bi-box-arrow-in-up::before{content:"";}.bi-box-arrow-left::before{content:"";}.bi-box-arrow-right::before{content:"";}.bi-box-arrow-up-left::before{content:"";}.bi-box-arrow-up-right::before{content:"";}.bi-box-arrow-up::before{content:"";}.bi-box-seam::before{content:"";}.bi-box::before{content:"";}.bi-braces::before{content:"";}.bi-bricks::before{content:"";}.bi-briefcase-fill::before{content:"";}.bi-briefcase::before{content:"";}.bi-brightness-alt-high-fill::before{content:"";}.bi-brightness-alt-high::before{content:"";}.bi-brightness-alt-low-fill::before{content:"";}.bi-brightness-alt-low::before{content:"";}.bi-brightness-high-fill::before{content:"";}.bi-brightness-high::before{content:"";}.bi-brightness-low-fill::before{content:"";}.bi-brightness-low::before{content:"";}.bi-broadcast-pin::before{content:"";}.bi-broadcast::before{content:"";}.bi-brush-fill::before{content:"";}.bi-brush::before{content:"";}.bi-bucket-fill::before{content:"";}.bi-bucket::before{content:"";}.bi-bug-fill::before{content:"";}.bi-bug::before{content:"";}.bi-building::before{content:"";}.bi-bullseye::before{content:"";}.bi-calculator-fill::before{content:"";}.bi-calculator::before{content:"";}.bi-calendar-check-fill::before{content:"";}.bi-calendar-check::before{content:"";}.bi-calendar-date-fill::before{content:"";}.bi-calendar-date::before{content:"";}.bi-calendar-day-fill::before{content:"";}.bi-calendar-day::before{content:"";}.bi-calendar-event-fill::before{content:"";}.bi-calendar-event::before{content:"";}.bi-calendar-fill::before{content:"";}.bi-calendar-minus-fill::before{content:"";}.bi-calendar-minus::before{content:"";}.bi-calendar-month-fill::before{content:"";}.bi-calendar-month::before{content:"";}.bi-calendar-plus-fill::before{content:"";}.bi-calendar-plus::before{content:"";}.bi-calendar-range-fill::before{content:"";}.bi-calendar-range::before{content:"";}.bi-calendar-week-fill::before{content:"";}.bi-calendar-week::before{content:"";}.bi-calendar-x-fill::before{content:"";}.bi-calendar-x::before{content:"";}.bi-calendar::before{content:"";}.bi-calendar2-check-fill::before{content:"";}.bi-calendar2-check::before{content:"";}.bi-calendar2-date-fill::before{content:"";}.bi-calendar2-date::before{content:"";}.bi-calendar2-day-fill::before{content:"";}.bi-calendar2-day::before{content:"";}.bi-calendar2-event-fill::before{content:"";}.bi-calendar2-event::before{content:"";}.bi-calendar2-fill::before{content:"";}.bi-calendar2-minus-fill::before{content:"";}.bi-calendar2-minus::before{content:"";}.bi-calendar2-month-fill::before{content:"";}.bi-calendar2-month::before{content:"";}.bi-calendar2-plus-fill::before{content:"";}.bi-calendar2-plus::before{content:"";}.bi-calendar2-range-fill::before{content:"";}.bi-calendar2-range::before{content:"";}.bi-calendar2-week-fill::before{content:"";}.bi-calendar2-week::before{content:"";}.bi-calendar2-x-fill::before{content:"";}.bi-calendar2-x::before{content:"";}.bi-calendar2::before{content:"";}.bi-calendar3-event-fill::before{content:"";}.bi-calendar3-event::before{content:"";}.bi-calendar3-fill::before{content:"";}.bi-calendar3-range-fill::before{content:"";}.bi-calendar3-range::before{content:"";}.bi-calendar3-week-fill::before{content:"";}.bi-calendar3-week::before{content:"";}.bi-calendar3::before{content:"";}.bi-calendar4-event::before{content:"";}.bi-calendar4-range::before{content:"";}.bi-calendar4-week::before{content:"";}.bi-calendar4::before{content:"";}.bi-camera-fill::before{content:"";}.bi-camera-reels-fill::before{content:"";}.bi-camera-reels::before{content:"";}.bi-camera-video-fill::before{content:"";}.bi-camera-video-off-fill::before{content:"";}.bi-camera-video-off::before{content:"";}.bi-camera-video::before{content:"";}.bi-camera::before{content:"";}.bi-camera2::before{content:"";}.bi-capslock-fill::before{content:"";}.bi-capslock::before{content:"";}.bi-card-checklist::before{content:"";}.bi-card-heading::before{content:"";}.bi-card-image::before{content:"";}.bi-card-list::before{content:"";}.bi-card-text::before{content:"";}.bi-caret-down-fill::before{content:"";}.bi-caret-down-square-fill::before{content:"";}.bi-caret-down-square::before{content:"";}.bi-caret-down::before{content:"";}.bi-caret-left-fill::before{content:"";}.bi-caret-left-square-fill::before{content:"";}.bi-caret-left-square::before{content:"";}.bi-caret-left::before{content:"";}.bi-caret-right-fill::before{content:"";}.bi-caret-right-square-fill::before{content:"";}.bi-caret-right-square::before{content:"";}.bi-caret-right::before{content:"";}.bi-caret-up-fill::before{content:"";}.bi-caret-up-square-fill::before{content:"";}.bi-caret-up-square::before{content:"";}.bi-caret-up::before{content:"";}.bi-cart-check-fill::before{content:"";}.bi-cart-check::before{content:"";}.bi-cart-dash-fill::before{content:"";}.bi-cart-dash::before{content:"";}.bi-cart-fill::before{content:"";}.bi-cart-plus-fill::before{content:"";}.bi-cart-plus::before{content:"";}.bi-cart-x-fill::before{content:"";}.bi-cart-x::before{content:"";}.bi-cart::before{content:"";}.bi-cart2::before{content:"";}.bi-cart3::before{content:"";}.bi-cart4::before{content:"";}.bi-cash-stack::before{content:"";}.bi-cash::before{content:"";}.bi-cast::before{content:"";}.bi-chat-dots-fill::before{content:"";}.bi-chat-dots::before{content:"";}.bi-chat-fill::before{content:"";}.bi-chat-left-dots-fill::before{content:"";}.bi-chat-left-dots::before{content:"";}.bi-chat-left-fill::before{content:"";}.bi-chat-left-quote-fill::before{content:"";}.bi-chat-left-quote::before{content:"";}.bi-chat-left-text-fill::before{content:"";}.bi-chat-left-text::before{content:"";}.bi-chat-left::before{content:"";}.bi-chat-quote-fill::before{content:"";}.bi-chat-quote::before{content:"";}.bi-chat-right-dots-fill::before{content:"";}.bi-chat-right-dots::before{content:"";}.bi-chat-right-fill::before{content:"";}.bi-chat-right-quote-fill::before{content:"";}.bi-chat-right-quote::before{content:"";}.bi-chat-right-text-fill::before{content:"";}.bi-chat-right-text::before{content:"";}.bi-chat-right::before{content:"";}.bi-chat-square-dots-fill::before{content:"";}.bi-chat-square-dots::before{content:"";}.bi-chat-square-fill::before{content:"";}.bi-chat-square-quote-fill::before{content:"";}.bi-chat-square-quote::before{content:"";}.bi-chat-square-text-fill::before{content:"";}.bi-chat-square-text::before{content:"";}.bi-chat-square::before{content:"";}.bi-chat-text-fill::before{content:"";}.bi-chat-text::before{content:"";}.bi-chat::before{content:"";}.bi-check-all::before{content:"";}.bi-check-circle-fill::before{content:"";}.bi-check-circle::before{content:"";}.bi-check-square-fill::before{content:"";}.bi-check-square::before{content:"";}.bi-check::before{content:"";}.bi-check2-all::before{content:"";}.bi-check2-circle::before{content:"";}.bi-check2-square::before{content:"";}.bi-check2::before{content:"";}.bi-chevron-bar-contract::before{content:"";}.bi-chevron-bar-down::before{content:"";}.bi-chevron-bar-expand::before{content:"";}.bi-chevron-bar-left::before{content:"";}.bi-chevron-bar-right::before{content:"";}.bi-chevron-bar-up::before{content:"";}.bi-chevron-compact-down::before{content:"";}.bi-chevron-compact-left::before{content:"";}.bi-chevron-compact-right::before{content:"";}.bi-chevron-compact-up::before{content:"";}.bi-chevron-contract::before{content:"";}.bi-chevron-double-down::before{content:"";}.bi-chevron-double-left::before{content:"";}.bi-chevron-double-right::before{content:"";}.bi-chevron-double-up::before{content:"";}.bi-chevron-down::before{content:"";}.bi-chevron-expand::before{content:"";}.bi-chevron-left::before{content:"";}.bi-chevron-right::before{content:"";}.bi-chevron-up::before{content:"";}.bi-circle-fill::before{content:"";}.bi-circle-half::before{content:"";}.bi-circle-square::before{content:"";}.bi-circle::before{content:"";}.bi-clipboard-check::before{content:"";}.bi-clipboard-data::before{content:"";}.bi-clipboard-minus::before{content:"";}.bi-clipboard-plus::before{content:"";}.bi-clipboard-x::before{content:"";}.bi-clipboard::before{content:"";}.bi-clock-fill::before{content:"";}.bi-clock-history::before{content:"";}.bi-clock::before{content:"";}.bi-cloud-arrow-down-fill::before{content:"";}.bi-cloud-arrow-down::before{content:"";}.bi-cloud-arrow-up-fill::before{content:"";}.bi-cloud-arrow-up::before{content:"";}.bi-cloud-check-fill::before{content:"";}.bi-cloud-check::before{content:"";}.bi-cloud-download-fill::before{content:"";}.bi-cloud-download::before{content:"";}.bi-cloud-drizzle-fill::before{content:"";}.bi-cloud-drizzle::before{content:"";}.bi-cloud-fill::before{content:"";}.bi-cloud-fog-fill::before{content:"";}.bi-cloud-fog::before{content:"";}.bi-cloud-fog2-fill::before{content:"";}.bi-cloud-fog2::before{content:"";}.bi-cloud-hail-fill::before{content:"";}.bi-cloud-hail::before{content:"";}.bi-cloud-haze-1::before{content:"";}.bi-cloud-haze-fill::before{content:"";}.bi-cloud-haze::before{content:"";}.bi-cloud-haze2-fill::before{content:"";}.bi-cloud-lightning-fill::before{content:"";}.bi-cloud-lightning-rain-fill::before{content:"";}.bi-cloud-lightning-rain::before{content:"";}.bi-cloud-lightning::before{content:"";}.bi-cloud-minus-fill::before{content:"";}.bi-cloud-minus::before{content:"";}.bi-cloud-moon-fill::before{content:"";}.bi-cloud-moon::before{content:"";}.bi-cloud-plus-fill::before{content:"";}.bi-cloud-plus::before{content:"";}.bi-cloud-rain-fill::before{content:"";}.bi-cloud-rain-heavy-fill::before{content:"";}.bi-cloud-rain-heavy::before{content:"";}.bi-cloud-rain::before{content:"";}.bi-cloud-slash-fill::before{content:"";}.bi-cloud-slash::before{content:"";}.bi-cloud-sleet-fill::before{content:"";}.bi-cloud-sleet::before{content:"";}.bi-cloud-snow-fill::before{content:"";}.bi-cloud-snow::before{content:"";}.bi-cloud-sun-fill::before{content:"";}.bi-cloud-sun::before{content:"";}.bi-cloud-upload-fill::before{content:"";}.bi-cloud-upload::before{content:"";}.bi-cloud::before{content:"";}.bi-clouds-fill::before{content:"";}.bi-clouds::before{content:"";}.bi-cloudy-fill::before{content:"";}.bi-cloudy::before{content:"";}.bi-code-slash::before{content:"";}.bi-code-square::before{content:"";}.bi-code::before{content:"";}.bi-collection-fill::before{content:"";}.bi-collection-play-fill::before{content:"";}.bi-collection-play::before{content:"";}.bi-collection::before{content:"";}.bi-columns-gap::before{content:"";}.bi-columns::before{content:"";}.bi-command::before{content:"";}.bi-compass-fill::before{content:"";}.bi-compass::before{content:"";}.bi-cone-striped::before{content:"";}.bi-cone::before{content:"";}.bi-controller::before{content:"";}.bi-cpu-fill::before{content:"";}.bi-cpu::before{content:"";}.bi-credit-card-2-back-fill::before{content:"";}.bi-credit-card-2-back::before{content:"";}.bi-credit-card-2-front-fill::before{content:"";}.bi-credit-card-2-front::before{content:"";}.bi-credit-card-fill::before{content:"";}.bi-credit-card::before{content:"";}.bi-crop::before{content:"";}.bi-cup-fill::before{content:"";}.bi-cup-straw::before{content:"";}.bi-cup::before{content:"";}.bi-cursor-fill::before{content:"";}.bi-cursor-text::before{content:"";}.bi-cursor::before{content:"";}.bi-dash-circle-dotted::before{content:"";}.bi-dash-circle-fill::before{content:"";}.bi-dash-circle::before{content:"";}.bi-dash-square-dotted::before{content:"";}.bi-dash-square-fill::before{content:"";}.bi-dash-square::before{content:"";}.bi-dash::before{content:"";}.bi-diagram-2-fill::before{content:"";}.bi-diagram-2::before{content:"";}.bi-diagram-3-fill::before{content:"";}.bi-diagram-3::before{content:"";}.bi-diamond-fill::before{content:"";}.bi-diamond-half::before{content:"";}.bi-diamond::before{content:"";}.bi-dice-1-fill::before{content:"";}.bi-dice-1::before{content:"";}.bi-dice-2-fill::before{content:"";}.bi-dice-2::before{content:"";}.bi-dice-3-fill::before{content:"";}.bi-dice-3::before{content:"";}.bi-dice-4-fill::before{content:"";}.bi-dice-4::before{content:"";}.bi-dice-5-fill::before{content:"";}.bi-dice-5::before{content:"";}.bi-dice-6-fill::before{content:"";}.bi-dice-6::before{content:"";}.bi-disc-fill::before{content:"";}.bi-disc::before{content:"";}.bi-discord::before{content:"";}.bi-display-fill::before{content:"";}.bi-display::before{content:"";}.bi-distribute-horizontal::before{content:"";}.bi-distribute-vertical::before{content:"";}.bi-door-closed-fill::before{content:"";}.bi-door-closed::before{content:"";}.bi-door-open-fill::before{content:"";}.bi-door-open::before{content:"";}.bi-dot::before{content:"";}.bi-download::before{content:"";}.bi-droplet-fill::before{content:"";}.bi-droplet-half::before{content:"";}.bi-droplet::before{content:"";}.bi-earbuds::before{content:"";}.bi-easel-fill::before{content:"";}.bi-easel::before{content:"";}.bi-egg-fill::before{content:"";}.bi-egg-fried::before{content:"";}.bi-egg::before{content:"";}.bi-eject-fill::before{content:"";}.bi-eject::before{content:"";}.bi-emoji-angry-fill::before{content:"";}.bi-emoji-angry::before{content:"";}.bi-emoji-dizzy-fill::before{content:"";}.bi-emoji-dizzy::before{content:"";}.bi-emoji-expressionless-fill::before{content:"";}.bi-emoji-expressionless::before{content:"";}.bi-emoji-frown-fill::before{content:"";}.bi-emoji-frown::before{content:"";}.bi-emoji-heart-eyes-fill::before{content:"";}.bi-emoji-heart-eyes::before{content:"";}.bi-emoji-laughing-fill::before{content:"";}.bi-emoji-laughing::before{content:"";}.bi-emoji-neutral-fill::before{content:"";}.bi-emoji-neutral::before{content:"";}.bi-emoji-smile-fill::before{content:"";}.bi-emoji-smile-upside-down-fill::before{content:"";}.bi-emoji-smile-upside-down::before{content:"";}.bi-emoji-smile::before{content:"";}.bi-emoji-sunglasses-fill::before{content:"";}.bi-emoji-sunglasses::before{content:"";}.bi-emoji-wink-fill::before{content:"";}.bi-emoji-wink::before{content:"";}.bi-envelope-fill::before{content:"";}.bi-envelope-open-fill::before{content:"";}.bi-envelope-open::before{content:"";}.bi-envelope::before{content:"";}.bi-eraser-fill::before{content:"";}.bi-eraser::before{content:"";}.bi-exclamation-circle-fill::before{content:"";}.bi-exclamation-circle::before{content:"";}.bi-exclamation-diamond-fill::before{content:"";}.bi-exclamation-diamond::before{content:"";}.bi-exclamation-octagon-fill::before{content:"";}.bi-exclamation-octagon::before{content:"";}.bi-exclamation-square-fill::before{content:"";}.bi-exclamation-square::before{content:"";}.bi-exclamation-triangle-fill::before{content:"";}.bi-exclamation-triangle::before{content:"";}.bi-exclamation::before{content:"";}.bi-exclude::before{content:"";}.bi-eye-fill::before{content:"";}.bi-eye-slash-fill::before{content:"";}.bi-eye-slash::before{content:"";}.bi-eye::before{content:"";}.bi-eyedropper::before{content:"";}.bi-eyeglasses::before{content:"";}.bi-facebook::before{content:"";}.bi-file-arrow-down-fill::before{content:"";}.bi-file-arrow-down::before{content:"";}.bi-file-arrow-up-fill::before{content:"";}.bi-file-arrow-up::before{content:"";}.bi-file-bar-graph-fill::before{content:"";}.bi-file-bar-graph::before{content:"";}.bi-file-binary-fill::before{content:"";}.bi-file-binary::before{content:"";}.bi-file-break-fill::before{content:"";}.bi-file-break::before{content:"";}.bi-file-check-fill::before{content:"";}.bi-file-check::before{content:"";}.bi-file-code-fill::before{content:"";}.bi-file-code::before{content:"";}.bi-file-diff-fill::before{content:"";}.bi-file-diff::before{content:"";}.bi-file-earmark-arrow-down-fill::before{content:"";}.bi-file-earmark-arrow-down::before{content:"";}.bi-file-earmark-arrow-up-fill::before{content:"";}.bi-file-earmark-arrow-up::before{content:"";}.bi-file-earmark-bar-graph-fill::before{content:"";}.bi-file-earmark-bar-graph::before{content:"";}.bi-file-earmark-binary-fill::before{content:"";}.bi-file-earmark-binary::before{content:"";}.bi-file-earmark-break-fill::before{content:"";}.bi-file-earmark-break::before{content:"";}.bi-file-earmark-check-fill::before{content:"";}.bi-file-earmark-check::before{content:"";}.bi-file-earmark-code-fill::before{content:"";}.bi-file-earmark-code::before{content:"";}.bi-file-earmark-diff-fill::before{content:"";}.bi-file-earmark-diff::before{content:"";}.bi-file-earmark-easel-fill::before{content:"";}.bi-file-earmark-easel::before{content:"";}.bi-file-earmark-excel-fill::before{content:"";}.bi-file-earmark-excel::before{content:"";}.bi-file-earmark-fill::before{content:"";}.bi-file-earmark-font-fill::before{content:"";}.bi-file-earmark-font::before{content:"";}.bi-file-earmark-image-fill::before{content:"";}.bi-file-earmark-image::before{content:"";}.bi-file-earmark-lock-fill::before{content:"";}.bi-file-earmark-lock::before{content:"";}.bi-file-earmark-lock2-fill::before{content:"";}.bi-file-earmark-lock2::before{content:"";}.bi-file-earmark-medical-fill::before{content:"";}.bi-file-earmark-medical::before{content:"";}.bi-file-earmark-minus-fill::before{content:"";}.bi-file-earmark-minus::before{content:"";}.bi-file-earmark-music-fill::before{content:"";}.bi-file-earmark-music::before{content:"";}.bi-file-earmark-person-fill::before{content:"";}.bi-file-earmark-person::before{content:"";}.bi-file-earmark-play-fill::before{content:"";}.bi-file-earmark-play::before{content:"";}.bi-file-earmark-plus-fill::before{content:"";}.bi-file-earmark-plus::before{content:"";}.bi-file-earmark-post-fill::before{content:"";}.bi-file-earmark-post::before{content:"";}.bi-file-earmark-ppt-fill::before{content:"";}.bi-file-earmark-ppt::before{content:"";}.bi-file-earmark-richtext-fill::before{content:"";}.bi-file-earmark-richtext::before{content:"";}.bi-file-earmark-ruled-fill::before{content:"";}.bi-file-earmark-ruled::before{content:"";}.bi-file-earmark-slides-fill::before{content:"";}.bi-file-earmark-slides::before{content:"";}.bi-file-earmark-spreadsheet-fill::before{content:"";}.bi-file-earmark-spreadsheet::before{content:"";}.bi-file-earmark-text-fill::before{content:"";}.bi-file-earmark-text::before{content:"";}.bi-file-earmark-word-fill::before{content:"";}.bi-file-earmark-word::before{content:"";}.bi-file-earmark-x-fill::before{content:"";}.bi-file-earmark-x::before{content:"";}.bi-file-earmark-zip-fill::before{content:"";}.bi-file-earmark-zip::before{content:"";}.bi-file-earmark::before{content:"";}.bi-file-easel-fill::before{content:"";}.bi-file-easel::before{content:"";}.bi-file-excel-fill::before{content:"";}.bi-file-excel::before{content:"";}.bi-file-fill::before{content:"";}.bi-file-font-fill::before{content:"";}.bi-file-font::before{content:"";}.bi-file-image-fill::before{content:"";}.bi-file-image::before{content:"";}.bi-file-lock-fill::before{content:"";}.bi-file-lock::before{content:"";}.bi-file-lock2-fill::before{content:"";}.bi-file-lock2::before{content:"";}.bi-file-medical-fill::before{content:"";}.bi-file-medical::before{content:"";}.bi-file-minus-fill::before{content:"";}.bi-file-minus::before{content:"";}.bi-file-music-fill::before{content:"";}.bi-file-music::before{content:"";}.bi-file-person-fill::before{content:"";}.bi-file-person::before{content:"";}.bi-file-play-fill::before{content:"";}.bi-file-play::before{content:"";}.bi-file-plus-fill::before{content:"";}.bi-file-plus::before{content:"";}.bi-file-post-fill::before{content:"";}.bi-file-post::before{content:"";}.bi-file-ppt-fill::before{content:"";}.bi-file-ppt::before{content:"";}.bi-file-richtext-fill::before{content:"";}.bi-file-richtext::before{content:"";}.bi-file-ruled-fill::before{content:"";}.bi-file-ruled::before{content:"";}.bi-file-slides-fill::before{content:"";}.bi-file-slides::before{content:"";}.bi-file-spreadsheet-fill::before{content:"";}.bi-file-spreadsheet::before{content:"";}.bi-file-text-fill::before{content:"";}.bi-file-text::before{content:"";}.bi-file-word-fill::before{content:"";}.bi-file-word::before{content:"";}.bi-file-x-fill::before{content:"";}.bi-file-x::before{content:"";}.bi-file-zip-fill::before{content:"";}.bi-file-zip::before{content:"";}.bi-file::before{content:"";}.bi-files-alt::before{content:"";}.bi-files::before{content:"";}.bi-film::before{content:"";}.bi-filter-circle-fill::before{content:"";}.bi-filter-circle::before{content:"";}.bi-filter-left::before{content:"";}.bi-filter-right::before{content:"";}.bi-filter-square-fill::before{content:"";}.bi-filter-square::before{content:"";}.bi-filter::before{content:"";}.bi-flag-fill::before{content:"";}.bi-flag::before{content:"";}.bi-flower1::before{content:"";}.bi-flower2::before{content:"";}.bi-flower3::before{content:"";}.bi-folder-check::before{content:"";}.bi-folder-fill::before{content:"";}.bi-folder-minus::before{content:"";}.bi-folder-plus::before{content:"";}.bi-folder-symlink-fill::before{content:"";}.bi-folder-symlink::before{content:"";}.bi-folder-x::before{content:"";}.bi-folder::before{content:"";}.bi-folder2-open::before{content:"";}.bi-folder2::before{content:"";}.bi-fonts::before{content:"";}.bi-forward-fill::before{content:"";}.bi-forward::before{content:"";}.bi-front::before{content:"";}.bi-fullscreen-exit::before{content:"";}.bi-fullscreen::before{content:"";}.bi-funnel-fill::before{content:"";}.bi-funnel::before{content:"";}.bi-gear-fill::before{content:"";}.bi-gear-wide-connected::before{content:"";}.bi-gear-wide::before{content:"";}.bi-gear::before{content:"";}.bi-gem::before{content:"";}.bi-geo-alt-fill::before{content:"";}.bi-geo-alt::before{content:"";}.bi-geo-fill::before{content:"";}.bi-geo::before{content:"";}.bi-gift-fill::before{content:"";}.bi-gift::before{content:"";}.bi-github::before{content:"";}.bi-globe::before{content:"";}.bi-globe2::before{content:"";}.bi-google::before{content:"";}.bi-graph-down::before{content:"";}.bi-graph-up::before{content:"";}.bi-grid-1x2-fill::before{content:"";}.bi-grid-1x2::before{content:"";}.bi-grid-3x2-gap-fill::before{content:"";}.bi-grid-3x2-gap::before{content:"";}.bi-grid-3x2::before{content:"";}.bi-grid-3x3-gap-fill::before{content:"";}.bi-grid-3x3-gap::before{content:"";}.bi-grid-3x3::before{content:"";}.bi-grid-fill::before{content:"";}.bi-grid::before{content:"";}.bi-grip-horizontal::before{content:"";}.bi-grip-vertical::before{content:"";}.bi-hammer::before{content:"";}.bi-hand-index-fill::before{content:"";}.bi-hand-index-thumb-fill::before{content:"";}.bi-hand-index-thumb::before{content:"";}.bi-hand-index::before{content:"";}.bi-hand-thumbs-down-fill::before{content:"";}.bi-hand-thumbs-down::before{content:"";}.bi-hand-thumbs-up-fill::before{content:"";}.bi-hand-thumbs-up::before{content:"";}.bi-handbag-fill::before{content:"";}.bi-handbag::before{content:"";}.bi-hash::before{content:"";}.bi-hdd-fill::before{content:"";}.bi-hdd-network-fill::before{content:"";}.bi-hdd-network::before{content:"";}.bi-hdd-rack-fill::before{content:"";}.bi-hdd-rack::before{content:"";}.bi-hdd-stack-fill::before{content:"";}.bi-hdd-stack::before{content:"";}.bi-hdd::before{content:"";}.bi-headphones::before{content:"";}.bi-headset::before{content:"";}.bi-heart-fill::before{content:"";}.bi-heart-half::before{content:"";}.bi-heart::before{content:"";}.bi-heptagon-fill::before{content:"";}.bi-heptagon-half::before{content:"";}.bi-heptagon::before{content:"";}.bi-hexagon-fill::before{content:"";}.bi-hexagon-half::before{content:"";}.bi-hexagon::before{content:"";}.bi-hourglass-bottom::before{content:"";}.bi-hourglass-split::before{content:"";}.bi-hourglass-top::before{content:"";}.bi-hourglass::before{content:"";}.bi-house-door-fill::before{content:"";}.bi-house-door::before{content:"";}.bi-house-fill::before{content:"";}.bi-house::before{content:"";}.bi-hr::before{content:"";}.bi-hurricane::before{content:"";}.bi-image-alt::before{content:"";}.bi-image-fill::before{content:"";}.bi-image::before{content:"";}.bi-images::before{content:"";}.bi-inbox-fill::before{content:"";}.bi-inbox::before{content:"";}.bi-inboxes-fill::before{content:"";}.bi-inboxes::before{content:"";}.bi-info-circle-fill::before{content:"";}.bi-info-circle::before{content:"";}.bi-info-square-fill::before{content:"";}.bi-info-square::before{content:"";}.bi-info::before{content:"";}.bi-input-cursor-text::before{content:"";}.bi-input-cursor::before{content:"";}.bi-instagram::before{content:"";}.bi-intersect::before{content:"";}.bi-journal-album::before{content:"";}.bi-journal-arrow-down::before{content:"";}.bi-journal-arrow-up::before{content:"";}.bi-journal-bookmark-fill::before{content:"";}.bi-journal-bookmark::before{content:"";}.bi-journal-check::before{content:"";}.bi-journal-code::before{content:"";}.bi-journal-medical::before{content:"";}.bi-journal-minus::before{content:"";}.bi-journal-plus::before{content:"";}.bi-journal-richtext::before{content:"";}.bi-journal-text::before{content:"";}.bi-journal-x::before{content:"";}.bi-journal::before{content:"";}.bi-journals::before{content:"";}.bi-joystick::before{content:"";}.bi-justify-left::before{content:"";}.bi-justify-right::before{content:"";}.bi-justify::before{content:"";}.bi-kanban-fill::before{content:"";}.bi-kanban::before{content:"";}.bi-key-fill::before{content:"";}.bi-key::before{content:"";}.bi-keyboard-fill::before{content:"";}.bi-keyboard::before{content:"";}.bi-ladder::before{content:"";}.bi-lamp-fill::before{content:"";}.bi-lamp::before{content:"";}.bi-laptop-fill::before{content:"";}.bi-laptop::before{content:"";}.bi-layer-backward::before{content:"";}.bi-layer-forward::before{content:"";}.bi-layers-fill::before{content:"";}.bi-layers-half::before{content:"";}.bi-layers::before{content:"";}.bi-layout-sidebar-inset-reverse::before{content:"";}.bi-layout-sidebar-inset::before{content:"";}.bi-layout-sidebar-reverse::before{content:"";}.bi-layout-sidebar::before{content:"";}.bi-layout-split::before{content:"";}.bi-layout-text-sidebar-reverse::before{content:"";}.bi-layout-text-sidebar::before{content:"";}.bi-layout-text-window-reverse::before{content:"";}.bi-layout-text-window::before{content:"";}.bi-layout-three-columns::before{content:"";}.bi-layout-wtf::before{content:"";}.bi-life-preserver::before{content:"";}.bi-lightbulb-fill::before{content:"";}.bi-lightbulb-off-fill::before{content:"";}.bi-lightbulb-off::before{content:"";}.bi-lightbulb::before{content:"";}.bi-lightning-charge-fill::before{content:"";}.bi-lightning-charge::before{content:"";}.bi-lightning-fill::before{content:"";}.bi-lightning::before{content:"";}.bi-link-45deg::before{content:"";}.bi-link::before{content:"";}.bi-linkedin::before{content:"";}.bi-list-check::before{content:"";}.bi-list-nested::before{content:"";}.bi-list-ol::before{content:"";}.bi-list-stars::before{content:"";}.bi-list-task::before{content:"";}.bi-list-ul::before{content:"";}.bi-list::before{content:"";}.bi-lock-fill::before{content:"";}.bi-lock::before{content:"";}.bi-mailbox::before{content:"";}.bi-mailbox2::before{content:"";}.bi-map-fill::before{content:"";}.bi-map::before{content:"";}.bi-markdown-fill::before{content:"";}.bi-markdown::before{content:"";}.bi-mask::before{content:"";}.bi-megaphone-fill::before{content:"";}.bi-megaphone::before{content:"";}.bi-menu-app-fill::before{content:"";}.bi-menu-app::before{content:"";}.bi-menu-button-fill::before{content:"";}.bi-menu-button-wide-fill::before{content:"";}.bi-menu-button-wide::before{content:"";}.bi-menu-button::before{content:"";}.bi-menu-down::before{content:"";}.bi-menu-up::before{content:"";}.bi-mic-fill::before{content:"";}.bi-mic-mute-fill::before{content:"";}.bi-mic-mute::before{content:"";}.bi-mic::before{content:"";}.bi-minecart-loaded::before{content:"";}.bi-minecart::before{content:"";}.bi-moisture::before{content:"";}.bi-moon-fill::before{content:"";}.bi-moon-stars-fill::before{content:"";}.bi-moon-stars::before{content:"";}.bi-moon::before{content:"";}.bi-mouse-fill::before{content:"";}.bi-mouse::before{content:"";}.bi-mouse2-fill::before{content:"";}.bi-mouse2::before{content:"";}.bi-mouse3-fill::before{content:"";}.bi-mouse3::before{content:"";}.bi-music-note-beamed::before{content:"";}.bi-music-note-list::before{content:"";}.bi-music-note::before{content:"";}.bi-music-player-fill::before{content:"";}.bi-music-player::before{content:"";}.bi-newspaper::before{content:"";}.bi-node-minus-fill::before{content:"";}.bi-node-minus::before{content:"";}.bi-node-plus-fill::before{content:"";}.bi-node-plus::before{content:"";}.bi-nut-fill::before{content:"";}.bi-nut::before{content:"";}.bi-octagon-fill::before{content:"";}.bi-octagon-half::before{content:"";}.bi-octagon::before{content:"";}.bi-option::before{content:"";}.bi-outlet::before{content:"";}.bi-paint-bucket::before{content:"";}.bi-palette-fill::before{content:"";}.bi-palette::before{content:"";}.bi-palette2::before{content:"";}.bi-paperclip::before{content:"";}.bi-paragraph::before{content:"";}.bi-patch-check-fill::before{content:"";}.bi-patch-check::before{content:"";}.bi-patch-exclamation-fill::before{content:"";}.bi-patch-exclamation::before{content:"";}.bi-patch-minus-fill::before{content:"";}.bi-patch-minus::before{content:"";}.bi-patch-plus-fill::before{content:"";}.bi-patch-plus::before{content:"";}.bi-patch-question-fill::before{content:"";}.bi-patch-question::before{content:"";}.bi-pause-btn-fill::before{content:"";}.bi-pause-btn::before{content:"";}.bi-pause-circle-fill::before{content:"";}.bi-pause-circle::before{content:"";}.bi-pause-fill::before{content:"";}.bi-pause::before{content:"";}.bi-peace-fill::before{content:"";}.bi-peace::before{content:"";}.bi-pen-fill::before{content:"";}.bi-pen::before{content:"";}.bi-pencil-fill::before{content:"";}.bi-pencil-square::before{content:"";}.bi-pencil::before{content:"";}.bi-pentagon-fill::before{content:"";}.bi-pentagon-half::before{content:"";}.bi-pentagon::before{content:"";}.bi-people-fill::before{content:"";}.bi-people::before{content:"";}.bi-percent::before{content:"";}.bi-person-badge-fill::before{content:"";}.bi-person-badge::before{content:"";}.bi-person-bounding-box::before{content:"";}.bi-person-check-fill::before{content:"";}.bi-person-check::before{content:"";}.bi-person-circle::before{content:"";}.bi-person-dash-fill::before{content:"";}.bi-person-dash::before{content:"";}.bi-person-fill::before{content:"";}.bi-person-lines-fill::before{content:"";}.bi-person-plus-fill::before{content:"";}.bi-person-plus::before{content:"";}.bi-person-square::before{content:"";}.bi-person-x-fill::before{content:"";}.bi-person-x::before{content:"";}.bi-person::before{content:"";}.bi-phone-fill::before{content:"";}.bi-phone-landscape-fill::before{content:"";}.bi-phone-landscape::before{content:"";}.bi-phone-vibrate-fill::before{content:"";}.bi-phone-vibrate::before{content:"";}.bi-phone::before{content:"";}.bi-pie-chart-fill::before{content:"";}.bi-pie-chart::before{content:"";}.bi-pin-angle-fill::before{content:"";}.bi-pin-angle::before{content:"";}.bi-pin-fill::before{content:"";}.bi-pin::before{content:"";}.bi-pip-fill::before{content:"";}.bi-pip::before{content:"";}.bi-play-btn-fill::before{content:"";}.bi-play-btn::before{content:"";}.bi-play-circle-fill::before{content:"";}.bi-play-circle::before{content:"";}.bi-play-fill::before{content:"";}.bi-play::before{content:"";}.bi-plug-fill::before{content:"";}.bi-plug::before{content:"";}.bi-plus-circle-dotted::before{content:"";}.bi-plus-circle-fill::before{content:"";}.bi-plus-circle::before{content:"";}.bi-plus-square-dotted::before{content:"";}.bi-plus-square-fill::before{content:"";}.bi-plus-square::before{content:"";}.bi-plus::before{content:"";}.bi-power::before{content:"";}.bi-printer-fill::before{content:"";}.bi-printer::before{content:"";}.bi-puzzle-fill::before{content:"";}.bi-puzzle::before{content:"";}.bi-question-circle-fill::before{content:"";}.bi-question-circle::before{content:"";}.bi-question-diamond-fill::before{content:"";}.bi-question-diamond::before{content:"";}.bi-question-octagon-fill::before{content:"";}.bi-question-octagon::before{content:"";}.bi-question-square-fill::before{content:"";}.bi-question-square::before{content:"";}.bi-question::before{content:"";}.bi-rainbow::before{content:"";}.bi-receipt-cutoff::before{content:"";}.bi-receipt::before{content:"";}.bi-reception-0::before{content:"";}.bi-reception-1::before{content:"";}.bi-reception-2::before{content:"";}.bi-reception-3::before{content:"";}.bi-reception-4::before{content:"";}.bi-record-btn-fill::before{content:"";}.bi-record-btn::before{content:"";}.bi-record-circle-fill::before{content:"";}.bi-record-circle::before{content:"";}.bi-record-fill::before{content:"";}.bi-record::before{content:"";}.bi-record2-fill::before{content:"";}.bi-record2::before{content:"";}.bi-reply-all-fill::before{content:"";}.bi-reply-all::before{content:"";}.bi-reply-fill::before{content:"";}.bi-reply::before{content:"";}.bi-rss-fill::before{content:"";}.bi-rss::before{content:"";}.bi-rulers::before{content:"";}.bi-save-fill::before{content:"";}.bi-save::before{content:"";}.bi-save2-fill::before{content:"";}.bi-save2::before{content:"";}.bi-scissors::before{content:"";}.bi-screwdriver::before{content:"";}.bi-search::before{content:"";}.bi-segmented-nav::before{content:"";}.bi-server::before{content:"";}.bi-share-fill::before{content:"";}.bi-share::before{content:"";}.bi-shield-check::before{content:"";}.bi-shield-exclamation::before{content:"";}.bi-shield-fill-check::before{content:"";}.bi-shield-fill-exclamation::before{content:"";}.bi-shield-fill-minus::before{content:"";}.bi-shield-fill-plus::before{content:"";}.bi-shield-fill-x::before{content:"";}.bi-shield-fill::before{content:"";}.bi-shield-lock-fill::before{content:"";}.bi-shield-lock::before{content:"";}.bi-shield-minus::before{content:"";}.bi-shield-plus::before{content:"";}.bi-shield-shaded::before{content:"";}.bi-shield-slash-fill::before{content:"";}.bi-shield-slash::before{content:"";}.bi-shield-x::before{content:"";}.bi-shield::before{content:"";}.bi-shift-fill::before{content:"";}.bi-shift::before{content:"";}.bi-shop-window::before{content:"";}.bi-shop::before{content:"";}.bi-shuffle::before{content:"";}.bi-signpost-2-fill::before{content:"";}.bi-signpost-2::before{content:"";}.bi-signpost-fill::before{content:"";}.bi-signpost-split-fill::before{content:"";}.bi-signpost-split::before{content:"";}.bi-signpost::before{content:"";}.bi-sim-fill::before{content:"";}.bi-sim::before{content:"";}.bi-skip-backward-btn-fill::before{content:"";}.bi-skip-backward-btn::before{content:"";}.bi-skip-backward-circle-fill::before{content:"";}.bi-skip-backward-circle::before{content:"";}.bi-skip-backward-fill::before{content:"";}.bi-skip-backward::before{content:"";}.bi-skip-end-btn-fill::before{content:"";}.bi-skip-end-btn::before{content:"";}.bi-skip-end-circle-fill::before{content:"";}.bi-skip-end-circle::before{content:"";}.bi-skip-end-fill::before{content:"";}.bi-skip-end::before{content:"";}.bi-skip-forward-btn-fill::before{content:"";}.bi-skip-forward-btn::before{content:"";}.bi-skip-forward-circle-fill::before{content:"";}.bi-skip-forward-circle::before{content:"";}.bi-skip-forward-fill::before{content:"";}.bi-skip-forward::before{content:"";}.bi-skip-start-btn-fill::before{content:"";}.bi-skip-start-btn::before{content:"";}.bi-skip-start-circle-fill::before{content:"";}.bi-skip-start-circle::before{content:"";}.bi-skip-start-fill::before{content:"";}.bi-skip-start::before{content:"";}.bi-slack::before{content:"";}.bi-slash-circle-fill::before{content:"";}.bi-slash-circle::before{content:"";}.bi-slash-square-fill::before{content:"";}.bi-slash-square::before{content:"";}.bi-slash::before{content:"";}.bi-sliders::before{content:"";}.bi-smartwatch::before{content:"";}.bi-snow::before{content:"";}.bi-snow2::before{content:"";}.bi-snow3::before{content:"";}.bi-sort-alpha-down-alt::before{content:"";}.bi-sort-alpha-down::before{content:"";}.bi-sort-alpha-up-alt::before{content:"";}.bi-sort-alpha-up::before{content:"";}.bi-sort-down-alt::before{content:"";}.bi-sort-down::before{content:"";}.bi-sort-numeric-down-alt::before{content:"";}.bi-sort-numeric-down::before{content:"";}.bi-sort-numeric-up-alt::before{content:"";}.bi-sort-numeric-up::before{content:"";}.bi-sort-up-alt::before{content:"";}.bi-sort-up::before{content:"";}.bi-soundwave::before{content:"";}.bi-speaker-fill::before{content:"";}.bi-speaker::before{content:"";}.bi-speedometer::before{content:"";}.bi-speedometer2::before{content:"";}.bi-spellcheck::before{content:"";}.bi-square-fill::before{content:"";}.bi-square-half::before{content:"";}.bi-square::before{content:"";}.bi-stack::before{content:"";}.bi-star-fill::before{content:"";}.bi-star-half::before{content:"";}.bi-star::before{content:"";}.bi-stars::before{content:"";}.bi-stickies-fill::before{content:"";}.bi-stickies::before{content:"";}.bi-sticky-fill::before{content:"";}.bi-sticky::before{content:"";}.bi-stop-btn-fill::before{content:"";}.bi-stop-btn::before{content:"";}.bi-stop-circle-fill::before{content:"";}.bi-stop-circle::before{content:"";}.bi-stop-fill::before{content:"";}.bi-stop::before{content:"";}.bi-stoplights-fill::before{content:"";}.bi-stoplights::before{content:"";}.bi-stopwatch-fill::before{content:"";}.bi-stopwatch::before{content:"";}.bi-subtract::before{content:"";}.bi-suit-club-fill::before{content:"";}.bi-suit-club::before{content:"";}.bi-suit-diamond-fill::before{content:"";}.bi-suit-diamond::before{content:"";}.bi-suit-heart-fill::before{content:"";}.bi-suit-heart::before{content:"";}.bi-suit-spade-fill::before{content:"";}.bi-suit-spade::before{content:"";}.bi-sun-fill::before{content:"";}.bi-sun::before{content:"";}.bi-sunglasses::before{content:"";}.bi-sunrise-fill::before{content:"";}.bi-sunrise::before{content:"";}.bi-sunset-fill::before{content:"";}.bi-sunset::before{content:"";}.bi-symmetry-horizontal::before{content:"";}.bi-symmetry-vertical::before{content:"";}.bi-table::before{content:"";}.bi-tablet-fill::before{content:"";}.bi-tablet-landscape-fill::before{content:"";}.bi-tablet-landscape::before{content:"";}.bi-tablet::before{content:"";}.bi-tag-fill::before{content:"";}.bi-tag::before{content:"";}.bi-tags-fill::before{content:"";}.bi-tags::before{content:"";}.bi-telegram::before{content:"";}.bi-telephone-fill::before{content:"";}.bi-telephone-forward-fill::before{content:"";}.bi-telephone-forward::before{content:"";}.bi-telephone-inbound-fill::before{content:"";}.bi-telephone-inbound::before{content:"";}.bi-telephone-minus-fill::before{content:"";}.bi-telephone-minus::before{content:"";}.bi-telephone-outbound-fill::before{content:"";}.bi-telephone-outbound::before{content:"";}.bi-telephone-plus-fill::before{content:"";}.bi-telephone-plus::before{content:"";}.bi-telephone-x-fill::before{content:"";}.bi-telephone-x::before{content:"";}.bi-telephone::before{content:"";}.bi-terminal-fill::before{content:"";}.bi-terminal::before{content:"";}.bi-text-center::before{content:"";}.bi-text-indent-left::before{content:"";}.bi-text-indent-right::before{content:"";}.bi-text-left::before{content:"";}.bi-text-paragraph::before{content:"";}.bi-text-right::before{content:"";}.bi-textarea-resize::before{content:"";}.bi-textarea-t::before{content:"";}.bi-textarea::before{content:"";}.bi-thermometer-half::before{content:"";}.bi-thermometer-high::before{content:"";}.bi-thermometer-low::before{content:"";}.bi-thermometer-snow::before{content:"";}.bi-thermometer-sun::before{content:"";}.bi-thermometer::before{content:"";}.bi-three-dots-vertical::before{content:"";}.bi-three-dots::before{content:"";}.bi-toggle-off::before{content:"";}.bi-toggle-on::before{content:"";}.bi-toggle2-off::before{content:"";}.bi-toggle2-on::before{content:"";}.bi-toggles::before{content:"";}.bi-toggles2::before{content:"";}.bi-tools::before{content:"";}.bi-tornado::before{content:"";}.bi-trash-fill::before{content:"";}.bi-trash::before{content:"";}.bi-trash2-fill::before{content:"";}.bi-trash2::before{content:"";}.bi-tree-fill::before{content:"";}.bi-tree::before{content:"";}.bi-triangle-fill::before{content:"";}.bi-triangle-half::before{content:"";}.bi-triangle::before{content:"";}.bi-trophy-fill::before{content:"";}.bi-trophy::before{content:"";}.bi-tropical-storm::before{content:"";}.bi-truck-flatbed::before{content:"";}.bi-truck::before{content:"";}.bi-tsunami::before{content:"";}.bi-tv-fill::before{content:"";}.bi-tv::before{content:"";}.bi-twitch::before{content:"";}.bi-twitter::before{content:"";}.bi-type-bold::before{content:"";}.bi-type-h1::before{content:"";}.bi-type-h2::before{content:"";}.bi-type-h3::before{content:"";}.bi-type-italic::before{content:"";}.bi-type-strikethrough::before{content:"";}.bi-type-underline::before{content:"";}.bi-type::before{content:"";}.bi-ui-checks-grid::before{content:"";}.bi-ui-checks::before{content:"";}.bi-ui-radios-grid::before{content:"";}.bi-ui-radios::before{content:"";}.bi-umbrella-fill::before{content:"";}.bi-umbrella::before{content:"";}.bi-union::before{content:"";}.bi-unlock-fill::before{content:"";}.bi-unlock::before{content:"";}.bi-upc-scan::before{content:"";}.bi-upc::before{content:"";}.bi-upload::before{content:"";}.bi-vector-pen::before{content:"";}.bi-view-list::before{content:"";}.bi-view-stacked::before{content:"";}.bi-vinyl-fill::before{content:"";}.bi-vinyl::before{content:"";}.bi-voicemail::before{content:"";}.bi-volume-down-fill::before{content:"";}.bi-volume-down::before{content:"";}.bi-volume-mute-fill::before{content:"";}.bi-volume-mute::before{content:"";}.bi-volume-off-fill::before{content:"";}.bi-volume-off::before{content:"";}.bi-volume-up-fill::before{content:"";}.bi-volume-up::before{content:"";}.bi-vr::before{content:"";}.bi-wallet-fill::before{content:"";}.bi-wallet::before{content:"";}.bi-wallet2::before{content:"";}.bi-watch::before{content:"";}.bi-water::before{content:"";}.bi-whatsapp::before{content:"";}.bi-wifi-1::before{content:"";}.bi-wifi-2::before{content:"";}.bi-wifi-off::before{content:"";}.bi-wifi::before{content:"";}.bi-wind::before{content:"";}.bi-window-dock::before{content:"";}.bi-window-sidebar::before{content:"";}.bi-window::before{content:"";}.bi-wrench::before{content:"";}.bi-x-circle-fill::before{content:"";}.bi-x-circle::before{content:"";}.bi-x-diamond-fill::before{content:"";}.bi-x-diamond::before{content:"";}.bi-x-octagon-fill::before{content:"";}.bi-x-octagon::before{content:"";}.bi-x-square-fill::before{content:"";}.bi-x-square::before{content:"";}.bi-x::before{content:"";}.bi-youtube::before{content:"";}.bi-zoom-in::before{content:"";}.bi-zoom-out::before{content:"";}.bi-bank::before{content:"";}.bi-bank2::before{content:"";}.bi-bell-slash-fill::before{content:"";}.bi-bell-slash::before{content:"";}.bi-cash-coin::before{content:"";}.bi-check-lg::before{content:"";}.bi-coin::before{content:"";}.bi-currency-bitcoin::before{content:"";}.bi-currency-dollar::before{content:"";}.bi-currency-euro::before{content:"";}.bi-currency-exchange::before{content:"";}.bi-currency-pound::before{content:"";}.bi-currency-yen::before{content:"";}.bi-dash-lg::before{content:"";}.bi-exclamation-lg::before{content:"";}.bi-file-earmark-pdf-fill::before{content:"";}.bi-file-earmark-pdf::before{content:"";}.bi-file-pdf-fill::before{content:"";}.bi-file-pdf::before{content:"";}.bi-gender-ambiguous::before{content:"";}.bi-gender-female::before{content:"";}.bi-gender-male::before{content:"";}.bi-gender-trans::before{content:"";}.bi-headset-vr::before{content:"";}.bi-info-lg::before{content:"";}.bi-mastodon::before{content:"";}.bi-messenger::before{content:"";}.bi-piggy-bank-fill::before{content:"";}.bi-piggy-bank::before{content:"";}.bi-pin-map-fill::before{content:"";}.bi-pin-map::before{content:"";}.bi-plus-lg::before{content:"";}.bi-question-lg::before{content:"";}.bi-recycle::before{content:"";}.bi-reddit::before{content:"";}.bi-safe-fill::before{content:"";}.bi-safe2-fill::before{content:"";}.bi-safe2::before{content:"";}.bi-sd-card-fill::before{content:"";}.bi-sd-card::before{content:"";}.bi-skype::before{content:"";}.bi-slash-lg::before{content:"";}.bi-translate::before{content:"";}.bi-x-lg::before{content:"";}.bi-safe::before{content:"";}.bi-apple::before{content:"";}.bi-microsoft::before{content:"";}.bi-windows::before{content:"";}.bi-behance::before{content:"";}.bi-dribbble::before{content:"";}.bi-line::before{content:"";}.bi-medium::before{content:"";}.bi-paypal::before{content:"";}.bi-pinterest::before{content:"";}.bi-signal::before{content:"";}.bi-snapchat::before{content:"";}.bi-spotify::before{content:"";}.bi-stack-overflow::before{content:"";}.bi-strava::before{content:"";}.bi-wordpress::before{content:"";}.bi-vimeo::before{content:"";}.bi-activity::before{content:"";}.bi-easel2-fill::before{content:"";}.bi-easel2::before{content:"";}.bi-easel3-fill::before{content:"";}.bi-easel3::before{content:"";}.bi-fan::before{content:"";}.bi-fingerprint::before{content:"";}.bi-graph-down-arrow::before{content:"";}.bi-graph-up-arrow::before{content:"";}.bi-hypnotize::before{content:"";}.bi-magic::before{content:"";}.bi-person-rolodex::before{content:"";}.bi-person-video::before{content:"";}.bi-person-video2::before{content:"";}.bi-person-video3::before{content:"";}.bi-person-workspace::before{content:"";}.bi-radioactive::before{content:"";}.bi-webcam-fill::before{content:"";}.bi-webcam::before{content:"";}.bi-yin-yang::before{content:"";}.bi-bandaid-fill::before{content:"";}.bi-bandaid::before{content:"";}.bi-bluetooth::before{content:"";}.bi-body-text::before{content:"";}.bi-boombox::before{content:"";}.bi-boxes::before{content:"";}.bi-dpad-fill::before{content:"";}.bi-dpad::before{content:"";}.bi-ear-fill::before{content:"";}.bi-ear::before{content:"";}.bi-envelope-check-1::before{content:"";}.bi-envelope-check-fill::before{content:"";}.bi-envelope-check::before{content:"";}.bi-envelope-dash-1::before{content:"";}.bi-envelope-dash-fill::before{content:"";}.bi-envelope-dash::before{content:"";}.bi-envelope-exclamation-1::before{content:"";}.bi-envelope-exclamation-fill::before{content:"";}.bi-envelope-exclamation::before{content:"";}.bi-envelope-plus-fill::before{content:"";}.bi-envelope-plus::before{content:"";}.bi-envelope-slash-1::before{content:"";}.bi-envelope-slash-fill::before{content:"";}.bi-envelope-slash::before{content:"";}.bi-envelope-x-1::before{content:"";}.bi-envelope-x-fill::before{content:"";}.bi-envelope-x::before{content:"";}.bi-explicit-fill::before{content:"";}.bi-explicit::before{content:"";}.bi-git::before{content:"";}.bi-infinity::before{content:"";}.bi-list-columns-reverse::before{content:"";}.bi-list-columns::before{content:"";}.bi-meta::before{content:"";}.bi-mortorboard-fill::before{content:"";}.bi-mortorboard::before{content:"";}.bi-nintendo-switch::before{content:"";}.bi-pc-display-horizontal::before{content:"";}.bi-pc-display::before{content:"";}.bi-pc-horizontal::before{content:"";}.bi-pc::before{content:"";}.bi-playstation::before{content:"";}.bi-plus-slash-minus::before{content:"";}.bi-projector-fill::before{content:"";}.bi-projector::before{content:"";}.bi-qr-code-scan::before{content:"";}.bi-qr-code::before{content:"";}.bi-quora::before{content:"";}.bi-quote::before{content:"";}.bi-robot::before{content:"";}.bi-send-check-fill::before{content:"";}.bi-send-check::before{content:"";}.bi-send-dash-fill::before{content:"";}.bi-send-dash::before{content:"";}.bi-send-exclamation-1::before{content:"";}.bi-send-exclamation-fill::before{content:"";}.bi-send-exclamation::before{content:"";}.bi-send-fill::before{content:"";}.bi-send-plus-fill::before{content:"";}.bi-send-plus::before{content:"";}.bi-send-slash-fill::before{content:"";}.bi-send-slash::before{content:"";}.bi-send-x-fill::before{content:"";}.bi-send-x::before{content:"";}.bi-send::before{content:"";}.bi-steam::before{content:"";}.bi-terminal-dash-1::before{content:"";}.bi-terminal-dash::before{content:"";}.bi-terminal-plus::before{content:"";}.bi-terminal-split::before{content:"";}.bi-ticket-detailed-fill::before{content:"";}.bi-ticket-detailed::before{content:"";}.bi-ticket-fill::before{content:"";}.bi-ticket-perforated-fill::before{content:"";}.bi-ticket-perforated::before{content:"";}.bi-ticket::before{content:"";}.bi-tiktok::before{content:"";}.bi-window-dash::before{content:"";}.bi-window-desktop::before{content:"";}.bi-window-fullscreen::before{content:"";}.bi-window-plus::before{content:"";}.bi-window-split::before{content:"";}.bi-window-stack::before{content:"";}.bi-window-x::before{content:"";}.bi-xbox::before{content:"";}.bi-ethernet::before{content:"";}.bi-hdmi-fill::before{content:"";}.bi-hdmi::before{content:"";}.bi-usb-c-fill::before{content:"";}.bi-usb-c::before{content:"";}.bi-usb-fill::before{content:"";}.bi-usb-plug-fill::before{content:"";}.bi-usb-plug::before{content:"";}.bi-usb-symbol::before{content:"";}.bi-usb::before{content:"";}.bi-boombox-fill::before{content:"";}.bi-displayport-1::before{content:"";}.bi-displayport::before{content:"";}.bi-gpu-card::before{content:"";}.bi-memory::before{content:"";}.bi-modem-fill::before{content:"";}.bi-modem::before{content:"";}.bi-motherboard-fill::before{content:"";}.bi-motherboard::before{content:"";}.bi-optical-audio-fill::before{content:"";}.bi-optical-audio::before{content:"";}.bi-pci-card::before{content:"";}.bi-router-fill::before{content:"";}.bi-router::before{content:"";}.bi-ssd-fill::before{content:"";}.bi-ssd::before{content:"";}.bi-thunderbolt-fill::before{content:"";}.bi-thunderbolt::before{content:"";}.bi-usb-drive-fill::before{content:"";}.bi-usb-drive::before{content:"";}.bi-usb-micro-fill::before{content:"";}.bi-usb-micro::before{content:"";}.bi-usb-mini-fill::before{content:"";}.bi-usb-mini::before{content:"";}.bi-cloud-haze2::before{content:"";}.bi-device-hdd-fill::before{content:"";}.bi-device-hdd::before{content:"";}.bi-device-ssd-fill::before{content:"";}.bi-device-ssd::before{content:"";}.bi-displayport-fill::before{content:"";}.bi-mortarboard-fill::before{content:"";}.bi-mortarboard::before{content:"";}.bi-terminal-x::before{content:"";}.bi-arrow-through-heart-fill::before{content:"";}.bi-arrow-through-heart::before{content:"";}.bi-badge-sd-fill::before{content:"";}.bi-badge-sd::before{content:"";}.bi-bag-heart-fill::before{content:"";}.bi-bag-heart::before{content:"";}.bi-balloon-fill::before{content:"";}.bi-balloon-heart-fill::before{content:"";}.bi-balloon-heart::before{content:"";}.bi-balloon::before{content:"";}.bi-box2-fill::before{content:"";}.bi-box2-heart-fill::before{content:"";}.bi-box2-heart::before{content:"";}.bi-box2::before{content:"";}.bi-braces-asterisk::before{content:"";}.bi-calendar-heart-fill::before{content:"";}.bi-calendar-heart::before{content:"";}.bi-calendar2-heart-fill::before{content:"";}.bi-calendar2-heart::before{content:"";}.bi-chat-heart-fill::before{content:"";}.bi-chat-heart::before{content:"";}.bi-chat-left-heart-fill::before{content:"";}.bi-chat-left-heart::before{content:"";}.bi-chat-right-heart-fill::before{content:"";}.bi-chat-right-heart::before{content:"";}.bi-chat-square-heart-fill::before{content:"";}.bi-chat-square-heart::before{content:"";}.bi-clipboard-check-fill::before{content:"";}.bi-clipboard-data-fill::before{content:"";}.bi-clipboard-fill::before{content:"";}.bi-clipboard-heart-fill::before{content:"";}.bi-clipboard-heart::before{content:"";}.bi-clipboard-minus-fill::before{content:"";}.bi-clipboard-plus-fill::before{content:"";}.bi-clipboard-pulse::before{content:"";}.bi-clipboard-x-fill::before{content:"";}.bi-clipboard2-check-fill::before{content:"";}.bi-clipboard2-check::before{content:"";}.bi-clipboard2-data-fill::before{content:"";}.bi-clipboard2-data::before{content:"";}.bi-clipboard2-fill::before{content:"";}.bi-clipboard2-heart-fill::before{content:"";}.bi-clipboard2-heart::before{content:"";}.bi-clipboard2-minus-fill::before{content:"";}.bi-clipboard2-minus::before{content:"";}.bi-clipboard2-plus-fill::before{content:"";}.bi-clipboard2-plus::before{content:"";}.bi-clipboard2-pulse-fill::before{content:"";}.bi-clipboard2-pulse::before{content:"";}.bi-clipboard2-x-fill::before{content:"";}.bi-clipboard2-x::before{content:"";}.bi-clipboard2::before{content:"";}.bi-emoji-kiss-fill::before{content:"";}.bi-emoji-kiss::before{content:"";}.bi-envelope-heart-fill::before{content:"";}.bi-envelope-heart::before{content:"";}.bi-envelope-open-heart-fill::before{content:"";}.bi-envelope-open-heart::before{content:"";}.bi-envelope-paper-fill::before{content:"";}.bi-envelope-paper-heart-fill::before{content:"";}.bi-envelope-paper-heart::before{content:"";}.bi-envelope-paper::before{content:"";}.bi-filetype-aac::before{content:"";}.bi-filetype-ai::before{content:"";}.bi-filetype-bmp::before{content:"";}.bi-filetype-cs::before{content:"";}.bi-filetype-css::before{content:"";}.bi-filetype-csv::before{content:"";}.bi-filetype-doc::before{content:"";}.bi-filetype-docx::before{content:"";}.bi-filetype-exe::before{content:"";}.bi-filetype-gif::before{content:"";}.bi-filetype-heic::before{content:"";}.bi-filetype-html::before{content:"";}.bi-filetype-java::before{content:"";}.bi-filetype-jpg::before{content:"";}.bi-filetype-js::before{content:"";}.bi-filetype-jsx::before{content:"";}.bi-filetype-key::before{content:"";}.bi-filetype-m4p::before{content:"";}.bi-filetype-md::before{content:"";}.bi-filetype-mdx::before{content:"";}.bi-filetype-mov::before{content:"";}.bi-filetype-mp3::before{content:"";}.bi-filetype-mp4::before{content:"";}.bi-filetype-otf::before{content:"";}.bi-filetype-pdf::before{content:"";}.bi-filetype-php::before{content:"";}.bi-filetype-png::before{content:"";}.bi-filetype-ppt-1::before{content:"";}.bi-filetype-ppt::before{content:"";}.bi-filetype-psd::before{content:"";}.bi-filetype-py::before{content:"";}.bi-filetype-raw::before{content:"";}.bi-filetype-rb::before{content:"";}.bi-filetype-sass::before{content:"";}.bi-filetype-scss::before{content:"";}.bi-filetype-sh::before{content:"";}.bi-filetype-svg::before{content:"";}.bi-filetype-tiff::before{content:"";}.bi-filetype-tsx::before{content:"";}.bi-filetype-ttf::before{content:"";}.bi-filetype-txt::before{content:"";}.bi-filetype-wav::before{content:"";}.bi-filetype-woff::before{content:"";}.bi-filetype-xls-1::before{content:"";}.bi-filetype-xls::before{content:"";}.bi-filetype-xml::before{content:"";}.bi-filetype-yml::before{content:"";}.bi-heart-arrow::before{content:"";}.bi-heart-pulse-fill::before{content:"";}.bi-heart-pulse::before{content:"";}.bi-heartbreak-fill::before{content:"";}.bi-heartbreak::before{content:"";}.bi-hearts::before{content:"";}.bi-hospital-fill::before{content:"";}.bi-hospital::before{content:"";}.bi-house-heart-fill::before{content:"";}.bi-house-heart::before{content:"";}.bi-incognito::before{content:"";}.bi-magnet-fill::before{content:"";}.bi-magnet::before{content:"";}.bi-person-heart::before{content:"";}.bi-person-hearts::before{content:"";}.bi-phone-flip::before{content:"";}.bi-plugin::before{content:"";}.bi-postage-fill::before{content:"";}.bi-postage-heart-fill::before{content:"";}.bi-postage-heart::before{content:"";}.bi-postage::before{content:"";}.bi-postcard-fill::before{content:"";}.bi-postcard-heart-fill::before{content:"";}.bi-postcard-heart::before{content:"";}.bi-postcard::before{content:"";}.bi-search-heart-fill::before{content:"";}.bi-search-heart::before{content:"";}.bi-sliders2-vertical::before{content:"";}.bi-sliders2::before{content:"";}.bi-trash3-fill::before{content:"";}.bi-trash3::before{content:"";}.bi-valentine::before{content:"";}.bi-valentine2::before{content:"";}.bi-wrench-adjustable-circle-fill::before{content:"";}.bi-wrench-adjustable-circle::before{content:"";}.bi-wrench-adjustable::before{content:"";}.bi-filetype-json::before{content:"";}.bi-filetype-pptx::before{content:"";}.bi-filetype-xlsx::before{content:"";}.bi-1-circle-1::before{content:"";}.bi-1-circle-fill-1::before{content:"";}.bi-1-circle-fill::before{content:"";}.bi-1-circle::before{content:"";}.bi-1-square-fill::before{content:"";}.bi-1-square::before{content:"";}.bi-2-circle-1::before{content:"";}.bi-2-circle-fill-1::before{content:"";}.bi-2-circle-fill::before{content:"";}.bi-2-circle::before{content:"";}.bi-2-square-fill::before{content:"";}.bi-2-square::before{content:"";}.bi-3-circle-1::before{content:"";}.bi-3-circle-fill-1::before{content:"";}.bi-3-circle-fill::before{content:"";}.bi-3-circle::before{content:"";}.bi-3-square-fill::before{content:"";}.bi-3-square::before{content:"";}.bi-4-circle-1::before{content:"";}.bi-4-circle-fill-1::before{content:"";}.bi-4-circle-fill::before{content:"";}.bi-4-circle::before{content:"";}.bi-4-square-fill::before{content:"";}.bi-4-square::before{content:"";}.bi-5-circle-1::before{content:"";}.bi-5-circle-fill-1::before{content:"";}.bi-5-circle-fill::before{content:"";}.bi-5-circle::before{content:"";}.bi-5-square-fill::before{content:"";}.bi-5-square::before{content:"";}.bi-6-circle-1::before{content:"";}.bi-6-circle-fill-1::before{content:"";}.bi-6-circle-fill::before{content:"";}.bi-6-circle::before{content:"";}.bi-6-square-fill::before{content:"";}.bi-6-square::before{content:"";}.bi-7-circle-1::before{content:"";}.bi-7-circle-fill-1::before{content:"";}.bi-7-circle-fill::before{content:"";}.bi-7-circle::before{content:"";}.bi-7-square-fill::before{content:"";}.bi-7-square::before{content:"";}.bi-8-circle-1::before{content:"";}.bi-8-circle-fill-1::before{content:"";}.bi-8-circle-fill::before{content:"";}.bi-8-circle::before{content:"";}.bi-8-square-fill::before{content:"";}.bi-8-square::before{content:"";}.bi-9-circle-1::before{content:"";}.bi-9-circle-fill-1::before{content:"";}.bi-9-circle-fill::before{content:"";}.bi-9-circle::before{content:"";}.bi-9-square-fill::before{content:"";}.bi-9-square::before{content:"";}.bi-airplane-engines-fill::before{content:"";}.bi-airplane-engines::before{content:"";}.bi-airplane-fill::before{content:"";}.bi-airplane::before{content:"";}.bi-alexa::before{content:"";}.bi-alipay::before{content:"";}.bi-android::before{content:"";}.bi-android2::before{content:"";}.bi-box-fill::before{content:"";}.bi-box-seam-fill::before{content:"";}.bi-browser-chrome::before{content:"";}.bi-browser-edge::before{content:"";}.bi-browser-firefox::before{content:"";}.bi-browser-safari::before{content:"";}.bi-c-circle-1::before{content:"";}.bi-c-circle-fill-1::before{content:"";}.bi-c-circle-fill::before{content:"";}.bi-c-circle::before{content:"";}.bi-c-square-fill::before{content:"";}.bi-c-square::before{content:"";}.bi-capsule-pill::before{content:"";}.bi-capsule::before{content:"";}.bi-car-front-fill::before{content:"";}.bi-car-front::before{content:"";}.bi-cassette-fill::before{content:"";}.bi-cassette::before{content:"";}.bi-cc-circle-1::before{content:"";}.bi-cc-circle-fill-1::before{content:"";}.bi-cc-circle-fill::before{content:"";}.bi-cc-circle::before{content:"";}.bi-cc-square-fill::before{content:"";}.bi-cc-square::before{content:"";}.bi-cup-hot-fill::before{content:"";}.bi-cup-hot::before{content:"";}.bi-currency-rupee::before{content:"";}.bi-dropbox::before{content:"";}.bi-escape::before{content:"";}.bi-fast-forward-btn-fill::before{content:"";}.bi-fast-forward-btn::before{content:"";}.bi-fast-forward-circle-fill::before{content:"";}.bi-fast-forward-circle::before{content:"";}.bi-fast-forward-fill::before{content:"";}.bi-fast-forward::before{content:"";}.bi-filetype-sql::before{content:"";}.bi-fire::before{content:"";}.bi-google-play::before{content:"";}.bi-h-circle-1::before{content:"";}.bi-h-circle-fill-1::before{content:"";}.bi-h-circle-fill::before{content:"";}.bi-h-circle::before{content:"";}.bi-h-square-fill::before{content:"";}.bi-h-square::before{content:"";}.bi-indent::before{content:"";}.bi-lungs-fill::before{content:"";}.bi-lungs::before{content:"";}.bi-microsoft-teams::before{content:"";}.bi-p-circle-1::before{content:"";}.bi-p-circle-fill-1::before{content:"";}.bi-p-circle-fill::before{content:"";}.bi-p-circle::before{content:"";}.bi-p-square-fill::before{content:"";}.bi-p-square::before{content:"";}.bi-pass-fill::before{content:"";}.bi-pass::before{content:"";}.bi-prescription::before{content:"";}.bi-prescription2::before{content:"";}.bi-r-circle-1::before{content:"";}.bi-r-circle-fill-1::before{content:"";}.bi-r-circle-fill::before{content:"";}.bi-r-circle::before{content:"";}.bi-r-square-fill::before{content:"";}.bi-r-square::before{content:"";}.bi-repeat-1::before{content:"";}.bi-repeat::before{content:"";}.bi-rewind-btn-fill::before{content:"";}.bi-rewind-btn::before{content:"";}.bi-rewind-circle-fill::before{content:"";}.bi-rewind-circle::before{content:"";}.bi-rewind-fill::before{content:"";}.bi-rewind::before{content:"";}.bi-train-freight-front-fill::before{content:"";}.bi-train-freight-front::before{content:"";}.bi-train-front-fill::before{content:"";}.bi-train-front::before{content:"";}.bi-train-lightrail-front-fill::before{content:"";}.bi-train-lightrail-front::before{content:"";}.bi-truck-front-fill::before{content:"";}.bi-truck-front::before{content:"";}.bi-ubuntu::before{content:"";}.bi-unindent::before{content:"";}.bi-unity::before{content:"";}.bi-universal-access-circle::before{content:"";}.bi-universal-access::before{content:"";}.bi-virus::before{content:"";}.bi-virus2::before{content:"";}.bi-wechat::before{content:"";}.bi-yelp::before{content:"";}.bi-sign-stop-fill::before{content:"";}.bi-sign-stop-lights-fill::before{content:"";}.bi-sign-stop-lights::before{content:"";}.bi-sign-stop::before{content:"";}.bi-sign-turn-left-fill::before{content:"";}.bi-sign-turn-left::before{content:"";}.bi-sign-turn-right-fill::before{content:"";}.bi-sign-turn-right::before{content:"";}.bi-sign-turn-slight-left-fill::before{content:"";}.bi-sign-turn-slight-left::before{content:"";}.bi-sign-turn-slight-right-fill::before{content:"";}.bi-sign-turn-slight-right::before{content:"";}.bi-sign-yield-fill::before{content:"";}.bi-sign-yield::before{content:"";}.bi-ev-station-fill::before{content:"";}.bi-ev-station::before{content:"";}.bi-fuel-pump-diesel-fill::before{content:"";}.bi-fuel-pump-diesel::before{content:"";}.bi-fuel-pump-fill::before{content:"";}.bi-fuel-pump::before{content:"";} +@charset "UTF-8"; /*! -* Bootstrap v5.1.3 (https://getbootstrap.com/) -* Copyright 2011-2021 The Bootstrap Authors -* Copyright 2011-2021 Twitter, Inc. -* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) -*/ -:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#4276a7;--bs-secondary:#6c757d;--bs-success:#6eab63;--bs-info:#6da3b7;--bs-warning:#d5804c;--bs-danger:#c54c45;--bs-light:#f5faff;--bs-dark:#30475d;--bs-primary-rgb:66,118,167;--bs-secondary-rgb:108,117,125;--bs-success-rgb:110,171,99;--bs-info-rgb:109,163,183;--bs-warning-rgb:213,128,76;--bs-danger-rgb:197,76,69;--bs-light-rgb:245,250,255;--bs-dark-rgb:48,71,93;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:0,48,94;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg,rgba(255,255,255,.15),rgba(255,255,255,0));--bs-root-font-size:15px;--bs-body-font-family:Source Sans Pro,sans-serif;--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#00305e;--bs-body-bg:#fff;}*,*::before,*::after{box-sizing:border-box;}:root{font-size:var(--bs-root-font-size);}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth;}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25;}hr:not([size]){height:1px;}h6,.h6,h5,.h5,h4,.h4,h3,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;}h1,.h1{font-size:calc(1.375rem + 1.5vw);}@media(min-width:1200px){h1,.h1{font-size:2.5rem;}}h2,.h2{font-size:calc(1.325rem + .9vw);}@media(min-width:1200px){h2,.h2{font-size:2rem;}}h3,.h3{font-size:calc(1.3rem + .6vw);}@media(min-width:1200px){h3,.h3{font-size:1.75rem;}}h4,.h4{font-size:calc(1.275rem + .3vw);}@media(min-width:1200px){h4,.h4{font-size:1.5rem;}}h5,.h5{font-size:1.25rem;}h6,.h6{font-size:1rem;}p{margin-top:0;margin-bottom:1rem;}abbr[title],abbr[data-bs-original-title]{text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none;}address{margin-bottom:1rem;font-style:normal;line-height:inherit;}ol,ul{padding-left:2rem;}ol,ul,dl{margin-top:0;margin-bottom:1rem;}ol ol,ul ul,ol ul,ul ol{margin-bottom:0;}dt{font-weight:700;}dd{margin-bottom:.5rem;margin-left:0;}blockquote{margin:0 0 1rem;}b,strong{font-weight:bolder;}small,.small{font-size:.875em;}mark,.mark{padding:.2em;background-color:#fcf8e3;}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline;}sub{bottom:-.25em;}sup{top:-.5em;}a{color:#4276a7;text-decoration:underline;}a:hover{color:#355e86;}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none;}pre,code,kbd,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override;}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em;}pre code{font-size:inherit;color:inherit;word-break:normal;}code{font-size:.875em;color:#d63384;word-wrap:break-word;}a>code{color:inherit;}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem;}kbd kbd{padding:0;font-size:1em;font-weight:700;}figure{margin:0 0 1rem;}img,svg{vertical-align:middle;}table{caption-side:bottom;border-collapse:collapse;}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left;}th{text-align:inherit;text-align:-webkit-match-parent;}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0;}label{display:inline-block;}button{border-radius:0;}button:focus:not(:focus-visible){outline:0;}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit;}button,select{text-transform:none;}[role=button]{cursor:pointer;}select{word-wrap:normal;}select:disabled{opacity:1;}[list]::-webkit-calendar-picker-indicator{display:none;}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer;}::-moz-focus-inner{padding:0;border-style:none;}textarea{resize:vertical;}fieldset{min-width:0;padding:0;margin:0;border:0;}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit;}@media(min-width:1200px){legend{font-size:1.5rem;}}legend+*{clear:left;}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0;}::-webkit-inner-spin-button{height:auto;}[type=search]{outline-offset:-2px;-webkit-appearance:textfield;}::-webkit-search-decoration{-webkit-appearance:none;}::-webkit-color-swatch-wrapper{padding:0;}::file-selector-button{font:inherit;}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button;}output{display:inline-block;}iframe{border:0;}summary{display:list-item;cursor:pointer;}progress{vertical-align:baseline;}[hidden]{display:none!important;}.lead{font-size:1.25rem;font-weight:300;}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-1{font-size:5rem;}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-2{font-size:4.5rem;}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-3{font-size:4rem;}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-4{font-size:3.5rem;}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-5{font-size:3rem;}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-6{font-size:2.5rem;}}.list-unstyled{padding-left:0;list-style:none;}.list-inline{padding-left:0;list-style:none;}.list-inline-item{display:inline-block;}.list-inline-item:not(:last-child){margin-right:.5rem;}.initialism{font-size:.875em;text-transform:uppercase;}.blockquote{margin-bottom:1rem;font-size:1.25rem;}.blockquote>:last-child{margin-bottom:0;}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d;}.blockquote-footer::before{content:"— ";}.img-fluid{max-width:100%;height:auto;}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto;}.figure{display:inline-block;}.figure-img{margin-bottom:.5rem;line-height:1;}.figure-caption{font-size:.875em;color:#6c757d;}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{width:100%;padding-right:var(--bs-gutter-x,.75rem);padding-left:var(--bs-gutter-x,.75rem);margin-right:auto;margin-left:auto;}@media(min-width:576px){.container-sm,.container{max-width:540px;}}@media(min-width:768px){.container-md,.container-sm,.container{max-width:720px;}}@media(min-width:992px){.container-lg,.container-md,.container-sm,.container{max-width:960px;}}@media(min-width:1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px;}}@media(min-width:1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px;}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1*var(--bs-gutter-y));margin-right:calc(-.5*var(--bs-gutter-x));margin-left:calc(-.5*var(--bs-gutter-x));}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x)*.5);padding-left:calc(var(--bs-gutter-x)*.5);margin-top:var(--bs-gutter-y);}.col{flex:1 0 0%;}.row-cols-auto>*{flex:0 0 auto;width:auto;}.row-cols-1>*{flex:0 0 auto;width:100%;}.row-cols-2>*{flex:0 0 auto;width:50%;}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%;}.row-cols-4>*{flex:0 0 auto;width:25%;}.row-cols-5>*{flex:0 0 auto;width:20%;}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%;}.col-auto{flex:0 0 auto;width:auto;}.col-1{flex:0 0 auto;width:8.33333333%;}.col-2{flex:0 0 auto;width:16.66666667%;}.col-3{flex:0 0 auto;width:25%;}.col-4{flex:0 0 auto;width:33.33333333%;}.col-5{flex:0 0 auto;width:41.66666667%;}.col-6{flex:0 0 auto;width:50%;}.col-7{flex:0 0 auto;width:58.33333333%;}.col-8{flex:0 0 auto;width:66.66666667%;}.col-9{flex:0 0 auto;width:75%;}.col-10{flex:0 0 auto;width:83.33333333%;}.col-11{flex:0 0 auto;width:91.66666667%;}.col-12{flex:0 0 auto;width:100%;}.offset-1{margin-left:8.33333333%;}.offset-2{margin-left:16.66666667%;}.offset-3{margin-left:25%;}.offset-4{margin-left:33.33333333%;}.offset-5{margin-left:41.66666667%;}.offset-6{margin-left:50%;}.offset-7{margin-left:58.33333333%;}.offset-8{margin-left:66.66666667%;}.offset-9{margin-left:75%;}.offset-10{margin-left:83.33333333%;}.offset-11{margin-left:91.66666667%;}.g-0,.gx-0{--bs-gutter-x:0;}.g-0,.gy-0{--bs-gutter-y:0;}.g-1,.gx-1{--bs-gutter-x:.25rem;}.g-1,.gy-1{--bs-gutter-y:.25rem;}.g-2,.gx-2{--bs-gutter-x:.5rem;}.g-2,.gy-2{--bs-gutter-y:.5rem;}.g-3,.gx-3{--bs-gutter-x:1rem;}.g-3,.gy-3{--bs-gutter-y:1rem;}.g-4,.gx-4{--bs-gutter-x:1.5rem;}.g-4,.gy-4{--bs-gutter-y:1.5rem;}.g-5,.gx-5{--bs-gutter-x:3rem;}.g-5,.gy-5{--bs-gutter-y:3rem;}@media(min-width:576px){.col-sm{flex:1 0 0%;}.row-cols-sm-auto>*{flex:0 0 auto;width:auto;}.row-cols-sm-1>*{flex:0 0 auto;width:100%;}.row-cols-sm-2>*{flex:0 0 auto;width:50%;}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%;}.row-cols-sm-4>*{flex:0 0 auto;width:25%;}.row-cols-sm-5>*{flex:0 0 auto;width:20%;}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%;}.col-sm-auto{flex:0 0 auto;width:auto;}.col-sm-1{flex:0 0 auto;width:8.33333333%;}.col-sm-2{flex:0 0 auto;width:16.66666667%;}.col-sm-3{flex:0 0 auto;width:25%;}.col-sm-4{flex:0 0 auto;width:33.33333333%;}.col-sm-5{flex:0 0 auto;width:41.66666667%;}.col-sm-6{flex:0 0 auto;width:50%;}.col-sm-7{flex:0 0 auto;width:58.33333333%;}.col-sm-8{flex:0 0 auto;width:66.66666667%;}.col-sm-9{flex:0 0 auto;width:75%;}.col-sm-10{flex:0 0 auto;width:83.33333333%;}.col-sm-11{flex:0 0 auto;width:91.66666667%;}.col-sm-12{flex:0 0 auto;width:100%;}.offset-sm-0{margin-left:0;}.offset-sm-1{margin-left:8.33333333%;}.offset-sm-2{margin-left:16.66666667%;}.offset-sm-3{margin-left:25%;}.offset-sm-4{margin-left:33.33333333%;}.offset-sm-5{margin-left:41.66666667%;}.offset-sm-6{margin-left:50%;}.offset-sm-7{margin-left:58.33333333%;}.offset-sm-8{margin-left:66.66666667%;}.offset-sm-9{margin-left:75%;}.offset-sm-10{margin-left:83.33333333%;}.offset-sm-11{margin-left:91.66666667%;}.g-sm-0,.gx-sm-0{--bs-gutter-x:0;}.g-sm-0,.gy-sm-0{--bs-gutter-y:0;}.g-sm-1,.gx-sm-1{--bs-gutter-x:.25rem;}.g-sm-1,.gy-sm-1{--bs-gutter-y:.25rem;}.g-sm-2,.gx-sm-2{--bs-gutter-x:.5rem;}.g-sm-2,.gy-sm-2{--bs-gutter-y:.5rem;}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem;}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem;}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem;}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem;}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem;}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem;}}@media(min-width:768px){.col-md{flex:1 0 0%;}.row-cols-md-auto>*{flex:0 0 auto;width:auto;}.row-cols-md-1>*{flex:0 0 auto;width:100%;}.row-cols-md-2>*{flex:0 0 auto;width:50%;}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%;}.row-cols-md-4>*{flex:0 0 auto;width:25%;}.row-cols-md-5>*{flex:0 0 auto;width:20%;}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%;}.col-md-auto{flex:0 0 auto;width:auto;}.col-md-1{flex:0 0 auto;width:8.33333333%;}.col-md-2{flex:0 0 auto;width:16.66666667%;}.col-md-3{flex:0 0 auto;width:25%;}.col-md-4{flex:0 0 auto;width:33.33333333%;}.col-md-5{flex:0 0 auto;width:41.66666667%;}.col-md-6{flex:0 0 auto;width:50%;}.col-md-7{flex:0 0 auto;width:58.33333333%;}.col-md-8{flex:0 0 auto;width:66.66666667%;}.col-md-9{flex:0 0 auto;width:75%;}.col-md-10{flex:0 0 auto;width:83.33333333%;}.col-md-11{flex:0 0 auto;width:91.66666667%;}.col-md-12{flex:0 0 auto;width:100%;}.offset-md-0{margin-left:0;}.offset-md-1{margin-left:8.33333333%;}.offset-md-2{margin-left:16.66666667%;}.offset-md-3{margin-left:25%;}.offset-md-4{margin-left:33.33333333%;}.offset-md-5{margin-left:41.66666667%;}.offset-md-6{margin-left:50%;}.offset-md-7{margin-left:58.33333333%;}.offset-md-8{margin-left:66.66666667%;}.offset-md-9{margin-left:75%;}.offset-md-10{margin-left:83.33333333%;}.offset-md-11{margin-left:91.66666667%;}.g-md-0,.gx-md-0{--bs-gutter-x:0;}.g-md-0,.gy-md-0{--bs-gutter-y:0;}.g-md-1,.gx-md-1{--bs-gutter-x:.25rem;}.g-md-1,.gy-md-1{--bs-gutter-y:.25rem;}.g-md-2,.gx-md-2{--bs-gutter-x:.5rem;}.g-md-2,.gy-md-2{--bs-gutter-y:.5rem;}.g-md-3,.gx-md-3{--bs-gutter-x:1rem;}.g-md-3,.gy-md-3{--bs-gutter-y:1rem;}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem;}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem;}.g-md-5,.gx-md-5{--bs-gutter-x:3rem;}.g-md-5,.gy-md-5{--bs-gutter-y:3rem;}}@media(min-width:992px){.col-lg{flex:1 0 0%;}.row-cols-lg-auto>*{flex:0 0 auto;width:auto;}.row-cols-lg-1>*{flex:0 0 auto;width:100%;}.row-cols-lg-2>*{flex:0 0 auto;width:50%;}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%;}.row-cols-lg-4>*{flex:0 0 auto;width:25%;}.row-cols-lg-5>*{flex:0 0 auto;width:20%;}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%;}.col-lg-auto{flex:0 0 auto;width:auto;}.col-lg-1{flex:0 0 auto;width:8.33333333%;}.col-lg-2{flex:0 0 auto;width:16.66666667%;}.col-lg-3{flex:0 0 auto;width:25%;}.col-lg-4{flex:0 0 auto;width:33.33333333%;}.col-lg-5{flex:0 0 auto;width:41.66666667%;}.col-lg-6{flex:0 0 auto;width:50%;}.col-lg-7{flex:0 0 auto;width:58.33333333%;}.col-lg-8{flex:0 0 auto;width:66.66666667%;}.col-lg-9{flex:0 0 auto;width:75%;}.col-lg-10{flex:0 0 auto;width:83.33333333%;}.col-lg-11{flex:0 0 auto;width:91.66666667%;}.col-lg-12{flex:0 0 auto;width:100%;}.offset-lg-0{margin-left:0;}.offset-lg-1{margin-left:8.33333333%;}.offset-lg-2{margin-left:16.66666667%;}.offset-lg-3{margin-left:25%;}.offset-lg-4{margin-left:33.33333333%;}.offset-lg-5{margin-left:41.66666667%;}.offset-lg-6{margin-left:50%;}.offset-lg-7{margin-left:58.33333333%;}.offset-lg-8{margin-left:66.66666667%;}.offset-lg-9{margin-left:75%;}.offset-lg-10{margin-left:83.33333333%;}.offset-lg-11{margin-left:91.66666667%;}.g-lg-0,.gx-lg-0{--bs-gutter-x:0;}.g-lg-0,.gy-lg-0{--bs-gutter-y:0;}.g-lg-1,.gx-lg-1{--bs-gutter-x:.25rem;}.g-lg-1,.gy-lg-1{--bs-gutter-y:.25rem;}.g-lg-2,.gx-lg-2{--bs-gutter-x:.5rem;}.g-lg-2,.gy-lg-2{--bs-gutter-y:.5rem;}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem;}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem;}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem;}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem;}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem;}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem;}}@media(min-width:1200px){.col-xl{flex:1 0 0%;}.row-cols-xl-auto>*{flex:0 0 auto;width:auto;}.row-cols-xl-1>*{flex:0 0 auto;width:100%;}.row-cols-xl-2>*{flex:0 0 auto;width:50%;}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%;}.row-cols-xl-4>*{flex:0 0 auto;width:25%;}.row-cols-xl-5>*{flex:0 0 auto;width:20%;}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%;}.col-xl-auto{flex:0 0 auto;width:auto;}.col-xl-1{flex:0 0 auto;width:8.33333333%;}.col-xl-2{flex:0 0 auto;width:16.66666667%;}.col-xl-3{flex:0 0 auto;width:25%;}.col-xl-4{flex:0 0 auto;width:33.33333333%;}.col-xl-5{flex:0 0 auto;width:41.66666667%;}.col-xl-6{flex:0 0 auto;width:50%;}.col-xl-7{flex:0 0 auto;width:58.33333333%;}.col-xl-8{flex:0 0 auto;width:66.66666667%;}.col-xl-9{flex:0 0 auto;width:75%;}.col-xl-10{flex:0 0 auto;width:83.33333333%;}.col-xl-11{flex:0 0 auto;width:91.66666667%;}.col-xl-12{flex:0 0 auto;width:100%;}.offset-xl-0{margin-left:0;}.offset-xl-1{margin-left:8.33333333%;}.offset-xl-2{margin-left:16.66666667%;}.offset-xl-3{margin-left:25%;}.offset-xl-4{margin-left:33.33333333%;}.offset-xl-5{margin-left:41.66666667%;}.offset-xl-6{margin-left:50%;}.offset-xl-7{margin-left:58.33333333%;}.offset-xl-8{margin-left:66.66666667%;}.offset-xl-9{margin-left:75%;}.offset-xl-10{margin-left:83.33333333%;}.offset-xl-11{margin-left:91.66666667%;}.g-xl-0,.gx-xl-0{--bs-gutter-x:0;}.g-xl-0,.gy-xl-0{--bs-gutter-y:0;}.g-xl-1,.gx-xl-1{--bs-gutter-x:.25rem;}.g-xl-1,.gy-xl-1{--bs-gutter-y:.25rem;}.g-xl-2,.gx-xl-2{--bs-gutter-x:.5rem;}.g-xl-2,.gy-xl-2{--bs-gutter-y:.5rem;}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem;}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem;}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem;}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem;}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem;}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem;}}@media(min-width:1400px){.col-xxl{flex:1 0 0%;}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto;}.row-cols-xxl-1>*{flex:0 0 auto;width:100%;}.row-cols-xxl-2>*{flex:0 0 auto;width:50%;}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%;}.row-cols-xxl-4>*{flex:0 0 auto;width:25%;}.row-cols-xxl-5>*{flex:0 0 auto;width:20%;}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%;}.col-xxl-auto{flex:0 0 auto;width:auto;}.col-xxl-1{flex:0 0 auto;width:8.33333333%;}.col-xxl-2{flex:0 0 auto;width:16.66666667%;}.col-xxl-3{flex:0 0 auto;width:25%;}.col-xxl-4{flex:0 0 auto;width:33.33333333%;}.col-xxl-5{flex:0 0 auto;width:41.66666667%;}.col-xxl-6{flex:0 0 auto;width:50%;}.col-xxl-7{flex:0 0 auto;width:58.33333333%;}.col-xxl-8{flex:0 0 auto;width:66.66666667%;}.col-xxl-9{flex:0 0 auto;width:75%;}.col-xxl-10{flex:0 0 auto;width:83.33333333%;}.col-xxl-11{flex:0 0 auto;width:91.66666667%;}.col-xxl-12{flex:0 0 auto;width:100%;}.offset-xxl-0{margin-left:0;}.offset-xxl-1{margin-left:8.33333333%;}.offset-xxl-2{margin-left:16.66666667%;}.offset-xxl-3{margin-left:25%;}.offset-xxl-4{margin-left:33.33333333%;}.offset-xxl-5{margin-left:41.66666667%;}.offset-xxl-6{margin-left:50%;}.offset-xxl-7{margin-left:58.33333333%;}.offset-xxl-8{margin-left:66.66666667%;}.offset-xxl-9{margin-left:75%;}.offset-xxl-10{margin-left:83.33333333%;}.offset-xxl-11{margin-left:91.66666667%;}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0;}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0;}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:.25rem;}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:.25rem;}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:.5rem;}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:.5rem;}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem;}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem;}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem;}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem;}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem;}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem;}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#00305e;--bs-table-striped-bg:rgba(0,0,0,.05);--bs-table-active-color:#00305e;--bs-table-active-bg:rgba(0,0,0,.1);--bs-table-hover-color:#00305e;--bs-table-hover-bg:rgba(0,0,0,.075);width:100%;margin-bottom:1rem;color:#00305e;vertical-align:top;border-color:#dee2e6;}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg);}.table>tbody{vertical-align:inherit;}.table>thead{vertical-align:bottom;}.table>:not(:first-child){border-top:2px solid currentColor;}.caption-top{caption-side:top;}.table-sm>:not(caption)>*>*{padding:.25rem .25rem;}.table-bordered>:not(caption)>*{border-width:1px 0;}.table-bordered>:not(caption)>*>*{border-width:0 1px;}.table-borderless>:not(caption)>*>*{border-bottom-width:0;}.table-borderless>:not(:first-child){border-top-width:0;}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color);}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color);}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color);}.table-primary{--bs-table-bg:#d9e4ed;--bs-table-striped-bg:#ced9e1;--bs-table-striped-color:#000;--bs-table-active-bg:#c3cdd5;--bs-table-active-color:#000;--bs-table-hover-bg:#c9d3db;--bs-table-hover-color:#000;color:#000;border-color:#c3cdd5;}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce;}.table-success{--bs-table-bg:#e2eee0;--bs-table-striped-bg:#d7e2d5;--bs-table-striped-color:#000;--bs-table-active-bg:#cbd6ca;--bs-table-active-color:#000;--bs-table-hover-bg:#d1dccf;--bs-table-hover-color:#000;color:#000;border-color:#cbd6ca;}.table-info{--bs-table-bg:#e2edf1;--bs-table-striped-bg:#d7e1e5;--bs-table-striped-color:#000;--bs-table-active-bg:#cbd5d9;--bs-table-active-color:#000;--bs-table-hover-bg:#d1dbdf;--bs-table-hover-color:#000;color:#000;border-color:#cbd5d9;}.table-warning{--bs-table-bg:#f7e6db;--bs-table-striped-bg:#ebdbd0;--bs-table-striped-color:#000;--bs-table-active-bg:#decfc5;--bs-table-active-color:#000;--bs-table-hover-bg:#e4d5cb;--bs-table-hover-color:#000;color:#000;border-color:#decfc5;}.table-danger{--bs-table-bg:#f3dbda;--bs-table-striped-bg:#e7d0cf;--bs-table-striped-color:#000;--bs-table-active-bg:#dbc5c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e1cbca;--bs-table-hover-color:#000;color:#000;border-color:#dbc5c4;}.table-light{--bs-table-bg:#f5faff;--bs-table-striped-bg:#e9eef2;--bs-table-striped-color:#000;--bs-table-active-bg:#dde1e6;--bs-table-active-color:#000;--bs-table-hover-bg:#e3e7ec;--bs-table-hover-color:#000;color:#000;border-color:#dde1e6;}.table-dark{--bs-table-bg:#30475d;--bs-table-striped-bg:#3a5065;--bs-table-striped-color:#fff;--bs-table-active-bg:#45596d;--bs-table-active-color:#fff;--bs-table-hover-bg:#405569;--bs-table-hover-color:#fff;color:#fff;border-color:#45596d;}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch;}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch;}}.form-label{margin-bottom:.5rem;}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5;}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d;}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#00305e;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-control{transition:none;}}.form-control[type=file]{overflow:hidden;}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer;}.form-control:focus{color:#00305e;background-color:#fff;border-color:#a1bbd3;outline:0;box-shadow:0 0 0 .25rem rgba(66,118,167,.25);}.form-control::-webkit-date-and-time-value{height:1.5em;}.form-control::placeholder{color:#6c757d;opacity:1;}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1;}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;margin-inline-end:.75rem;color:#00305e;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none;}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3;}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;margin-inline-end:.75rem;color:#00305e;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{transition:none;}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3;}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#00305e;background-color:transparent;border:solid transparent;border-width:1px 0;}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0;}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem;}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;margin-inline-end:.5rem;}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;margin-inline-end:.5rem;}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem;}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;margin-inline-end:1rem;}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;margin-inline-end:1rem;}textarea.form-control{min-height:calc(1.5em + .75rem + 2px);}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px);}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px);}.form-control-color{width:3rem;height:auto;padding:.375rem;}.form-control-color:not(:disabled):not([readonly]){cursor:pointer;}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem;}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem;}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#00305e;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none;}@media(prefers-reduced-motion:reduce){.form-select{transition:none;}}.form-select:focus{border-color:#a1bbd3;outline:0;box-shadow:0 0 0 .25rem rgba(66,118,167,.25);}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none;}.form-select:disabled{background-color:#e9ecef;}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #00305e;}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:.2rem;}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:.3rem;}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem;}.form-check .form-check-input{float:left;margin-left:-1.5em;}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);appearance:none;color-adjust:exact;}.form-check-input[type=checkbox]{border-radius:.25em;}.form-check-input[type=radio]{border-radius:50%;}.form-check-input:active{filter:brightness(90%);}.form-check-input:focus{border-color:#a1bbd3;outline:0;box-shadow:0 0 0 .25rem rgba(66,118,167,.25);}.form-check-input:checked{background-color:#4276a7;border-color:#4276a7;}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");}.form-check-input[type=checkbox]:indeterminate{background-color:#4276a7;border-color:#4276a7;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5;}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{opacity:.5;}.form-switch{padding-left:2.5em;}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none;}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23a1bbd3'/%3e%3c/svg%3e");}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");}.form-check-inline{display:inline-block;margin-right:1rem;}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none;}.btn-check[disabled]+.btn,.btn-check:disabled+.btn{pointer-events:none;filter:none;opacity:.65;}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;appearance:none;}.form-range:focus{outline:0;}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(66,118,167,.25);}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(66,118,167,.25);}.form-range::-moz-focus-outer{border:0;}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#4276a7;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none;}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{transition:none;}}.form-range::-webkit-slider-thumb:active{background-color:#c6d6e5;}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem;}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#4276a7;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none;}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{transition:none;}}.form-range::-moz-range-thumb:active{background-color:#c6d6e5;}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem;}.form-range:disabled{pointer-events:none;}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd;}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd;}.form-floating{position:relative;}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25;}.form-floating>label{position:absolute;top:0;left:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none;}}.form-floating>.form-control{padding:1rem .75rem;}.form-floating>.form-control::placeholder{color:transparent;}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem;}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem;}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem);}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem);}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%;}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0;}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3;}.input-group .btn{position:relative;z-index:2;}.input-group .btn:focus{z-index:3;}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#00305e;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem;}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem;}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem;}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem;}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3){border-top-right-radius:0;border-bottom-right-radius:0;}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4){border-top-right-radius:0;border-bottom-right-radius:0;}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0;}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#6eab63;}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#000;background-color:rgba(110,171,99,.9);border-radius:.25rem;}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block;}.was-validated .form-control:valid,.form-control.is-valid{border-color:#6eab63;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%236EAB63' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:#6eab63;box-shadow:0 0 0 .25rem rgba(110,171,99,.25);}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem);}.was-validated .form-select:valid,.form-select.is-valid{border-color:#6eab63;}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%236EAB63' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:#6eab63;box-shadow:0 0 0 .25rem rgba(110,171,99,.25);}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:#6eab63;}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:#6eab63;}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(110,171,99,.25);}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:#6eab63;}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em;}.was-validated .input-group .form-control:valid,.input-group .form-control.is-valid,.was-validated .input-group .form-select:valid,.input-group .form-select.is-valid{z-index:1;}.was-validated .input-group .form-control:valid:focus,.input-group .form-control.is-valid:focus,.was-validated .input-group .form-select:valid:focus,.input-group .form-select.is-valid:focus{z-index:3;}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#c54c45;}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(197,76,69,.9);border-radius:.25rem;}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block;}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:#c54c45;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23C54C45'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23C54C45' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:#c54c45;box-shadow:0 0 0 .25rem rgba(197,76,69,.25);}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem);}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:#c54c45;}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23C54C45'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23C54C45' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:#c54c45;box-shadow:0 0 0 .25rem rgba(197,76,69,.25);}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:#c54c45;}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:#c54c45;}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(197,76,69,.25);}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:#c54c45;}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em;}.was-validated .input-group .form-control:invalid,.input-group .form-control.is-invalid,.was-validated .input-group .form-select:invalid,.input-group .form-select.is-invalid{z-index:2;}.was-validated .input-group .form-control:invalid:focus,.input-group .form-control.is-invalid:focus,.was-validated .input-group .form-select:invalid:focus,.input-group .form-select.is-invalid:focus{z-index:3;}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#00305e;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.btn{transition:none;}}.btn:hover{color:#00305e;}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(66,118,167,.25);}.btn:disabled,.btn.disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65;}.btn-primary{color:#fff;background-color:#4276a7;border-color:#4276a7;}.btn-primary:hover{color:#fff;background-color:#38648e;border-color:#355e86;}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#38648e;border-color:#355e86;box-shadow:0 0 0 .25rem rgba(94,139,180,.5);}.btn-check:checked+.btn-primary,.btn-check:active+.btn-primary,.btn-primary:active,.btn-primary.active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#355e86;border-color:#32597d;}.btn-check:checked+.btn-primary:focus,.btn-check:active+.btn-primary:focus,.btn-primary:active:focus,.btn-primary.active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(94,139,180,.5);}.btn-primary:disabled,.btn-primary.disabled{color:#fff;background-color:#4276a7;border-color:#4276a7;}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d;}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64;}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5);}.btn-check:checked+.btn-secondary,.btn-check:active+.btn-secondary,.btn-secondary:active,.btn-secondary.active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e;}.btn-check:checked+.btn-secondary:focus,.btn-check:active+.btn-secondary:focus,.btn-secondary:active:focus,.btn-secondary.active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5);}.btn-secondary:disabled,.btn-secondary.disabled{color:#fff;background-color:#6c757d;border-color:#6c757d;}.btn-success{color:#000;background-color:#6eab63;border-color:#6eab63;}.btn-success:hover{color:#000;background-color:#84b87a;border-color:#7db373;}.btn-check:focus+.btn-success,.btn-success:focus{color:#000;background-color:#84b87a;border-color:#7db373;box-shadow:0 0 0 .25rem rgba(94,145,84,.5);}.btn-check:checked+.btn-success,.btn-check:active+.btn-success,.btn-success:active,.btn-success.active,.show>.btn-success.dropdown-toggle{color:#000;background-color:#8bbc82;border-color:#7db373;}.btn-check:checked+.btn-success:focus,.btn-check:active+.btn-success:focus,.btn-success:active:focus,.btn-success.active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(94,145,84,.5);}.btn-success:disabled,.btn-success.disabled{color:#000;background-color:#6eab63;border-color:#6eab63;}.btn-info{color:#000;background-color:#6da3b7;border-color:#6da3b7;}.btn-info:hover{color:#000;background-color:#83b1c2;border-color:#7cacbe;}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#83b1c2;border-color:#7cacbe;box-shadow:0 0 0 .25rem rgba(93,139,156,.5);}.btn-check:checked+.btn-info,.btn-check:active+.btn-info,.btn-info:active,.btn-info.active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#8ab5c5;border-color:#7cacbe;}.btn-check:checked+.btn-info:focus,.btn-check:active+.btn-info:focus,.btn-info:active:focus,.btn-info.active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(93,139,156,.5);}.btn-info:disabled,.btn-info.disabled{color:#000;background-color:#6da3b7;border-color:#6da3b7;}.btn-warning{color:#000;background-color:#d5804c;border-color:#d5804c;}.btn-warning:hover{color:#000;background-color:#db9367;border-color:#d98d5e;}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#db9367;border-color:#d98d5e;box-shadow:0 0 0 .25rem rgba(181,109,65,.5);}.btn-check:checked+.btn-warning,.btn-check:active+.btn-warning,.btn-warning:active,.btn-warning.active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#dd9970;border-color:#d98d5e;}.btn-check:checked+.btn-warning:focus,.btn-check:active+.btn-warning:focus,.btn-warning:active:focus,.btn-warning.active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(181,109,65,.5);}.btn-warning:disabled,.btn-warning.disabled{color:#000;background-color:#d5804c;border-color:#d5804c;}.btn-danger{color:#fff;background-color:#c54c45;border-color:#c54c45;}.btn-danger:hover{color:#fff;background-color:#a7413b;border-color:#9e3d37;}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#a7413b;border-color:#9e3d37;box-shadow:0 0 0 .25rem rgba(206,103,97,.5);}.btn-check:checked+.btn-danger,.btn-check:active+.btn-danger,.btn-danger:active,.btn-danger.active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#9e3d37;border-color:#943934;}.btn-check:checked+.btn-danger:focus,.btn-check:active+.btn-danger:focus,.btn-danger:active:focus,.btn-danger.active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(206,103,97,.5);}.btn-danger:disabled,.btn-danger.disabled{color:#fff;background-color:#c54c45;border-color:#c54c45;}.btn-light{color:#000;background-color:#f5faff;border-color:#f5faff;}.btn-light:hover{color:#000;background-color:#f7fbff;border-color:#f6fbff;}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f7fbff;border-color:#f6fbff;box-shadow:0 0 0 .25rem rgba(208,213,217,.5);}.btn-check:checked+.btn-light,.btn-check:active+.btn-light,.btn-light:active,.btn-light.active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f7fbff;border-color:#f6fbff;}.btn-check:checked+.btn-light:focus,.btn-check:active+.btn-light:focus,.btn-light:active:focus,.btn-light.active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(208,213,217,.5);}.btn-light:disabled,.btn-light.disabled{color:#000;background-color:#f5faff;border-color:#f5faff;}.btn-dark{color:#fff;background-color:#30475d;border-color:#30475d;}.btn-dark:hover{color:#fff;background-color:#293c4f;border-color:#26394a;}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#293c4f;border-color:#26394a;box-shadow:0 0 0 .25rem rgba(79,99,117,.5);}.btn-check:checked+.btn-dark,.btn-check:active+.btn-dark,.btn-dark:active,.btn-dark.active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#26394a;border-color:#243546;}.btn-check:checked+.btn-dark:focus,.btn-check:active+.btn-dark:focus,.btn-dark:active:focus,.btn-dark.active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(79,99,117,.5);}.btn-dark:disabled,.btn-dark.disabled{color:#fff;background-color:#30475d;border-color:#30475d;}.btn-outline-primary{color:#4276a7;border-color:#4276a7;}.btn-outline-primary:hover{color:#fff;background-color:#4276a7;border-color:#4276a7;}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(66,118,167,.5);}.btn-check:checked+.btn-outline-primary,.btn-check:active+.btn-outline-primary,.btn-outline-primary:active,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show{color:#fff;background-color:#4276a7;border-color:#4276a7;}.btn-check:checked+.btn-outline-primary:focus,.btn-check:active+.btn-outline-primary:focus,.btn-outline-primary:active:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(66,118,167,.5);}.btn-outline-primary:disabled,.btn-outline-primary.disabled{color:#4276a7;background-color:transparent;}.btn-outline-secondary{color:#6c757d;border-color:#6c757d;}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d;}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5);}.btn-check:checked+.btn-outline-secondary,.btn-check:active+.btn-outline-secondary,.btn-outline-secondary:active,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show{color:#fff;background-color:#6c757d;border-color:#6c757d;}.btn-check:checked+.btn-outline-secondary:focus,.btn-check:active+.btn-outline-secondary:focus,.btn-outline-secondary:active:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5);}.btn-outline-secondary:disabled,.btn-outline-secondary.disabled{color:#6c757d;background-color:transparent;}.btn-outline-success{color:#6eab63;border-color:#6eab63;}.btn-outline-success:hover{color:#000;background-color:#6eab63;border-color:#6eab63;}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(110,171,99,.5);}.btn-check:checked+.btn-outline-success,.btn-check:active+.btn-outline-success,.btn-outline-success:active,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show{color:#000;background-color:#6eab63;border-color:#6eab63;}.btn-check:checked+.btn-outline-success:focus,.btn-check:active+.btn-outline-success:focus,.btn-outline-success:active:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(110,171,99,.5);}.btn-outline-success:disabled,.btn-outline-success.disabled{color:#6eab63;background-color:transparent;}.btn-outline-info{color:#6da3b7;border-color:#6da3b7;}.btn-outline-info:hover{color:#000;background-color:#6da3b7;border-color:#6da3b7;}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(109,163,183,.5);}.btn-check:checked+.btn-outline-info,.btn-check:active+.btn-outline-info,.btn-outline-info:active,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show{color:#000;background-color:#6da3b7;border-color:#6da3b7;}.btn-check:checked+.btn-outline-info:focus,.btn-check:active+.btn-outline-info:focus,.btn-outline-info:active:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(109,163,183,.5);}.btn-outline-info:disabled,.btn-outline-info.disabled{color:#6da3b7;background-color:transparent;}.btn-outline-warning{color:#d5804c;border-color:#d5804c;}.btn-outline-warning:hover{color:#000;background-color:#d5804c;border-color:#d5804c;}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(213,128,76,.5);}.btn-check:checked+.btn-outline-warning,.btn-check:active+.btn-outline-warning,.btn-outline-warning:active,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show{color:#000;background-color:#d5804c;border-color:#d5804c;}.btn-check:checked+.btn-outline-warning:focus,.btn-check:active+.btn-outline-warning:focus,.btn-outline-warning:active:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(213,128,76,.5);}.btn-outline-warning:disabled,.btn-outline-warning.disabled{color:#d5804c;background-color:transparent;}.btn-outline-danger{color:#c54c45;border-color:#c54c45;}.btn-outline-danger:hover{color:#fff;background-color:#c54c45;border-color:#c54c45;}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(197,76,69,.5);}.btn-check:checked+.btn-outline-danger,.btn-check:active+.btn-outline-danger,.btn-outline-danger:active,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show{color:#fff;background-color:#c54c45;border-color:#c54c45;}.btn-check:checked+.btn-outline-danger:focus,.btn-check:active+.btn-outline-danger:focus,.btn-outline-danger:active:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(197,76,69,.5);}.btn-outline-danger:disabled,.btn-outline-danger.disabled{color:#c54c45;background-color:transparent;}.btn-outline-light{color:#f5faff;border-color:#f5faff;}.btn-outline-light:hover{color:#000;background-color:#f5faff;border-color:#f5faff;}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(245,250,255,.5);}.btn-check:checked+.btn-outline-light,.btn-check:active+.btn-outline-light,.btn-outline-light:active,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show{color:#000;background-color:#f5faff;border-color:#f5faff;}.btn-check:checked+.btn-outline-light:focus,.btn-check:active+.btn-outline-light:focus,.btn-outline-light:active:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(245,250,255,.5);}.btn-outline-light:disabled,.btn-outline-light.disabled{color:#f5faff;background-color:transparent;}.btn-outline-dark{color:#30475d;border-color:#30475d;}.btn-outline-dark:hover{color:#fff;background-color:#30475d;border-color:#30475d;}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(48,71,93,.5);}.btn-check:checked+.btn-outline-dark,.btn-check:active+.btn-outline-dark,.btn-outline-dark:active,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show{color:#fff;background-color:#30475d;border-color:#30475d;}.btn-check:checked+.btn-outline-dark:focus,.btn-check:active+.btn-outline-dark:focus,.btn-outline-dark:active:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus{box-shadow:0 0 0 .25rem rgba(48,71,93,.5);}.btn-outline-dark:disabled,.btn-outline-dark.disabled{color:#30475d;background-color:transparent;}.btn-link{font-weight:400;color:#4276a7;text-decoration:underline;}.btn-link:hover{color:#355e86;}.btn-link:disabled,.btn-link.disabled{color:#6c757d;}.btn-lg,.btn-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem;}.btn-sm,.btn-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem;}.fade{transition:opacity .15s linear;}@media(prefers-reduced-motion:reduce){.fade{transition:none;}}.fade:not(.show){opacity:0;}.collapse:not(.show){display:none;}.collapsing{height:0;overflow:hidden;transition:height .35s ease;}@media(prefers-reduced-motion:reduce){.collapsing{transition:none;}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease;}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none;}}.dropup,.dropend,.dropdown,.dropstart{position:relative;}.dropdown-toggle{white-space:nowrap;}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent;}.dropdown-toggle:empty::after{margin-left:0;}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#00305e;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem;}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:.125rem;}.dropdown-menu-start{--bs-position:start;}.dropdown-menu-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-end{--bs-position:end;}.dropdown-menu-end[data-bs-popper]{right:0;left:auto;}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position:start;}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-sm-end{--bs-position:end;}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position:start;}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-md-end{--bs-position:end;}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position:start;}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-lg-end{--bs-position:end;}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position:start;}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-xl-end{--bs-position:end;}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start;}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-xxl-end{--bs-position:end;}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto;}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem;}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent;}.dropup .dropdown-toggle:empty::after{margin-left:0;}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem;}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid;}.dropend .dropdown-toggle:empty::after{margin-left:0;}.dropend .dropdown-toggle::after{vertical-align:0;}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem;}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";}.dropstart .dropdown-toggle::after{display:none;}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent;}.dropstart .dropdown-toggle:empty::after{margin-left:0;}.dropstart .dropdown-toggle::before{vertical-align:0;}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15);}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;}.dropdown-item:hover,.dropdown-item:focus{color:#1e2125;background-color:#e9ecef;}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#4276a7;}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent;}.dropdown-menu.show{display:block;}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap;}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529;}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15);}.dropdown-menu-dark .dropdown-item{color:#dee2e6;}.dropdown-menu-dark .dropdown-item:hover,.dropdown-menu-dark .dropdown-item:focus{color:#fff;background-color:rgba(255,255,255,.15);}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#4276a7;}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd;}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15);}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6;}.dropdown-menu-dark .dropdown-header{color:#adb5bd;}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle;}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto;}.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1;}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start;}.btn-toolbar .input-group{width:auto;}.btn-group>.btn:not(:first-child),.btn-group>.btn-group:not(:first-child){margin-left:-1px;}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0;}.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0;}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem;}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after{margin-left:0;}.dropstart .dropdown-toggle-split::before{margin-right:0;}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem;}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem;}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center;}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%;}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:-1px;}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0;}.btn-group-vertical>.btn~.btn,.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0;}.nav{display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none;}.nav-link{display:block;padding:.5rem 1rem;color:#4276a7;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.nav-link{transition:none;}}.nav-link:hover,.nav-link:focus{color:#355e86;}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default;}.nav-tabs{border-bottom:1px solid #dee2e6;}.nav-tabs .nav-link{margin-bottom:-1px;background:none;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem;}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate;}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent;}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff;}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0;}.nav-pills .nav-link{background:none;border:0;border-radius:.25rem;}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#4276a7;}.nav-fill>.nav-link,.nav-fill .nav-item{flex:1 1 auto;text-align:center;}.nav-justified>.nav-link,.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center;}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%;}.tab-content>.tab-pane{display:none;}.tab-content>.active{display:block;}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem;}.navbar>.container,.navbar>.container-fluid,.navbar>.container-sm,.navbar>.container-md,.navbar>.container-lg,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between;}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap;}.navbar-nav{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none;}.navbar-nav .nav-link{padding-right:0;padding-left:0;}.navbar-nav .dropdown-menu{position:static;}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center;}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none;}}.navbar-toggler:hover{text-decoration:none;}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem;}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%;}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto;}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-sm .navbar-nav{flex-direction:row;}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem;}.navbar-expand-sm .navbar-nav-scroll{overflow:visible;}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-sm .navbar-toggler{display:none;}.navbar-expand-sm .offcanvas-header{display:none;}.navbar-expand-sm .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none;}.navbar-expand-sm .offcanvas-top,.navbar-expand-sm .offcanvas-bottom{height:auto;border-top:0;border-bottom:0;}.navbar-expand-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-md .navbar-nav{flex-direction:row;}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem;}.navbar-expand-md .navbar-nav-scroll{overflow:visible;}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-md .navbar-toggler{display:none;}.navbar-expand-md .offcanvas-header{display:none;}.navbar-expand-md .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none;}.navbar-expand-md .offcanvas-top,.navbar-expand-md .offcanvas-bottom{height:auto;border-top:0;border-bottom:0;}.navbar-expand-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-lg .navbar-nav{flex-direction:row;}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem;}.navbar-expand-lg .navbar-nav-scroll{overflow:visible;}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-lg .navbar-toggler{display:none;}.navbar-expand-lg .offcanvas-header{display:none;}.navbar-expand-lg .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none;}.navbar-expand-lg .offcanvas-top,.navbar-expand-lg .offcanvas-bottom{height:auto;border-top:0;border-bottom:0;}.navbar-expand-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-xl .navbar-nav{flex-direction:row;}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem;}.navbar-expand-xl .navbar-nav-scroll{overflow:visible;}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-xl .navbar-toggler{display:none;}.navbar-expand-xl .offcanvas-header{display:none;}.navbar-expand-xl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none;}.navbar-expand-xl .offcanvas-top,.navbar-expand-xl .offcanvas-bottom{height:auto;border-top:0;border-bottom:0;}.navbar-expand-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-xxl .navbar-nav{flex-direction:row;}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem;}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible;}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-xxl .navbar-toggler{display:none;}.navbar-expand-xxl .offcanvas-header{display:none;}.navbar-expand-xxl .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none;}.navbar-expand-xxl .offcanvas-top,.navbar-expand-xxl .offcanvas-bottom{height:auto;border-top:0;border-bottom:0;}.navbar-expand-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand .navbar-nav{flex-direction:row;}.navbar-expand .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem;}.navbar-expand .navbar-nav-scroll{overflow:visible;}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand .navbar-toggler{display:none;}.navbar-expand .offcanvas-header{display:none;}.navbar-expand .offcanvas{position:inherit;bottom:0;z-index:1000;flex-grow:1;visibility:visible!important;background-color:transparent;border-right:0;border-left:0;transition:none;transform:none;}.navbar-expand .offcanvas-top,.navbar-expand .offcanvas-bottom{height:auto;border-top:0;border-bottom:0;}.navbar-expand .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}.navbar-light .navbar-brand{color:rgba(0,0,0,.9);}.navbar-light .navbar-brand:hover,.navbar-light .navbar-brand:focus{color:rgba(0,0,0,.9);}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55);}.navbar-light .navbar-nav .nav-link:hover,.navbar-light .navbar-nav .nav-link:focus{color:rgba(0,0,0,.7);}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3);}.navbar-light .navbar-nav .show>.nav-link,.navbar-light .navbar-nav .nav-link.active{color:rgba(0,0,0,.9);}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1);}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");}.navbar-light .navbar-text{color:rgba(0,0,0,.55);}.navbar-light .navbar-text a,.navbar-light .navbar-text a:hover,.navbar-light .navbar-text a:focus{color:rgba(0,0,0,.9);}.navbar-dark .navbar-brand{color:#fff;}.navbar-dark .navbar-brand:hover,.navbar-dark .navbar-brand:focus{color:#fff;}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55);}.navbar-dark .navbar-nav .nav-link:hover,.navbar-dark .navbar-nav .nav-link:focus{color:rgba(255,255,255,.75);}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25);}.navbar-dark .navbar-nav .show>.nav-link,.navbar-dark .navbar-nav .nav-link.active{color:#fff;}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1);}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");}.navbar-dark .navbar-text{color:rgba(255,255,255,.55);}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:hover,.navbar-dark .navbar-text a:focus{color:#fff;}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem;}.card>hr{margin-right:0;margin-left:0;}.card>.list-group{border-top:inherit;border-bottom:inherit;}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px);}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px);}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0;}.card-body{flex:1 1 auto;padding:1rem 1rem;}.card-title{margin-bottom:.5rem;}.card-subtitle{margin-top:-.25rem;margin-bottom:0;}.card-text:last-child{margin-bottom:0;}.card-link+.card-link{margin-left:1rem;}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125);}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0;}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125);}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px);}.card-header-tabs{margin-right:-.5rem;margin-bottom:-.5rem;margin-left:-.5rem;border-bottom:0;}.card-header-pills{margin-right:-.5rem;margin-left:-.5rem;}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1rem;border-radius:calc(.25rem - 1px);}.card-img,.card-img-top,.card-img-bottom{width:100%;}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px);}.card-img,.card-img-bottom{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px);}.card-group>.card{margin-bottom:.75rem;}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap;}.card-group>.card{flex:1 0 0%;margin-bottom:0;}.card-group>.card+.card{margin-left:0;border-left:0;}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0;}.card-group>.card:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header{border-top-right-radius:0;}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer{border-bottom-right-radius:0;}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0;}.card-group>.card:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header{border-top-left-radius:0;}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer{border-bottom-left-radius:0;}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#00305e;text-align:left;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease;}@media(prefers-reduced-motion:reduce){.accordion-button{transition:none;}}.accordion-button:not(.collapsed){color:#3b6a96;background-color:#ecf1f6;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125);}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%233b6a96'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(-180deg);}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-left:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%2300305E'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out;}@media(prefers-reduced-motion:reduce){.accordion-button::after{transition:none;}}.accordion-button:hover{z-index:2;}.accordion-button:focus{z-index:3;border-color:#a1bbd3;outline:0;box-shadow:0 0 0 .25rem rgba(66,118,167,.25);}.accordion-header{margin-bottom:0;}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125);}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem;}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px);}.accordion-item:not(:first-of-type){border-top:0;}.accordion-item:last-of-type{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem;}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px);}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem;}.accordion-body{padding:1rem 1.25rem;}.accordion-flush .accordion-collapse{border-width:0;}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0;}.accordion-flush .accordion-item:first-child{border-top:0;}.accordion-flush .accordion-item:last-child{border-bottom:0;}.accordion-flush .accordion-item .accordion-button{border-radius:0;}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none;}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem;}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider,"/");}.breadcrumb-item.active{color:#6c757d;}.pagination{display:flex;padding-left:0;list-style:none;}.page-link{position:relative;display:block;color:#4276a7;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.page-link{transition:none;}}.page-link:hover{z-index:2;color:#355e86;background-color:#e9ecef;border-color:#dee2e6;}.page-link:focus{z-index:3;color:#355e86;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(66,118,167,.25);}.page-item:not(:first-child) .page-link{margin-left:-1px;}.page-item.active .page-link{z-index:3;color:#fff;background-color:#4276a7;border-color:#4276a7;}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6;}.page-link{padding:.375rem .75rem;}.page-item:first-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem;}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem;}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem;}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem;}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;}.badge:empty{display:none;}.btn .badge{position:relative;top:-1px;}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem;}.alert-heading{color:inherit;}.alert-link{font-weight:700;}.alert-dismissible{padding-right:3rem;}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem;}.alert-primary{color:#284764;background-color:#d9e4ed;border-color:#c6d6e5;}.alert-primary .alert-link{color:#203950;}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8;}.alert-secondary .alert-link{color:#34383c;}.alert-success{color:#42673b;background-color:#e2eee0;border-color:#d4e6d0;}.alert-success .alert-link{color:#35522f;}.alert-info{color:#41626e;background-color:#e2edf1;border-color:#d3e3e9;}.alert-info .alert-link{color:#344e58;}.alert-warning{color:#804d2e;background-color:#f7e6db;border-color:#f2d9c9;}.alert-warning .alert-link{color:#663e25;}.alert-danger{color:#762e29;background-color:#f3dbda;border-color:#eec9c7;}.alert-danger .alert-link{color:#5e2521;}.alert-light{color:#626466;background-color:#fdfeff;border-color:#fcfeff;}.alert-light .alert-link{color:#4e5052;}.alert-dark{color:#1d2b38;background-color:#d6dadf;border-color:#c1c8ce;}.alert-dark .alert-link{color:#17222d;}@keyframes progress-bar-stripes{0%{background-position-x:1rem;}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem;}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#4276a7;transition:width .6s ease;}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none;}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem;}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes;}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none;}}.list-group{display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:.25rem;}.list-group-numbered{list-style-type:none;counter-reset:section;}.list-group-numbered>li::before{content:counters(section,".") ". ";counter-increment:section;}.list-group-item-action{width:100%;color:#495057;text-align:inherit;}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa;}.list-group-item-action:active{color:#00305e;background-color:#e9ecef;}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125);}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit;}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit;}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff;}.list-group-item.active{z-index:2;color:#fff;background-color:#4276a7;border-color:#4276a7;}.list-group-item+.list-group-item{border-top-width:0;}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px;}.list-group-horizontal{flex-direction:row;}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0;}.list-group-horizontal>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0;}.list-group-horizontal>.list-group-item.active{margin-top:0;}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0;}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px;}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row;}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0;}.list-group-horizontal-sm>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0;}.list-group-horizontal-sm>.list-group-item.active{margin-top:0;}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0;}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px;}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row;}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0;}.list-group-horizontal-md>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0;}.list-group-horizontal-md>.list-group-item.active{margin-top:0;}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0;}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px;}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row;}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0;}.list-group-horizontal-lg>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0;}.list-group-horizontal-lg>.list-group-item.active{margin-top:0;}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0;}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px;}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row;}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0;}.list-group-horizontal-xl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0;}.list-group-horizontal-xl>.list-group-item.active{margin-top:0;}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0;}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px;}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row;}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0;}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-right-radius:.25rem;border-bottom-left-radius:0;}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0;}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-left-width:0;}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:-1px;border-left-width:1px;}}.list-group-flush{border-radius:0;}.list-group-flush>.list-group-item{border-width:0 0 1px;}.list-group-flush>.list-group-item:last-child{border-bottom-width:0;}.list-group-item-primary{color:#284764;background-color:#d9e4ed;}.list-group-item-primary.list-group-item-action:hover,.list-group-item-primary.list-group-item-action:focus{color:#284764;background-color:#c3cdd5;}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#284764;border-color:#284764;}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5;}.list-group-item-secondary.list-group-item-action:hover,.list-group-item-secondary.list-group-item-action:focus{color:#41464b;background-color:#cbccce;}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b;}.list-group-item-success{color:#42673b;background-color:#e2eee0;}.list-group-item-success.list-group-item-action:hover,.list-group-item-success.list-group-item-action:focus{color:#42673b;background-color:#cbd6ca;}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#42673b;border-color:#42673b;}.list-group-item-info{color:#41626e;background-color:#e2edf1;}.list-group-item-info.list-group-item-action:hover,.list-group-item-info.list-group-item-action:focus{color:#41626e;background-color:#cbd5d9;}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#41626e;border-color:#41626e;}.list-group-item-warning{color:#804d2e;background-color:#f7e6db;}.list-group-item-warning.list-group-item-action:hover,.list-group-item-warning.list-group-item-action:focus{color:#804d2e;background-color:#decfc5;}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#804d2e;border-color:#804d2e;}.list-group-item-danger{color:#762e29;background-color:#f3dbda;}.list-group-item-danger.list-group-item-action:hover,.list-group-item-danger.list-group-item-action:focus{color:#762e29;background-color:#dbc5c4;}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#762e29;border-color:#762e29;}.list-group-item-light{color:#626466;background-color:#fdfeff;}.list-group-item-light.list-group-item-action:hover,.list-group-item-light.list-group-item-action:focus{color:#626466;background-color:#e4e5e6;}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#626466;border-color:#626466;}.list-group-item-dark{color:#1d2b38;background-color:#d6dadf;}.list-group-item-dark.list-group-item-action:hover,.list-group-item-dark.list-group-item-action:focus{color:#1d2b38;background-color:#c1c4c9;}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1d2b38;border-color:#1d2b38;}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5;}.btn-close:hover{color:#000;text-decoration:none;opacity:.75;}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(66,118,167,.25);opacity:1;}.btn-close:disabled,.btn-close.disabled{pointer-events:none;user-select:none;opacity:.25;}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%);}.modal{position:fixed;top:0;left:0;z-index:1055;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0;}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none;}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px);}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none;}}.modal.show .modal-dialog{transform:none;}.modal.modal-static .modal-dialog{transform:scale(1.02);}.modal-dialog-scrollable{height:calc(100% - 1rem);}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden;}.modal-dialog-scrollable .modal-body{overflow-y:auto;}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem);}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0;}.modal-backdrop{position:fixed;top:0;left:0;z-index:1050;width:100vw;height:100vh;background-color:#000;}.modal-backdrop.fade{opacity:0;}.modal-backdrop.show{opacity:.5;}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px);}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem -.5rem -.5rem auto;}.modal-title{margin-bottom:0;line-height:1.5;}.modal-body{position:relative;flex:1 1 auto;padding:1rem;}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-right-radius:calc(.3rem - 1px);border-bottom-left-radius:calc(.3rem - 1px);}.modal-footer>*{margin:.25rem;}@media(min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto;}.modal-dialog-scrollable{height:calc(100% - 3.5rem);}.modal-dialog-centered{min-height:calc(100% - 3.5rem);}.modal-sm{max-width:300px;}}@media(min-width:992px){.modal-lg,.modal-xl{max-width:800px;}}@media(min-width:1200px){.modal-xl{max-width:1140px;}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen .modal-header{border-radius:0;}.modal-fullscreen .modal-body{overflow-y:auto;}.modal-fullscreen .modal-footer{border-radius:0;}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-sm-down .modal-header{border-radius:0;}.modal-fullscreen-sm-down .modal-body{overflow-y:auto;}.modal-fullscreen-sm-down .modal-footer{border-radius:0;}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-md-down .modal-header{border-radius:0;}.modal-fullscreen-md-down .modal-body{overflow-y:auto;}.modal-fullscreen-md-down .modal-footer{border-radius:0;}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-lg-down .modal-header{border-radius:0;}.modal-fullscreen-lg-down .modal-body{overflow-y:auto;}.modal-fullscreen-lg-down .modal-footer{border-radius:0;}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-xl-down .modal-header{border-radius:0;}.modal-fullscreen-xl-down .modal-body{overflow-y:auto;}.modal-fullscreen-xl-down .modal-footer{border-radius:0;}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-xxl-down .modal-header{border-radius:0;}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto;}.modal-fullscreen-xxl-down .modal-footer{border-radius:0;}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:"Source Sans Pro",sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0;}.tooltip.show{opacity:.9;}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem;}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid;}.bs-tooltip-top,.bs-tooltip-auto[data-popper-placement^=top]{padding:.4rem 0;}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:0;}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000;}.bs-tooltip-end,.bs-tooltip-auto[data-popper-placement^=right]{padding:0 .4rem;}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:0;width:.4rem;height:.8rem;}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000;}.bs-tooltip-bottom,.bs-tooltip-auto[data-popper-placement^=bottom]{padding:.4rem 0;}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:0;}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000;}.bs-tooltip-start,.bs-tooltip-auto[data-popper-placement^=left]{padding:0 .4rem;}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:0;width:.4rem;height:.8rem;}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000;}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem;}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:"Source Sans Pro",sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem;}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-.5rem - 1px);}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25);}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff;}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem;}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25);}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff;}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-.5rem - 1px);}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25);}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff;}.bs-popover-bottom .popover-header::before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f0f0f0;}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem;}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25);}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff;}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px);}.popover-header:empty{display:none;}.popover-body{padding:1rem 1rem;color:#00305e;}.carousel{position:relative;}.carousel.pointer-event{touch-action:pan-y;}.carousel-inner{position:relative;width:100%;overflow:hidden;}.carousel-inner::after{display:block;clear:both;content:"";}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out;}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none;}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block;}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translateX(100%);}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translateX(-100%);}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none;}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1;}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s;}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none;}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;border:0;opacity:.5;transition:opacity .15s ease;}@media(prefers-reduced-motion:reduce){.carousel-control-prev,.carousel-control-next{transition:none;}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9;}.carousel-control-prev{left:0;}.carousel-control-next{right:0;}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%;}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none;}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease;}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none;}}.carousel-indicators .active{opacity:1;}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center;}.carousel-dark .carousel-control-prev-icon,.carousel-dark .carousel-control-next-icon{filter:invert(1) grayscale(100);}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000;}.carousel-dark .carousel-caption{color:#000;}@keyframes spinner-border{to{transform:rotate(360deg);}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;animation:.75s linear infinite spinner-border;}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em;}@keyframes spinner-grow{0%{transform:scale(0);}50%{opacity:1;transform:none;}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;animation:.75s linear infinite spinner-grow;}.spinner-grow-sm{width:1rem;height:1rem;}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{animation-duration:1.5s;}}.offcanvas{position:fixed;bottom:0;z-index:1045;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out;}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none;}}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000;}.offcanvas-backdrop.fade{opacity:0;}.offcanvas-backdrop.show{opacity:.5;}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem;}.offcanvas-header .btn-close{padding:.5rem .5rem;margin-top:-.5rem;margin-right:-.5rem;margin-bottom:-.5rem;}.offcanvas-title{margin-bottom:0;line-height:1.5;}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto;}.offcanvas-start{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%);}.offcanvas-end{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%);}.offcanvas-top{top:0;right:0;left:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%);}.offcanvas-bottom{right:0;left:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%);}.offcanvas.show{transform:none;}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentColor;opacity:.5;}.placeholder.btn::before{display:inline-block;content:"";}.placeholder-xs{min-height:.6em;}.placeholder-sm{min-height:.8em;}.placeholder-lg{min-height:1.2em;}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite;}@keyframes placeholder-glow{50%{opacity:.2;}}.placeholder-wave{mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);mask-size:200% 100%;animation:placeholder-wave 2s linear infinite;}@keyframes placeholder-wave{100%{mask-position:-200% 0%;}}.clearfix::after{display:block;clear:both;content:"";}.link-primary{color:#4276a7;}.link-primary:hover,.link-primary:focus{color:#355e86;}.link-secondary{color:#6c757d;}.link-secondary:hover,.link-secondary:focus{color:#565e64;}.link-success{color:#6eab63;}.link-success:hover,.link-success:focus{color:#8bbc82;}.link-info{color:#6da3b7;}.link-info:hover,.link-info:focus{color:#8ab5c5;}.link-warning{color:#d5804c;}.link-warning:hover,.link-warning:focus{color:#dd9970;}.link-danger{color:#c54c45;}.link-danger:hover,.link-danger:focus{color:#9e3d37;}.link-light{color:#f5faff;}.link-light:hover,.link-light:focus{color:#f7fbff;}.link-dark{color:#30475d;}.link-dark:hover,.link-dark:focus{color:#26394a;}.ratio{position:relative;width:100%;}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:"";}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%;}.ratio-1x1{--bs-aspect-ratio:100%;}.ratio-4x3{--bs-aspect-ratio:75%;}.ratio-16x9{--bs-aspect-ratio:56.25%;}.ratio-21x9{--bs-aspect-ratio:42.8571428571%;}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030;}.sticky-top{position:sticky;top:0;z-index:1020;}@media(min-width:576px){.sticky-sm-top{position:sticky;top:0;z-index:1020;}}@media(min-width:768px){.sticky-md-top{position:sticky;top:0;z-index:1020;}}@media(min-width:992px){.sticky-lg-top{position:sticky;top:0;z-index:1020;}}@media(min-width:1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020;}}@media(min-width:1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020;}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch;}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch;}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important;}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:"";}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentColor;opacity:.25;}.align-baseline{vertical-align:baseline!important;}.align-top{vertical-align:top!important;}.align-middle{vertical-align:middle!important;}.align-bottom{vertical-align:bottom!important;}.align-text-bottom{vertical-align:text-bottom!important;}.align-text-top{vertical-align:text-top!important;}.float-start{float:left!important;}.float-end{float:right!important;}.float-none{float:none!important;}.opacity-0{opacity:0!important;}.opacity-25{opacity:.25!important;}.opacity-50{opacity:.5!important;}.opacity-75{opacity:.75!important;}.opacity-100{opacity:1!important;}.overflow-auto{overflow:auto!important;}.overflow-hidden{overflow:hidden!important;}.overflow-visible{overflow:visible!important;}.overflow-scroll{overflow:scroll!important;}.d-inline{display:inline!important;}.d-inline-block{display:inline-block!important;}.d-block{display:block!important;}.d-grid{display:grid!important;}.d-table{display:table!important;}.d-table-row{display:table-row!important;}.d-table-cell{display:table-cell!important;}.d-flex{display:flex!important;}.d-inline-flex{display:inline-flex!important;}.d-none{display:none!important;}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important;}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important;}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important;}.shadow-none{box-shadow:none!important;}.position-static{position:static!important;}.position-relative{position:relative!important;}.position-absolute{position:absolute!important;}.position-fixed{position:fixed!important;}.position-sticky{position:sticky!important;}.top-0{top:0!important;}.top-50{top:50%!important;}.top-100{top:100%!important;}.bottom-0{bottom:0!important;}.bottom-50{bottom:50%!important;}.bottom-100{bottom:100%!important;}.start-0{left:0!important;}.start-50{left:50%!important;}.start-100{left:100%!important;}.end-0{right:0!important;}.end-50{right:50%!important;}.end-100{right:100%!important;}.translate-middle{transform:translate(-50%,-50%)!important;}.translate-middle-x{transform:translateX(-50%)!important;}.translate-middle-y{transform:translateY(-50%)!important;}.border{border:1px solid #dee2e6!important;}.border-0{border:0!important;}.border-top{border-top:1px solid #dee2e6!important;}.border-top-0{border-top:0!important;}.border-end{border-right:1px solid #dee2e6!important;}.border-end-0{border-right:0!important;}.border-bottom{border-bottom:1px solid #dee2e6!important;}.border-bottom-0{border-bottom:0!important;}.border-start{border-left:1px solid #dee2e6!important;}.border-start-0{border-left:0!important;}.border-primary{border-color:#4276a7!important;}.border-secondary{border-color:#6c757d!important;}.border-success{border-color:#6eab63!important;}.border-info{border-color:#6da3b7!important;}.border-warning{border-color:#d5804c!important;}.border-danger{border-color:#c54c45!important;}.border-light{border-color:#f5faff!important;}.border-dark{border-color:#30475d!important;}.border-white{border-color:#fff!important;}.border-1{border-width:1px!important;}.border-2{border-width:2px!important;}.border-3{border-width:3px!important;}.border-4{border-width:4px!important;}.border-5{border-width:5px!important;}.w-25{width:25%!important;}.w-50{width:50%!important;}.w-75{width:75%!important;}.w-100{width:100%!important;}.w-auto{width:auto!important;}.mw-100{max-width:100%!important;}.vw-100{width:100vw!important;}.min-vw-100{min-width:100vw!important;}.h-25{height:25%!important;}.h-50{height:50%!important;}.h-75{height:75%!important;}.h-100{height:100%!important;}.h-auto{height:auto!important;}.mh-100{max-height:100%!important;}.vh-100{height:100vh!important;}.min-vh-100{min-height:100vh!important;}.flex-fill{flex:1 1 auto!important;}.flex-row{flex-direction:row!important;}.flex-column{flex-direction:column!important;}.flex-row-reverse{flex-direction:row-reverse!important;}.flex-column-reverse{flex-direction:column-reverse!important;}.flex-grow-0{flex-grow:0!important;}.flex-grow-1{flex-grow:1!important;}.flex-shrink-0{flex-shrink:0!important;}.flex-shrink-1{flex-shrink:1!important;}.flex-wrap{flex-wrap:wrap!important;}.flex-nowrap{flex-wrap:nowrap!important;}.flex-wrap-reverse{flex-wrap:wrap-reverse!important;}.gap-0{gap:0!important;}.gap-1{gap:.25rem!important;}.gap-2{gap:.5rem!important;}.gap-3{gap:1rem!important;}.gap-4{gap:1.5rem!important;}.gap-5{gap:3rem!important;}.justify-content-start{justify-content:flex-start!important;}.justify-content-end{justify-content:flex-end!important;}.justify-content-center{justify-content:center!important;}.justify-content-between{justify-content:space-between!important;}.justify-content-around{justify-content:space-around!important;}.justify-content-evenly{justify-content:space-evenly!important;}.align-items-start{align-items:flex-start!important;}.align-items-end{align-items:flex-end!important;}.align-items-center{align-items:center!important;}.align-items-baseline{align-items:baseline!important;}.align-items-stretch{align-items:stretch!important;}.align-content-start{align-content:flex-start!important;}.align-content-end{align-content:flex-end!important;}.align-content-center{align-content:center!important;}.align-content-between{align-content:space-between!important;}.align-content-around{align-content:space-around!important;}.align-content-stretch{align-content:stretch!important;}.align-self-auto{align-self:auto!important;}.align-self-start{align-self:flex-start!important;}.align-self-end{align-self:flex-end!important;}.align-self-center{align-self:center!important;}.align-self-baseline{align-self:baseline!important;}.align-self-stretch{align-self:stretch!important;}.order-first{order:-1!important;}.order-0{order:0!important;}.order-1{order:1!important;}.order-2{order:2!important;}.order-3{order:3!important;}.order-4{order:4!important;}.order-5{order:5!important;}.order-last{order:6!important;}.m-0{margin:0!important;}.m-1{margin:.25rem!important;}.m-2{margin:.5rem!important;}.m-3{margin:1rem!important;}.m-4{margin:1.5rem!important;}.m-5{margin:3rem!important;}.m-auto{margin:auto!important;}.mx-0{margin-right:0!important;margin-left:0!important;}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-auto{margin-right:auto!important;margin-left:auto!important;}.my-0{margin-top:0!important;margin-bottom:0!important;}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-0{margin-top:0!important;}.mt-1{margin-top:.25rem!important;}.mt-2{margin-top:.5rem!important;}.mt-3{margin-top:1rem!important;}.mt-4{margin-top:1.5rem!important;}.mt-5{margin-top:3rem!important;}.mt-auto{margin-top:auto!important;}.me-0{margin-right:0!important;}.me-1{margin-right:.25rem!important;}.me-2{margin-right:.5rem!important;}.me-3{margin-right:1rem!important;}.me-4{margin-right:1.5rem!important;}.me-5{margin-right:3rem!important;}.me-auto{margin-right:auto!important;}.mb-0{margin-bottom:0!important;}.mb-1{margin-bottom:.25rem!important;}.mb-2{margin-bottom:.5rem!important;}.mb-3{margin-bottom:1rem!important;}.mb-4{margin-bottom:1.5rem!important;}.mb-5{margin-bottom:3rem!important;}.mb-auto{margin-bottom:auto!important;}.ms-0{margin-left:0!important;}.ms-1{margin-left:.25rem!important;}.ms-2{margin-left:.5rem!important;}.ms-3{margin-left:1rem!important;}.ms-4{margin-left:1.5rem!important;}.ms-5{margin-left:3rem!important;}.ms-auto{margin-left:auto!important;}.p-0{padding:0!important;}.p-1{padding:.25rem!important;}.p-2{padding:.5rem!important;}.p-3{padding:1rem!important;}.p-4{padding:1.5rem!important;}.p-5{padding:3rem!important;}.px-0{padding-right:0!important;padding-left:0!important;}.px-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-3{padding-right:1rem!important;padding-left:1rem!important;}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-5{padding-right:3rem!important;padding-left:3rem!important;}.py-0{padding-top:0!important;padding-bottom:0!important;}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-0{padding-top:0!important;}.pt-1{padding-top:.25rem!important;}.pt-2{padding-top:.5rem!important;}.pt-3{padding-top:1rem!important;}.pt-4{padding-top:1.5rem!important;}.pt-5{padding-top:3rem!important;}.pe-0{padding-right:0!important;}.pe-1{padding-right:.25rem!important;}.pe-2{padding-right:.5rem!important;}.pe-3{padding-right:1rem!important;}.pe-4{padding-right:1.5rem!important;}.pe-5{padding-right:3rem!important;}.pb-0{padding-bottom:0!important;}.pb-1{padding-bottom:.25rem!important;}.pb-2{padding-bottom:.5rem!important;}.pb-3{padding-bottom:1rem!important;}.pb-4{padding-bottom:1.5rem!important;}.pb-5{padding-bottom:3rem!important;}.ps-0{padding-left:0!important;}.ps-1{padding-left:.25rem!important;}.ps-2{padding-left:.5rem!important;}.ps-3{padding-left:1rem!important;}.ps-4{padding-left:1.5rem!important;}.ps-5{padding-left:3rem!important;}.font-monospace{font-family:var(--bs-font-monospace)!important;}.fs-1{font-size:calc(1.375rem + 1.5vw)!important;}.fs-2{font-size:calc(1.325rem + .9vw)!important;}.fs-3{font-size:calc(1.3rem + .6vw)!important;}.fs-4{font-size:calc(1.275rem + .3vw)!important;}.fs-5{font-size:1.25rem!important;}.fs-6{font-size:1rem!important;}.fst-italic{font-style:italic!important;}.fst-normal{font-style:normal!important;}.fw-light{font-weight:300!important;}.fw-lighter{font-weight:lighter!important;}.fw-normal{font-weight:400!important;}.fw-bold{font-weight:700!important;}.fw-bolder{font-weight:bolder!important;}.lh-1{line-height:1!important;}.lh-sm{line-height:1.25!important;}.lh-base{line-height:1.5!important;}.lh-lg{line-height:2!important;}.text-start{text-align:left!important;}.text-end{text-align:right!important;}.text-center{text-align:center!important;}.text-decoration-none{text-decoration:none!important;}.text-decoration-underline{text-decoration:underline!important;}.text-decoration-line-through{text-decoration:line-through!important;}.text-lowercase{text-transform:lowercase!important;}.text-uppercase{text-transform:uppercase!important;}.text-capitalize{text-transform:capitalize!important;}.text-wrap{white-space:normal!important;}.text-nowrap{white-space:nowrap!important;}.text-break{word-wrap:break-word!important;word-break:break-word!important;}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important;}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important;}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important;}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important;}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important;}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important;}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important;}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important;}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important;}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important;}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important;}.text-muted{--bs-text-opacity:1;color:#6c757d!important;}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important;}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important;}.text-reset{--bs-text-opacity:1;color:inherit!important;}.text-opacity-25{--bs-text-opacity:.25;}.text-opacity-50{--bs-text-opacity:.5;}.text-opacity-75{--bs-text-opacity:.75;}.text-opacity-100{--bs-text-opacity:1;}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important;}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important;}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important;}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important;}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important;}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important;}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important;}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important;}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important;}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important;}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important;}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important;}.bg-opacity-10{--bs-bg-opacity:.1;}.bg-opacity-25{--bs-bg-opacity:.25;}.bg-opacity-50{--bs-bg-opacity:.5;}.bg-opacity-75{--bs-bg-opacity:.75;}.bg-opacity-100{--bs-bg-opacity:1;}.bg-gradient{background-image:var(--bs-gradient)!important;}.user-select-all{user-select:all!important;}.user-select-auto{user-select:auto!important;}.user-select-none{user-select:none!important;}.pe-none{pointer-events:none!important;}.pe-auto{pointer-events:auto!important;}.rounded{border-radius:.25rem!important;}.rounded-0{border-radius:0!important;}.rounded-1{border-radius:.2rem!important;}.rounded-2{border-radius:.25rem!important;}.rounded-3{border-radius:.3rem!important;}.rounded-circle{border-radius:50%!important;}.rounded-pill{border-radius:50rem!important;}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important;}.rounded-end{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important;}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important;}.rounded-start{border-bottom-left-radius:.25rem!important;border-top-left-radius:.25rem!important;}.visible{visibility:visible!important;}.invisible{visibility:hidden!important;}@media(min-width:576px){.float-sm-start{float:left!important;}.float-sm-end{float:right!important;}.float-sm-none{float:none!important;}.d-sm-inline{display:inline!important;}.d-sm-inline-block{display:inline-block!important;}.d-sm-block{display:block!important;}.d-sm-grid{display:grid!important;}.d-sm-table{display:table!important;}.d-sm-table-row{display:table-row!important;}.d-sm-table-cell{display:table-cell!important;}.d-sm-flex{display:flex!important;}.d-sm-inline-flex{display:inline-flex!important;}.d-sm-none{display:none!important;}.flex-sm-fill{flex:1 1 auto!important;}.flex-sm-row{flex-direction:row!important;}.flex-sm-column{flex-direction:column!important;}.flex-sm-row-reverse{flex-direction:row-reverse!important;}.flex-sm-column-reverse{flex-direction:column-reverse!important;}.flex-sm-grow-0{flex-grow:0!important;}.flex-sm-grow-1{flex-grow:1!important;}.flex-sm-shrink-0{flex-shrink:0!important;}.flex-sm-shrink-1{flex-shrink:1!important;}.flex-sm-wrap{flex-wrap:wrap!important;}.flex-sm-nowrap{flex-wrap:nowrap!important;}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important;}.gap-sm-0{gap:0!important;}.gap-sm-1{gap:.25rem!important;}.gap-sm-2{gap:.5rem!important;}.gap-sm-3{gap:1rem!important;}.gap-sm-4{gap:1.5rem!important;}.gap-sm-5{gap:3rem!important;}.justify-content-sm-start{justify-content:flex-start!important;}.justify-content-sm-end{justify-content:flex-end!important;}.justify-content-sm-center{justify-content:center!important;}.justify-content-sm-between{justify-content:space-between!important;}.justify-content-sm-around{justify-content:space-around!important;}.justify-content-sm-evenly{justify-content:space-evenly!important;}.align-items-sm-start{align-items:flex-start!important;}.align-items-sm-end{align-items:flex-end!important;}.align-items-sm-center{align-items:center!important;}.align-items-sm-baseline{align-items:baseline!important;}.align-items-sm-stretch{align-items:stretch!important;}.align-content-sm-start{align-content:flex-start!important;}.align-content-sm-end{align-content:flex-end!important;}.align-content-sm-center{align-content:center!important;}.align-content-sm-between{align-content:space-between!important;}.align-content-sm-around{align-content:space-around!important;}.align-content-sm-stretch{align-content:stretch!important;}.align-self-sm-auto{align-self:auto!important;}.align-self-sm-start{align-self:flex-start!important;}.align-self-sm-end{align-self:flex-end!important;}.align-self-sm-center{align-self:center!important;}.align-self-sm-baseline{align-self:baseline!important;}.align-self-sm-stretch{align-self:stretch!important;}.order-sm-first{order:-1!important;}.order-sm-0{order:0!important;}.order-sm-1{order:1!important;}.order-sm-2{order:2!important;}.order-sm-3{order:3!important;}.order-sm-4{order:4!important;}.order-sm-5{order:5!important;}.order-sm-last{order:6!important;}.m-sm-0{margin:0!important;}.m-sm-1{margin:.25rem!important;}.m-sm-2{margin:.5rem!important;}.m-sm-3{margin:1rem!important;}.m-sm-4{margin:1.5rem!important;}.m-sm-5{margin:3rem!important;}.m-sm-auto{margin:auto!important;}.mx-sm-0{margin-right:0!important;margin-left:0!important;}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important;}.my-sm-0{margin-top:0!important;margin-bottom:0!important;}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-sm-0{margin-top:0!important;}.mt-sm-1{margin-top:.25rem!important;}.mt-sm-2{margin-top:.5rem!important;}.mt-sm-3{margin-top:1rem!important;}.mt-sm-4{margin-top:1.5rem!important;}.mt-sm-5{margin-top:3rem!important;}.mt-sm-auto{margin-top:auto!important;}.me-sm-0{margin-right:0!important;}.me-sm-1{margin-right:.25rem!important;}.me-sm-2{margin-right:.5rem!important;}.me-sm-3{margin-right:1rem!important;}.me-sm-4{margin-right:1.5rem!important;}.me-sm-5{margin-right:3rem!important;}.me-sm-auto{margin-right:auto!important;}.mb-sm-0{margin-bottom:0!important;}.mb-sm-1{margin-bottom:.25rem!important;}.mb-sm-2{margin-bottom:.5rem!important;}.mb-sm-3{margin-bottom:1rem!important;}.mb-sm-4{margin-bottom:1.5rem!important;}.mb-sm-5{margin-bottom:3rem!important;}.mb-sm-auto{margin-bottom:auto!important;}.ms-sm-0{margin-left:0!important;}.ms-sm-1{margin-left:.25rem!important;}.ms-sm-2{margin-left:.5rem!important;}.ms-sm-3{margin-left:1rem!important;}.ms-sm-4{margin-left:1.5rem!important;}.ms-sm-5{margin-left:3rem!important;}.ms-sm-auto{margin-left:auto!important;}.p-sm-0{padding:0!important;}.p-sm-1{padding:.25rem!important;}.p-sm-2{padding:.5rem!important;}.p-sm-3{padding:1rem!important;}.p-sm-4{padding:1.5rem!important;}.p-sm-5{padding:3rem!important;}.px-sm-0{padding-right:0!important;padding-left:0!important;}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important;}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important;}.py-sm-0{padding-top:0!important;padding-bottom:0!important;}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-sm-0{padding-top:0!important;}.pt-sm-1{padding-top:.25rem!important;}.pt-sm-2{padding-top:.5rem!important;}.pt-sm-3{padding-top:1rem!important;}.pt-sm-4{padding-top:1.5rem!important;}.pt-sm-5{padding-top:3rem!important;}.pe-sm-0{padding-right:0!important;}.pe-sm-1{padding-right:.25rem!important;}.pe-sm-2{padding-right:.5rem!important;}.pe-sm-3{padding-right:1rem!important;}.pe-sm-4{padding-right:1.5rem!important;}.pe-sm-5{padding-right:3rem!important;}.pb-sm-0{padding-bottom:0!important;}.pb-sm-1{padding-bottom:.25rem!important;}.pb-sm-2{padding-bottom:.5rem!important;}.pb-sm-3{padding-bottom:1rem!important;}.pb-sm-4{padding-bottom:1.5rem!important;}.pb-sm-5{padding-bottom:3rem!important;}.ps-sm-0{padding-left:0!important;}.ps-sm-1{padding-left:.25rem!important;}.ps-sm-2{padding-left:.5rem!important;}.ps-sm-3{padding-left:1rem!important;}.ps-sm-4{padding-left:1.5rem!important;}.ps-sm-5{padding-left:3rem!important;}.text-sm-start{text-align:left!important;}.text-sm-end{text-align:right!important;}.text-sm-center{text-align:center!important;}}@media(min-width:768px){.float-md-start{float:left!important;}.float-md-end{float:right!important;}.float-md-none{float:none!important;}.d-md-inline{display:inline!important;}.d-md-inline-block{display:inline-block!important;}.d-md-block{display:block!important;}.d-md-grid{display:grid!important;}.d-md-table{display:table!important;}.d-md-table-row{display:table-row!important;}.d-md-table-cell{display:table-cell!important;}.d-md-flex{display:flex!important;}.d-md-inline-flex{display:inline-flex!important;}.d-md-none{display:none!important;}.flex-md-fill{flex:1 1 auto!important;}.flex-md-row{flex-direction:row!important;}.flex-md-column{flex-direction:column!important;}.flex-md-row-reverse{flex-direction:row-reverse!important;}.flex-md-column-reverse{flex-direction:column-reverse!important;}.flex-md-grow-0{flex-grow:0!important;}.flex-md-grow-1{flex-grow:1!important;}.flex-md-shrink-0{flex-shrink:0!important;}.flex-md-shrink-1{flex-shrink:1!important;}.flex-md-wrap{flex-wrap:wrap!important;}.flex-md-nowrap{flex-wrap:nowrap!important;}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important;}.gap-md-0{gap:0!important;}.gap-md-1{gap:.25rem!important;}.gap-md-2{gap:.5rem!important;}.gap-md-3{gap:1rem!important;}.gap-md-4{gap:1.5rem!important;}.gap-md-5{gap:3rem!important;}.justify-content-md-start{justify-content:flex-start!important;}.justify-content-md-end{justify-content:flex-end!important;}.justify-content-md-center{justify-content:center!important;}.justify-content-md-between{justify-content:space-between!important;}.justify-content-md-around{justify-content:space-around!important;}.justify-content-md-evenly{justify-content:space-evenly!important;}.align-items-md-start{align-items:flex-start!important;}.align-items-md-end{align-items:flex-end!important;}.align-items-md-center{align-items:center!important;}.align-items-md-baseline{align-items:baseline!important;}.align-items-md-stretch{align-items:stretch!important;}.align-content-md-start{align-content:flex-start!important;}.align-content-md-end{align-content:flex-end!important;}.align-content-md-center{align-content:center!important;}.align-content-md-between{align-content:space-between!important;}.align-content-md-around{align-content:space-around!important;}.align-content-md-stretch{align-content:stretch!important;}.align-self-md-auto{align-self:auto!important;}.align-self-md-start{align-self:flex-start!important;}.align-self-md-end{align-self:flex-end!important;}.align-self-md-center{align-self:center!important;}.align-self-md-baseline{align-self:baseline!important;}.align-self-md-stretch{align-self:stretch!important;}.order-md-first{order:-1!important;}.order-md-0{order:0!important;}.order-md-1{order:1!important;}.order-md-2{order:2!important;}.order-md-3{order:3!important;}.order-md-4{order:4!important;}.order-md-5{order:5!important;}.order-md-last{order:6!important;}.m-md-0{margin:0!important;}.m-md-1{margin:.25rem!important;}.m-md-2{margin:.5rem!important;}.m-md-3{margin:1rem!important;}.m-md-4{margin:1.5rem!important;}.m-md-5{margin:3rem!important;}.m-md-auto{margin:auto!important;}.mx-md-0{margin-right:0!important;margin-left:0!important;}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-md-auto{margin-right:auto!important;margin-left:auto!important;}.my-md-0{margin-top:0!important;margin-bottom:0!important;}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-md-0{margin-top:0!important;}.mt-md-1{margin-top:.25rem!important;}.mt-md-2{margin-top:.5rem!important;}.mt-md-3{margin-top:1rem!important;}.mt-md-4{margin-top:1.5rem!important;}.mt-md-5{margin-top:3rem!important;}.mt-md-auto{margin-top:auto!important;}.me-md-0{margin-right:0!important;}.me-md-1{margin-right:.25rem!important;}.me-md-2{margin-right:.5rem!important;}.me-md-3{margin-right:1rem!important;}.me-md-4{margin-right:1.5rem!important;}.me-md-5{margin-right:3rem!important;}.me-md-auto{margin-right:auto!important;}.mb-md-0{margin-bottom:0!important;}.mb-md-1{margin-bottom:.25rem!important;}.mb-md-2{margin-bottom:.5rem!important;}.mb-md-3{margin-bottom:1rem!important;}.mb-md-4{margin-bottom:1.5rem!important;}.mb-md-5{margin-bottom:3rem!important;}.mb-md-auto{margin-bottom:auto!important;}.ms-md-0{margin-left:0!important;}.ms-md-1{margin-left:.25rem!important;}.ms-md-2{margin-left:.5rem!important;}.ms-md-3{margin-left:1rem!important;}.ms-md-4{margin-left:1.5rem!important;}.ms-md-5{margin-left:3rem!important;}.ms-md-auto{margin-left:auto!important;}.p-md-0{padding:0!important;}.p-md-1{padding:.25rem!important;}.p-md-2{padding:.5rem!important;}.p-md-3{padding:1rem!important;}.p-md-4{padding:1.5rem!important;}.p-md-5{padding:3rem!important;}.px-md-0{padding-right:0!important;padding-left:0!important;}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-md-3{padding-right:1rem!important;padding-left:1rem!important;}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-md-5{padding-right:3rem!important;padding-left:3rem!important;}.py-md-0{padding-top:0!important;padding-bottom:0!important;}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-md-0{padding-top:0!important;}.pt-md-1{padding-top:.25rem!important;}.pt-md-2{padding-top:.5rem!important;}.pt-md-3{padding-top:1rem!important;}.pt-md-4{padding-top:1.5rem!important;}.pt-md-5{padding-top:3rem!important;}.pe-md-0{padding-right:0!important;}.pe-md-1{padding-right:.25rem!important;}.pe-md-2{padding-right:.5rem!important;}.pe-md-3{padding-right:1rem!important;}.pe-md-4{padding-right:1.5rem!important;}.pe-md-5{padding-right:3rem!important;}.pb-md-0{padding-bottom:0!important;}.pb-md-1{padding-bottom:.25rem!important;}.pb-md-2{padding-bottom:.5rem!important;}.pb-md-3{padding-bottom:1rem!important;}.pb-md-4{padding-bottom:1.5rem!important;}.pb-md-5{padding-bottom:3rem!important;}.ps-md-0{padding-left:0!important;}.ps-md-1{padding-left:.25rem!important;}.ps-md-2{padding-left:.5rem!important;}.ps-md-3{padding-left:1rem!important;}.ps-md-4{padding-left:1.5rem!important;}.ps-md-5{padding-left:3rem!important;}.text-md-start{text-align:left!important;}.text-md-end{text-align:right!important;}.text-md-center{text-align:center!important;}}@media(min-width:992px){.float-lg-start{float:left!important;}.float-lg-end{float:right!important;}.float-lg-none{float:none!important;}.d-lg-inline{display:inline!important;}.d-lg-inline-block{display:inline-block!important;}.d-lg-block{display:block!important;}.d-lg-grid{display:grid!important;}.d-lg-table{display:table!important;}.d-lg-table-row{display:table-row!important;}.d-lg-table-cell{display:table-cell!important;}.d-lg-flex{display:flex!important;}.d-lg-inline-flex{display:inline-flex!important;}.d-lg-none{display:none!important;}.flex-lg-fill{flex:1 1 auto!important;}.flex-lg-row{flex-direction:row!important;}.flex-lg-column{flex-direction:column!important;}.flex-lg-row-reverse{flex-direction:row-reverse!important;}.flex-lg-column-reverse{flex-direction:column-reverse!important;}.flex-lg-grow-0{flex-grow:0!important;}.flex-lg-grow-1{flex-grow:1!important;}.flex-lg-shrink-0{flex-shrink:0!important;}.flex-lg-shrink-1{flex-shrink:1!important;}.flex-lg-wrap{flex-wrap:wrap!important;}.flex-lg-nowrap{flex-wrap:nowrap!important;}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important;}.gap-lg-0{gap:0!important;}.gap-lg-1{gap:.25rem!important;}.gap-lg-2{gap:.5rem!important;}.gap-lg-3{gap:1rem!important;}.gap-lg-4{gap:1.5rem!important;}.gap-lg-5{gap:3rem!important;}.justify-content-lg-start{justify-content:flex-start!important;}.justify-content-lg-end{justify-content:flex-end!important;}.justify-content-lg-center{justify-content:center!important;}.justify-content-lg-between{justify-content:space-between!important;}.justify-content-lg-around{justify-content:space-around!important;}.justify-content-lg-evenly{justify-content:space-evenly!important;}.align-items-lg-start{align-items:flex-start!important;}.align-items-lg-end{align-items:flex-end!important;}.align-items-lg-center{align-items:center!important;}.align-items-lg-baseline{align-items:baseline!important;}.align-items-lg-stretch{align-items:stretch!important;}.align-content-lg-start{align-content:flex-start!important;}.align-content-lg-end{align-content:flex-end!important;}.align-content-lg-center{align-content:center!important;}.align-content-lg-between{align-content:space-between!important;}.align-content-lg-around{align-content:space-around!important;}.align-content-lg-stretch{align-content:stretch!important;}.align-self-lg-auto{align-self:auto!important;}.align-self-lg-start{align-self:flex-start!important;}.align-self-lg-end{align-self:flex-end!important;}.align-self-lg-center{align-self:center!important;}.align-self-lg-baseline{align-self:baseline!important;}.align-self-lg-stretch{align-self:stretch!important;}.order-lg-first{order:-1!important;}.order-lg-0{order:0!important;}.order-lg-1{order:1!important;}.order-lg-2{order:2!important;}.order-lg-3{order:3!important;}.order-lg-4{order:4!important;}.order-lg-5{order:5!important;}.order-lg-last{order:6!important;}.m-lg-0{margin:0!important;}.m-lg-1{margin:.25rem!important;}.m-lg-2{margin:.5rem!important;}.m-lg-3{margin:1rem!important;}.m-lg-4{margin:1.5rem!important;}.m-lg-5{margin:3rem!important;}.m-lg-auto{margin:auto!important;}.mx-lg-0{margin-right:0!important;margin-left:0!important;}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important;}.my-lg-0{margin-top:0!important;margin-bottom:0!important;}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-lg-0{margin-top:0!important;}.mt-lg-1{margin-top:.25rem!important;}.mt-lg-2{margin-top:.5rem!important;}.mt-lg-3{margin-top:1rem!important;}.mt-lg-4{margin-top:1.5rem!important;}.mt-lg-5{margin-top:3rem!important;}.mt-lg-auto{margin-top:auto!important;}.me-lg-0{margin-right:0!important;}.me-lg-1{margin-right:.25rem!important;}.me-lg-2{margin-right:.5rem!important;}.me-lg-3{margin-right:1rem!important;}.me-lg-4{margin-right:1.5rem!important;}.me-lg-5{margin-right:3rem!important;}.me-lg-auto{margin-right:auto!important;}.mb-lg-0{margin-bottom:0!important;}.mb-lg-1{margin-bottom:.25rem!important;}.mb-lg-2{margin-bottom:.5rem!important;}.mb-lg-3{margin-bottom:1rem!important;}.mb-lg-4{margin-bottom:1.5rem!important;}.mb-lg-5{margin-bottom:3rem!important;}.mb-lg-auto{margin-bottom:auto!important;}.ms-lg-0{margin-left:0!important;}.ms-lg-1{margin-left:.25rem!important;}.ms-lg-2{margin-left:.5rem!important;}.ms-lg-3{margin-left:1rem!important;}.ms-lg-4{margin-left:1.5rem!important;}.ms-lg-5{margin-left:3rem!important;}.ms-lg-auto{margin-left:auto!important;}.p-lg-0{padding:0!important;}.p-lg-1{padding:.25rem!important;}.p-lg-2{padding:.5rem!important;}.p-lg-3{padding:1rem!important;}.p-lg-4{padding:1.5rem!important;}.p-lg-5{padding:3rem!important;}.px-lg-0{padding-right:0!important;padding-left:0!important;}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important;}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important;}.py-lg-0{padding-top:0!important;padding-bottom:0!important;}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-lg-0{padding-top:0!important;}.pt-lg-1{padding-top:.25rem!important;}.pt-lg-2{padding-top:.5rem!important;}.pt-lg-3{padding-top:1rem!important;}.pt-lg-4{padding-top:1.5rem!important;}.pt-lg-5{padding-top:3rem!important;}.pe-lg-0{padding-right:0!important;}.pe-lg-1{padding-right:.25rem!important;}.pe-lg-2{padding-right:.5rem!important;}.pe-lg-3{padding-right:1rem!important;}.pe-lg-4{padding-right:1.5rem!important;}.pe-lg-5{padding-right:3rem!important;}.pb-lg-0{padding-bottom:0!important;}.pb-lg-1{padding-bottom:.25rem!important;}.pb-lg-2{padding-bottom:.5rem!important;}.pb-lg-3{padding-bottom:1rem!important;}.pb-lg-4{padding-bottom:1.5rem!important;}.pb-lg-5{padding-bottom:3rem!important;}.ps-lg-0{padding-left:0!important;}.ps-lg-1{padding-left:.25rem!important;}.ps-lg-2{padding-left:.5rem!important;}.ps-lg-3{padding-left:1rem!important;}.ps-lg-4{padding-left:1.5rem!important;}.ps-lg-5{padding-left:3rem!important;}.text-lg-start{text-align:left!important;}.text-lg-end{text-align:right!important;}.text-lg-center{text-align:center!important;}}@media(min-width:1200px){.float-xl-start{float:left!important;}.float-xl-end{float:right!important;}.float-xl-none{float:none!important;}.d-xl-inline{display:inline!important;}.d-xl-inline-block{display:inline-block!important;}.d-xl-block{display:block!important;}.d-xl-grid{display:grid!important;}.d-xl-table{display:table!important;}.d-xl-table-row{display:table-row!important;}.d-xl-table-cell{display:table-cell!important;}.d-xl-flex{display:flex!important;}.d-xl-inline-flex{display:inline-flex!important;}.d-xl-none{display:none!important;}.flex-xl-fill{flex:1 1 auto!important;}.flex-xl-row{flex-direction:row!important;}.flex-xl-column{flex-direction:column!important;}.flex-xl-row-reverse{flex-direction:row-reverse!important;}.flex-xl-column-reverse{flex-direction:column-reverse!important;}.flex-xl-grow-0{flex-grow:0!important;}.flex-xl-grow-1{flex-grow:1!important;}.flex-xl-shrink-0{flex-shrink:0!important;}.flex-xl-shrink-1{flex-shrink:1!important;}.flex-xl-wrap{flex-wrap:wrap!important;}.flex-xl-nowrap{flex-wrap:nowrap!important;}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important;}.gap-xl-0{gap:0!important;}.gap-xl-1{gap:.25rem!important;}.gap-xl-2{gap:.5rem!important;}.gap-xl-3{gap:1rem!important;}.gap-xl-4{gap:1.5rem!important;}.gap-xl-5{gap:3rem!important;}.justify-content-xl-start{justify-content:flex-start!important;}.justify-content-xl-end{justify-content:flex-end!important;}.justify-content-xl-center{justify-content:center!important;}.justify-content-xl-between{justify-content:space-between!important;}.justify-content-xl-around{justify-content:space-around!important;}.justify-content-xl-evenly{justify-content:space-evenly!important;}.align-items-xl-start{align-items:flex-start!important;}.align-items-xl-end{align-items:flex-end!important;}.align-items-xl-center{align-items:center!important;}.align-items-xl-baseline{align-items:baseline!important;}.align-items-xl-stretch{align-items:stretch!important;}.align-content-xl-start{align-content:flex-start!important;}.align-content-xl-end{align-content:flex-end!important;}.align-content-xl-center{align-content:center!important;}.align-content-xl-between{align-content:space-between!important;}.align-content-xl-around{align-content:space-around!important;}.align-content-xl-stretch{align-content:stretch!important;}.align-self-xl-auto{align-self:auto!important;}.align-self-xl-start{align-self:flex-start!important;}.align-self-xl-end{align-self:flex-end!important;}.align-self-xl-center{align-self:center!important;}.align-self-xl-baseline{align-self:baseline!important;}.align-self-xl-stretch{align-self:stretch!important;}.order-xl-first{order:-1!important;}.order-xl-0{order:0!important;}.order-xl-1{order:1!important;}.order-xl-2{order:2!important;}.order-xl-3{order:3!important;}.order-xl-4{order:4!important;}.order-xl-5{order:5!important;}.order-xl-last{order:6!important;}.m-xl-0{margin:0!important;}.m-xl-1{margin:.25rem!important;}.m-xl-2{margin:.5rem!important;}.m-xl-3{margin:1rem!important;}.m-xl-4{margin:1.5rem!important;}.m-xl-5{margin:3rem!important;}.m-xl-auto{margin:auto!important;}.mx-xl-0{margin-right:0!important;margin-left:0!important;}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important;}.my-xl-0{margin-top:0!important;margin-bottom:0!important;}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-xl-0{margin-top:0!important;}.mt-xl-1{margin-top:.25rem!important;}.mt-xl-2{margin-top:.5rem!important;}.mt-xl-3{margin-top:1rem!important;}.mt-xl-4{margin-top:1.5rem!important;}.mt-xl-5{margin-top:3rem!important;}.mt-xl-auto{margin-top:auto!important;}.me-xl-0{margin-right:0!important;}.me-xl-1{margin-right:.25rem!important;}.me-xl-2{margin-right:.5rem!important;}.me-xl-3{margin-right:1rem!important;}.me-xl-4{margin-right:1.5rem!important;}.me-xl-5{margin-right:3rem!important;}.me-xl-auto{margin-right:auto!important;}.mb-xl-0{margin-bottom:0!important;}.mb-xl-1{margin-bottom:.25rem!important;}.mb-xl-2{margin-bottom:.5rem!important;}.mb-xl-3{margin-bottom:1rem!important;}.mb-xl-4{margin-bottom:1.5rem!important;}.mb-xl-5{margin-bottom:3rem!important;}.mb-xl-auto{margin-bottom:auto!important;}.ms-xl-0{margin-left:0!important;}.ms-xl-1{margin-left:.25rem!important;}.ms-xl-2{margin-left:.5rem!important;}.ms-xl-3{margin-left:1rem!important;}.ms-xl-4{margin-left:1.5rem!important;}.ms-xl-5{margin-left:3rem!important;}.ms-xl-auto{margin-left:auto!important;}.p-xl-0{padding:0!important;}.p-xl-1{padding:.25rem!important;}.p-xl-2{padding:.5rem!important;}.p-xl-3{padding:1rem!important;}.p-xl-4{padding:1.5rem!important;}.p-xl-5{padding:3rem!important;}.px-xl-0{padding-right:0!important;padding-left:0!important;}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important;}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important;}.py-xl-0{padding-top:0!important;padding-bottom:0!important;}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-xl-0{padding-top:0!important;}.pt-xl-1{padding-top:.25rem!important;}.pt-xl-2{padding-top:.5rem!important;}.pt-xl-3{padding-top:1rem!important;}.pt-xl-4{padding-top:1.5rem!important;}.pt-xl-5{padding-top:3rem!important;}.pe-xl-0{padding-right:0!important;}.pe-xl-1{padding-right:.25rem!important;}.pe-xl-2{padding-right:.5rem!important;}.pe-xl-3{padding-right:1rem!important;}.pe-xl-4{padding-right:1.5rem!important;}.pe-xl-5{padding-right:3rem!important;}.pb-xl-0{padding-bottom:0!important;}.pb-xl-1{padding-bottom:.25rem!important;}.pb-xl-2{padding-bottom:.5rem!important;}.pb-xl-3{padding-bottom:1rem!important;}.pb-xl-4{padding-bottom:1.5rem!important;}.pb-xl-5{padding-bottom:3rem!important;}.ps-xl-0{padding-left:0!important;}.ps-xl-1{padding-left:.25rem!important;}.ps-xl-2{padding-left:.5rem!important;}.ps-xl-3{padding-left:1rem!important;}.ps-xl-4{padding-left:1.5rem!important;}.ps-xl-5{padding-left:3rem!important;}.text-xl-start{text-align:left!important;}.text-xl-end{text-align:right!important;}.text-xl-center{text-align:center!important;}}@media(min-width:1400px){.float-xxl-start{float:left!important;}.float-xxl-end{float:right!important;}.float-xxl-none{float:none!important;}.d-xxl-inline{display:inline!important;}.d-xxl-inline-block{display:inline-block!important;}.d-xxl-block{display:block!important;}.d-xxl-grid{display:grid!important;}.d-xxl-table{display:table!important;}.d-xxl-table-row{display:table-row!important;}.d-xxl-table-cell{display:table-cell!important;}.d-xxl-flex{display:flex!important;}.d-xxl-inline-flex{display:inline-flex!important;}.d-xxl-none{display:none!important;}.flex-xxl-fill{flex:1 1 auto!important;}.flex-xxl-row{flex-direction:row!important;}.flex-xxl-column{flex-direction:column!important;}.flex-xxl-row-reverse{flex-direction:row-reverse!important;}.flex-xxl-column-reverse{flex-direction:column-reverse!important;}.flex-xxl-grow-0{flex-grow:0!important;}.flex-xxl-grow-1{flex-grow:1!important;}.flex-xxl-shrink-0{flex-shrink:0!important;}.flex-xxl-shrink-1{flex-shrink:1!important;}.flex-xxl-wrap{flex-wrap:wrap!important;}.flex-xxl-nowrap{flex-wrap:nowrap!important;}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important;}.gap-xxl-0{gap:0!important;}.gap-xxl-1{gap:.25rem!important;}.gap-xxl-2{gap:.5rem!important;}.gap-xxl-3{gap:1rem!important;}.gap-xxl-4{gap:1.5rem!important;}.gap-xxl-5{gap:3rem!important;}.justify-content-xxl-start{justify-content:flex-start!important;}.justify-content-xxl-end{justify-content:flex-end!important;}.justify-content-xxl-center{justify-content:center!important;}.justify-content-xxl-between{justify-content:space-between!important;}.justify-content-xxl-around{justify-content:space-around!important;}.justify-content-xxl-evenly{justify-content:space-evenly!important;}.align-items-xxl-start{align-items:flex-start!important;}.align-items-xxl-end{align-items:flex-end!important;}.align-items-xxl-center{align-items:center!important;}.align-items-xxl-baseline{align-items:baseline!important;}.align-items-xxl-stretch{align-items:stretch!important;}.align-content-xxl-start{align-content:flex-start!important;}.align-content-xxl-end{align-content:flex-end!important;}.align-content-xxl-center{align-content:center!important;}.align-content-xxl-between{align-content:space-between!important;}.align-content-xxl-around{align-content:space-around!important;}.align-content-xxl-stretch{align-content:stretch!important;}.align-self-xxl-auto{align-self:auto!important;}.align-self-xxl-start{align-self:flex-start!important;}.align-self-xxl-end{align-self:flex-end!important;}.align-self-xxl-center{align-self:center!important;}.align-self-xxl-baseline{align-self:baseline!important;}.align-self-xxl-stretch{align-self:stretch!important;}.order-xxl-first{order:-1!important;}.order-xxl-0{order:0!important;}.order-xxl-1{order:1!important;}.order-xxl-2{order:2!important;}.order-xxl-3{order:3!important;}.order-xxl-4{order:4!important;}.order-xxl-5{order:5!important;}.order-xxl-last{order:6!important;}.m-xxl-0{margin:0!important;}.m-xxl-1{margin:.25rem!important;}.m-xxl-2{margin:.5rem!important;}.m-xxl-3{margin:1rem!important;}.m-xxl-4{margin:1.5rem!important;}.m-xxl-5{margin:3rem!important;}.m-xxl-auto{margin:auto!important;}.mx-xxl-0{margin-right:0!important;margin-left:0!important;}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important;}.my-xxl-0{margin-top:0!important;margin-bottom:0!important;}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-xxl-0{margin-top:0!important;}.mt-xxl-1{margin-top:.25rem!important;}.mt-xxl-2{margin-top:.5rem!important;}.mt-xxl-3{margin-top:1rem!important;}.mt-xxl-4{margin-top:1.5rem!important;}.mt-xxl-5{margin-top:3rem!important;}.mt-xxl-auto{margin-top:auto!important;}.me-xxl-0{margin-right:0!important;}.me-xxl-1{margin-right:.25rem!important;}.me-xxl-2{margin-right:.5rem!important;}.me-xxl-3{margin-right:1rem!important;}.me-xxl-4{margin-right:1.5rem!important;}.me-xxl-5{margin-right:3rem!important;}.me-xxl-auto{margin-right:auto!important;}.mb-xxl-0{margin-bottom:0!important;}.mb-xxl-1{margin-bottom:.25rem!important;}.mb-xxl-2{margin-bottom:.5rem!important;}.mb-xxl-3{margin-bottom:1rem!important;}.mb-xxl-4{margin-bottom:1.5rem!important;}.mb-xxl-5{margin-bottom:3rem!important;}.mb-xxl-auto{margin-bottom:auto!important;}.ms-xxl-0{margin-left:0!important;}.ms-xxl-1{margin-left:.25rem!important;}.ms-xxl-2{margin-left:.5rem!important;}.ms-xxl-3{margin-left:1rem!important;}.ms-xxl-4{margin-left:1.5rem!important;}.ms-xxl-5{margin-left:3rem!important;}.ms-xxl-auto{margin-left:auto!important;}.p-xxl-0{padding:0!important;}.p-xxl-1{padding:.25rem!important;}.p-xxl-2{padding:.5rem!important;}.p-xxl-3{padding:1rem!important;}.p-xxl-4{padding:1.5rem!important;}.p-xxl-5{padding:3rem!important;}.px-xxl-0{padding-right:0!important;padding-left:0!important;}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important;}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important;}.py-xxl-0{padding-top:0!important;padding-bottom:0!important;}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-xxl-0{padding-top:0!important;}.pt-xxl-1{padding-top:.25rem!important;}.pt-xxl-2{padding-top:.5rem!important;}.pt-xxl-3{padding-top:1rem!important;}.pt-xxl-4{padding-top:1.5rem!important;}.pt-xxl-5{padding-top:3rem!important;}.pe-xxl-0{padding-right:0!important;}.pe-xxl-1{padding-right:.25rem!important;}.pe-xxl-2{padding-right:.5rem!important;}.pe-xxl-3{padding-right:1rem!important;}.pe-xxl-4{padding-right:1.5rem!important;}.pe-xxl-5{padding-right:3rem!important;}.pb-xxl-0{padding-bottom:0!important;}.pb-xxl-1{padding-bottom:.25rem!important;}.pb-xxl-2{padding-bottom:.5rem!important;}.pb-xxl-3{padding-bottom:1rem!important;}.pb-xxl-4{padding-bottom:1.5rem!important;}.pb-xxl-5{padding-bottom:3rem!important;}.ps-xxl-0{padding-left:0!important;}.ps-xxl-1{padding-left:.25rem!important;}.ps-xxl-2{padding-left:.5rem!important;}.ps-xxl-3{padding-left:1rem!important;}.ps-xxl-4{padding-left:1.5rem!important;}.ps-xxl-5{padding-left:3rem!important;}.text-xxl-start{text-align:left!important;}.text-xxl-end{text-align:right!important;}.text-xxl-center{text-align:center!important;}}@media(min-width:1200px){.fs-1{font-size:2.5rem!important;}.fs-2{font-size:2rem!important;}.fs-3{font-size:1.75rem!important;}.fs-4{font-size:1.5rem!important;}}@media print{.d-print-inline{display:inline!important;}.d-print-inline-block{display:inline-block!important;}.d-print-block{display:block!important;}.d-print-grid{display:grid!important;}.d-print-table{display:table!important;}.d-print-table-row{display:table-row!important;}.d-print-table-cell{display:table-cell!important;}.d-print-flex{display:flex!important;}.d-print-inline-flex{display:inline-flex!important;}.d-print-none{display:none!important;}}:root{--bs-gray-100-rgb:248,249,250;--bs-gray-200-rgb:233,236,239;--bs-gray-300-rgb:222,226,230;--bs-gray-400-rgb:206,212,218;--bs-gray-500-rgb:173,181,189;--bs-gray-600-rgb:108,117,125;--bs-gray-700-rgb:73,80,87;--bs-gray-800-rgb:52,58,64;--bs-gray-900-rgb:33,37,41;--scroll-size:8px;--scroll-radius:0px;--scroll-track:var(--bs-light);--scroll-thumb-color:var(--bs-primary);}html,body{width:100%;height:100%;}.spinner-border{margin-top:3rem;color:#fff;}#logo-container,#animation-container{width:100%;height:100%;background:var(--bs-primary)!important;fill:white;stroke:white;position:absolute;top:0;left:0;bottom:0;right:0;display:flex;align-items:center;justify-content:center;flex-direction:column;}#logo-container svg,#animation-container svg{max-width:200px;}#svg-definitions{display:none;}.logo-viewbox{width:25%;display:block;fill:var(--bs-black);stroke:var(--bs-black);}#splash-viewbox{stroke-miterlimit:5;stroke-dashArray:2000;stroke-dashoffset:2000;fill-opacity:0;}.fade-out{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:fade-out-animation;animation-duration:1s;animation-delay:3s;}.animation-phase-1{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:.2s,2.2s,1.2s;}.animation-phase-2{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:.4s,2.4s,1.4s;}.animation-phase-3{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:.6s,2.6s,1.6s;}.animation-phase-4{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:.8s,2.8s,1.8s;}.animation-phase-5{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:1s,3s,2s;}.animation-phase-6{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:1.2s,3.2s,2.2s;}.animation-phase-7{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:1.4s,3.4s,2.4s;}.animation-phase-8{animation-timing-function:ease-in-out;animation-fill-mode:forwards;animation-iteration:1;animation-name:stroke-draw-animation,stroke-fade-out-animation,fade-in-animation;animation-duration:1.5s,.75s,.75s;animation-delay:1.6s,3.6s,2.6s;}@keyframes stroke-draw-animation{to{stroke-dashOffset:0;}}@keyframes stroke-fade-out-animation{to{stroke-opacity:0;}}@keyframes fade-in-animation{to{fill-opacity:1;}}@keyframes fade-out-animation{to{opacity:0;}}*{padding:0;margin:0;}body{scrollbar-color:var(--scroll-thumb-color,#808080) var(--scroll-track,transparent);scrollbar-width:thin;}body::-webkit-scrollbar{width:var(--scroll-size,10px);height:var(--scroll-size,10px);}body::-webkit-scrollbar-track{background-color:var(--scroll-track,transparent);border-radius:var(--scroll-track-radius,var(--scroll-radius));}body::-webkit-scrollbar-thumb{background-color:var(--scroll-thumb-color,#808080);background-image:var(--scroll-thumb,none);border-radius:var(--scroll-thumb-radius,var(--scroll-radius));}#blazor-error-ui{background:var(--bs-danger);color:var(--bs-light);bottom:0;display:none;left:0;padding:.6rem 1.25rem .7rem 1.25rem;position:fixed;width:100%;z-index:1000;}#blazor-error-ui a{color:var(--bs-light);text-decoration:none;}#blazor-error-ui .reload{font-weight:600;}#blazor-error-ui .dismiss{cursor:pointer;position:absolute;right:.75rem;top:.5rem;}.form-control.invalid{border-color:#f00;}input[type=time]::-webkit-datetime-edit-ampm-field{display:none;}input[type=time]::-webkit-clear-button{-webkit-appearance:none;-moz-appearance:none;-o-appearance:none;-ms-appearance:none;appearance:none;margin:-10px;}.cursor-pointer{cursor:pointer;}.flex-grow{flex-grow:1;}.default.group .port{width:0!important;height:0!important;margin:0!important;}.monaco-normal{height:500px!important;}.monaco-small{height:300px!important;}.monaco-extra-small{height:100px!important;}.bi{width:16px;height:16px;margin-right:8px;display:inline-flex;justify-content:flex-end;align-items:center;}.toast-container{z-index:1!important;position:fixed!important;width:25rem!important;top:0!important;right:0!important;}.end-node circle{stroke-width:3px;}.btn-outline-dark:hover{fill:white;}.mh-50{max-height:50%!important;}.overflow-y-scroll{overflow-y:scroll;}.tab-content{position:absolute;top:0;left:0;right:0;bottom:0;}.node.active rect,.cluster.active rect{fill:rgba(var(--bs-info-rgb),.2);}.node .badge circle,.cluster .badge circle{stroke-width:0;fill:var(--bs-primary);}.node .badge foreignObject,.cluster .badge foreignObject{text-align:center;font-size:8px;color:#fff;}.node .badge foreignObject div,.cluster .badge foreignObject div{display:flex;align-content:center;align-items:center;justify-content:center;}.node .activity-badge.activity-badge--active circle,.cluster .activity-badge.activity-badge--active circle{fill:var(--bs-success);}.node .activity-badge.activity-badge--faulted circle,.cluster .activity-badge.activity-badge--faulted circle{fill:var(--bs-danger);}.node .add-state .add-state__label,.cluster .add-state .add-state__label{font-size:12px;font-weight:bold;}.toolbar .btn{display:flex;align-items:center;justify-content:flex-start;}.edges{pointer-events:none;}.edges .used-for-compensation path{stroke-dasharray:4;}.ghost{opacity:.3;pointer-events:none;}.drop-destination rect,.drop-destination circle,.drop-destination ellipse{stroke:red;stroke-width:2px;stroke-dasharray:2;}.card-metric{box-shadow:2px 2px 10px var(--bs-gray-200);margin:5px;padding:10px;background-color:var(--bs-white);border-radius:5px;transition:.3s linear all;}.card-metric:hover{box-shadow:4px 4px 20px var(--bs-gray-200);transition:.3s linear all;}.card-metric.info{background-color:var(--bs-info);color:var(--bs-white);}.card-metric.danger{background-color:var(--bs-danger);color:var(--bs-white);}.card-metric.success{background-color:var(--bs-success);color:var(--bs-white);}.card-metric.secondary{background-color:var(--bs-secondary);color:var(--bs-white);}.card-metric.warning{background-color:var(--bs-warning);color:var(--bs-white);}.card-metric i{font-size:5em;opacity:.2;}.card-metric .card-metric-value{font-size:32px;display:block;}.card-metric .card-metric-name{font-style:italic;text-transform:capitalize;opacity:.5;display:block;font-size:18px;}table .details{text-align:right;}.upload-page{gap:var(--bs-gutter-x);}.breadcrumb a{text-decoration:none;font-size:.9em;display:flex;align-items:center;justify-content:flex-start;}.breadcrumb a .bi{margin-right:4px;}.vh-90{height:90vh;}.vh-85{height:85vh;}.pxh-150{height:150px;}.form-select.bg-secondary{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23FFFFFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e")!important;} \ No newline at end of file + * Bootstrap Icons v1.11.3 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ +@import url("https://fonts.googleapis.com/css2?family=Inconsolata:wght@200..900&display=swap");@import url("https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;400;500;600;700&display=swap");@font-face{font-display:block;font-family:"bootstrap-icons";src:url("../lib/bootstrap-icons/font/fonts/bootstrap-icons.woff2?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff2"),url("../lib/bootstrap-icons/font/fonts/bootstrap-icons.woff?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff");}.bi::before,[class^=bi-]::before,[class*=" bi-"]::before{display:inline-block;font-family:"bootstrap-icons"!important;font-style:normal;font-weight:normal!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}.bi-123::before{content:"";}.bi-alarm-fill::before{content:"";}.bi-alarm::before{content:"";}.bi-align-bottom::before{content:"";}.bi-align-center::before{content:"";}.bi-align-end::before{content:"";}.bi-align-middle::before{content:"";}.bi-align-start::before{content:"";}.bi-align-top::before{content:"";}.bi-alt::before{content:"";}.bi-app-indicator::before{content:"";}.bi-app::before{content:"";}.bi-archive-fill::before{content:"";}.bi-archive::before{content:"";}.bi-arrow-90deg-down::before{content:"";}.bi-arrow-90deg-left::before{content:"";}.bi-arrow-90deg-right::before{content:"";}.bi-arrow-90deg-up::before{content:"";}.bi-arrow-bar-down::before{content:"";}.bi-arrow-bar-left::before{content:"";}.bi-arrow-bar-right::before{content:"";}.bi-arrow-bar-up::before{content:"";}.bi-arrow-clockwise::before{content:"";}.bi-arrow-counterclockwise::before{content:"";}.bi-arrow-down-circle-fill::before{content:"";}.bi-arrow-down-circle::before{content:"";}.bi-arrow-down-left-circle-fill::before{content:"";}.bi-arrow-down-left-circle::before{content:"";}.bi-arrow-down-left-square-fill::before{content:"";}.bi-arrow-down-left-square::before{content:"";}.bi-arrow-down-left::before{content:"";}.bi-arrow-down-right-circle-fill::before{content:"";}.bi-arrow-down-right-circle::before{content:"";}.bi-arrow-down-right-square-fill::before{content:"";}.bi-arrow-down-right-square::before{content:"";}.bi-arrow-down-right::before{content:"";}.bi-arrow-down-short::before{content:"";}.bi-arrow-down-square-fill::before{content:"";}.bi-arrow-down-square::before{content:"";}.bi-arrow-down-up::before{content:"";}.bi-arrow-down::before{content:"";}.bi-arrow-left-circle-fill::before{content:"";}.bi-arrow-left-circle::before{content:"";}.bi-arrow-left-right::before{content:"";}.bi-arrow-left-short::before{content:"";}.bi-arrow-left-square-fill::before{content:"";}.bi-arrow-left-square::before{content:"";}.bi-arrow-left::before{content:"";}.bi-arrow-repeat::before{content:"";}.bi-arrow-return-left::before{content:"";}.bi-arrow-return-right::before{content:"";}.bi-arrow-right-circle-fill::before{content:"";}.bi-arrow-right-circle::before{content:"";}.bi-arrow-right-short::before{content:"";}.bi-arrow-right-square-fill::before{content:"";}.bi-arrow-right-square::before{content:"";}.bi-arrow-right::before{content:"";}.bi-arrow-up-circle-fill::before{content:"";}.bi-arrow-up-circle::before{content:"";}.bi-arrow-up-left-circle-fill::before{content:"";}.bi-arrow-up-left-circle::before{content:"";}.bi-arrow-up-left-square-fill::before{content:"";}.bi-arrow-up-left-square::before{content:"";}.bi-arrow-up-left::before{content:"";}.bi-arrow-up-right-circle-fill::before{content:"";}.bi-arrow-up-right-circle::before{content:"";}.bi-arrow-up-right-square-fill::before{content:"";}.bi-arrow-up-right-square::before{content:"";}.bi-arrow-up-right::before{content:"";}.bi-arrow-up-short::before{content:"";}.bi-arrow-up-square-fill::before{content:"";}.bi-arrow-up-square::before{content:"";}.bi-arrow-up::before{content:"";}.bi-arrows-angle-contract::before{content:"";}.bi-arrows-angle-expand::before{content:"";}.bi-arrows-collapse::before{content:"";}.bi-arrows-expand::before{content:"";}.bi-arrows-fullscreen::before{content:"";}.bi-arrows-move::before{content:"";}.bi-aspect-ratio-fill::before{content:"";}.bi-aspect-ratio::before{content:"";}.bi-asterisk::before{content:"";}.bi-at::before{content:"";}.bi-award-fill::before{content:"";}.bi-award::before{content:"";}.bi-back::before{content:"";}.bi-backspace-fill::before{content:"";}.bi-backspace-reverse-fill::before{content:"";}.bi-backspace-reverse::before{content:"";}.bi-backspace::before{content:"";}.bi-badge-3d-fill::before{content:"";}.bi-badge-3d::before{content:"";}.bi-badge-4k-fill::before{content:"";}.bi-badge-4k::before{content:"";}.bi-badge-8k-fill::before{content:"";}.bi-badge-8k::before{content:"";}.bi-badge-ad-fill::before{content:"";}.bi-badge-ad::before{content:"";}.bi-badge-ar-fill::before{content:"";}.bi-badge-ar::before{content:"";}.bi-badge-cc-fill::before{content:"";}.bi-badge-cc::before{content:"";}.bi-badge-hd-fill::before{content:"";}.bi-badge-hd::before{content:"";}.bi-badge-tm-fill::before{content:"";}.bi-badge-tm::before{content:"";}.bi-badge-vo-fill::before{content:"";}.bi-badge-vo::before{content:"";}.bi-badge-vr-fill::before{content:"";}.bi-badge-vr::before{content:"";}.bi-badge-wc-fill::before{content:"";}.bi-badge-wc::before{content:"";}.bi-bag-check-fill::before{content:"";}.bi-bag-check::before{content:"";}.bi-bag-dash-fill::before{content:"";}.bi-bag-dash::before{content:"";}.bi-bag-fill::before{content:"";}.bi-bag-plus-fill::before{content:"";}.bi-bag-plus::before{content:"";}.bi-bag-x-fill::before{content:"";}.bi-bag-x::before{content:"";}.bi-bag::before{content:"";}.bi-bar-chart-fill::before{content:"";}.bi-bar-chart-line-fill::before{content:"";}.bi-bar-chart-line::before{content:"";}.bi-bar-chart-steps::before{content:"";}.bi-bar-chart::before{content:"";}.bi-basket-fill::before{content:"";}.bi-basket::before{content:"";}.bi-basket2-fill::before{content:"";}.bi-basket2::before{content:"";}.bi-basket3-fill::before{content:"";}.bi-basket3::before{content:"";}.bi-battery-charging::before{content:"";}.bi-battery-full::before{content:"";}.bi-battery-half::before{content:"";}.bi-battery::before{content:"";}.bi-bell-fill::before{content:"";}.bi-bell::before{content:"";}.bi-bezier::before{content:"";}.bi-bezier2::before{content:"";}.bi-bicycle::before{content:"";}.bi-binoculars-fill::before{content:"";}.bi-binoculars::before{content:"";}.bi-blockquote-left::before{content:"";}.bi-blockquote-right::before{content:"";}.bi-book-fill::before{content:"";}.bi-book-half::before{content:"";}.bi-book::before{content:"";}.bi-bookmark-check-fill::before{content:"";}.bi-bookmark-check::before{content:"";}.bi-bookmark-dash-fill::before{content:"";}.bi-bookmark-dash::before{content:"";}.bi-bookmark-fill::before{content:"";}.bi-bookmark-heart-fill::before{content:"";}.bi-bookmark-heart::before{content:"";}.bi-bookmark-plus-fill::before{content:"";}.bi-bookmark-plus::before{content:"";}.bi-bookmark-star-fill::before{content:"";}.bi-bookmark-star::before{content:"";}.bi-bookmark-x-fill::before{content:"";}.bi-bookmark-x::before{content:"";}.bi-bookmark::before{content:"";}.bi-bookmarks-fill::before{content:"";}.bi-bookmarks::before{content:"";}.bi-bookshelf::before{content:"";}.bi-bootstrap-fill::before{content:"";}.bi-bootstrap-reboot::before{content:"";}.bi-bootstrap::before{content:"";}.bi-border-all::before{content:"";}.bi-border-bottom::before{content:"";}.bi-border-center::before{content:"";}.bi-border-inner::before{content:"";}.bi-border-left::before{content:"";}.bi-border-middle::before{content:"";}.bi-border-outer::before{content:"";}.bi-border-right::before{content:"";}.bi-border-style::before{content:"";}.bi-border-top::before{content:"";}.bi-border-width::before{content:"";}.bi-border::before{content:"";}.bi-bounding-box-circles::before{content:"";}.bi-bounding-box::before{content:"";}.bi-box-arrow-down-left::before{content:"";}.bi-box-arrow-down-right::before{content:"";}.bi-box-arrow-down::before{content:"";}.bi-box-arrow-in-down-left::before{content:"";}.bi-box-arrow-in-down-right::before{content:"";}.bi-box-arrow-in-down::before{content:"";}.bi-box-arrow-in-left::before{content:"";}.bi-box-arrow-in-right::before{content:"";}.bi-box-arrow-in-up-left::before{content:"";}.bi-box-arrow-in-up-right::before{content:"";}.bi-box-arrow-in-up::before{content:"";}.bi-box-arrow-left::before{content:"";}.bi-box-arrow-right::before{content:"";}.bi-box-arrow-up-left::before{content:"";}.bi-box-arrow-up-right::before{content:"";}.bi-box-arrow-up::before{content:"";}.bi-box-seam::before{content:"";}.bi-box::before{content:"";}.bi-braces::before{content:"";}.bi-bricks::before{content:"";}.bi-briefcase-fill::before{content:"";}.bi-briefcase::before{content:"";}.bi-brightness-alt-high-fill::before{content:"";}.bi-brightness-alt-high::before{content:"";}.bi-brightness-alt-low-fill::before{content:"";}.bi-brightness-alt-low::before{content:"";}.bi-brightness-high-fill::before{content:"";}.bi-brightness-high::before{content:"";}.bi-brightness-low-fill::before{content:"";}.bi-brightness-low::before{content:"";}.bi-broadcast-pin::before{content:"";}.bi-broadcast::before{content:"";}.bi-brush-fill::before{content:"";}.bi-brush::before{content:"";}.bi-bucket-fill::before{content:"";}.bi-bucket::before{content:"";}.bi-bug-fill::before{content:"";}.bi-bug::before{content:"";}.bi-building::before{content:"";}.bi-bullseye::before{content:"";}.bi-calculator-fill::before{content:"";}.bi-calculator::before{content:"";}.bi-calendar-check-fill::before{content:"";}.bi-calendar-check::before{content:"";}.bi-calendar-date-fill::before{content:"";}.bi-calendar-date::before{content:"";}.bi-calendar-day-fill::before{content:"";}.bi-calendar-day::before{content:"";}.bi-calendar-event-fill::before{content:"";}.bi-calendar-event::before{content:"";}.bi-calendar-fill::before{content:"";}.bi-calendar-minus-fill::before{content:"";}.bi-calendar-minus::before{content:"";}.bi-calendar-month-fill::before{content:"";}.bi-calendar-month::before{content:"";}.bi-calendar-plus-fill::before{content:"";}.bi-calendar-plus::before{content:"";}.bi-calendar-range-fill::before{content:"";}.bi-calendar-range::before{content:"";}.bi-calendar-week-fill::before{content:"";}.bi-calendar-week::before{content:"";}.bi-calendar-x-fill::before{content:"";}.bi-calendar-x::before{content:"";}.bi-calendar::before{content:"";}.bi-calendar2-check-fill::before{content:"";}.bi-calendar2-check::before{content:"";}.bi-calendar2-date-fill::before{content:"";}.bi-calendar2-date::before{content:"";}.bi-calendar2-day-fill::before{content:"";}.bi-calendar2-day::before{content:"";}.bi-calendar2-event-fill::before{content:"";}.bi-calendar2-event::before{content:"";}.bi-calendar2-fill::before{content:"";}.bi-calendar2-minus-fill::before{content:"";}.bi-calendar2-minus::before{content:"";}.bi-calendar2-month-fill::before{content:"";}.bi-calendar2-month::before{content:"";}.bi-calendar2-plus-fill::before{content:"";}.bi-calendar2-plus::before{content:"";}.bi-calendar2-range-fill::before{content:"";}.bi-calendar2-range::before{content:"";}.bi-calendar2-week-fill::before{content:"";}.bi-calendar2-week::before{content:"";}.bi-calendar2-x-fill::before{content:"";}.bi-calendar2-x::before{content:"";}.bi-calendar2::before{content:"";}.bi-calendar3-event-fill::before{content:"";}.bi-calendar3-event::before{content:"";}.bi-calendar3-fill::before{content:"";}.bi-calendar3-range-fill::before{content:"";}.bi-calendar3-range::before{content:"";}.bi-calendar3-week-fill::before{content:"";}.bi-calendar3-week::before{content:"";}.bi-calendar3::before{content:"";}.bi-calendar4-event::before{content:"";}.bi-calendar4-range::before{content:"";}.bi-calendar4-week::before{content:"";}.bi-calendar4::before{content:"";}.bi-camera-fill::before{content:"";}.bi-camera-reels-fill::before{content:"";}.bi-camera-reels::before{content:"";}.bi-camera-video-fill::before{content:"";}.bi-camera-video-off-fill::before{content:"";}.bi-camera-video-off::before{content:"";}.bi-camera-video::before{content:"";}.bi-camera::before{content:"";}.bi-camera2::before{content:"";}.bi-capslock-fill::before{content:"";}.bi-capslock::before{content:"";}.bi-card-checklist::before{content:"";}.bi-card-heading::before{content:"";}.bi-card-image::before{content:"";}.bi-card-list::before{content:"";}.bi-card-text::before{content:"";}.bi-caret-down-fill::before{content:"";}.bi-caret-down-square-fill::before{content:"";}.bi-caret-down-square::before{content:"";}.bi-caret-down::before{content:"";}.bi-caret-left-fill::before{content:"";}.bi-caret-left-square-fill::before{content:"";}.bi-caret-left-square::before{content:"";}.bi-caret-left::before{content:"";}.bi-caret-right-fill::before{content:"";}.bi-caret-right-square-fill::before{content:"";}.bi-caret-right-square::before{content:"";}.bi-caret-right::before{content:"";}.bi-caret-up-fill::before{content:"";}.bi-caret-up-square-fill::before{content:"";}.bi-caret-up-square::before{content:"";}.bi-caret-up::before{content:"";}.bi-cart-check-fill::before{content:"";}.bi-cart-check::before{content:"";}.bi-cart-dash-fill::before{content:"";}.bi-cart-dash::before{content:"";}.bi-cart-fill::before{content:"";}.bi-cart-plus-fill::before{content:"";}.bi-cart-plus::before{content:"";}.bi-cart-x-fill::before{content:"";}.bi-cart-x::before{content:"";}.bi-cart::before{content:"";}.bi-cart2::before{content:"";}.bi-cart3::before{content:"";}.bi-cart4::before{content:"";}.bi-cash-stack::before{content:"";}.bi-cash::before{content:"";}.bi-cast::before{content:"";}.bi-chat-dots-fill::before{content:"";}.bi-chat-dots::before{content:"";}.bi-chat-fill::before{content:"";}.bi-chat-left-dots-fill::before{content:"";}.bi-chat-left-dots::before{content:"";}.bi-chat-left-fill::before{content:"";}.bi-chat-left-quote-fill::before{content:"";}.bi-chat-left-quote::before{content:"";}.bi-chat-left-text-fill::before{content:"";}.bi-chat-left-text::before{content:"";}.bi-chat-left::before{content:"";}.bi-chat-quote-fill::before{content:"";}.bi-chat-quote::before{content:"";}.bi-chat-right-dots-fill::before{content:"";}.bi-chat-right-dots::before{content:"";}.bi-chat-right-fill::before{content:"";}.bi-chat-right-quote-fill::before{content:"";}.bi-chat-right-quote::before{content:"";}.bi-chat-right-text-fill::before{content:"";}.bi-chat-right-text::before{content:"";}.bi-chat-right::before{content:"";}.bi-chat-square-dots-fill::before{content:"";}.bi-chat-square-dots::before{content:"";}.bi-chat-square-fill::before{content:"";}.bi-chat-square-quote-fill::before{content:"";}.bi-chat-square-quote::before{content:"";}.bi-chat-square-text-fill::before{content:"";}.bi-chat-square-text::before{content:"";}.bi-chat-square::before{content:"";}.bi-chat-text-fill::before{content:"";}.bi-chat-text::before{content:"";}.bi-chat::before{content:"";}.bi-check-all::before{content:"";}.bi-check-circle-fill::before{content:"";}.bi-check-circle::before{content:"";}.bi-check-square-fill::before{content:"";}.bi-check-square::before{content:"";}.bi-check::before{content:"";}.bi-check2-all::before{content:"";}.bi-check2-circle::before{content:"";}.bi-check2-square::before{content:"";}.bi-check2::before{content:"";}.bi-chevron-bar-contract::before{content:"";}.bi-chevron-bar-down::before{content:"";}.bi-chevron-bar-expand::before{content:"";}.bi-chevron-bar-left::before{content:"";}.bi-chevron-bar-right::before{content:"";}.bi-chevron-bar-up::before{content:"";}.bi-chevron-compact-down::before{content:"";}.bi-chevron-compact-left::before{content:"";}.bi-chevron-compact-right::before{content:"";}.bi-chevron-compact-up::before{content:"";}.bi-chevron-contract::before{content:"";}.bi-chevron-double-down::before{content:"";}.bi-chevron-double-left::before{content:"";}.bi-chevron-double-right::before{content:"";}.bi-chevron-double-up::before{content:"";}.bi-chevron-down::before{content:"";}.bi-chevron-expand::before{content:"";}.bi-chevron-left::before{content:"";}.bi-chevron-right::before{content:"";}.bi-chevron-up::before{content:"";}.bi-circle-fill::before{content:"";}.bi-circle-half::before{content:"";}.bi-circle-square::before{content:"";}.bi-circle::before{content:"";}.bi-clipboard-check::before{content:"";}.bi-clipboard-data::before{content:"";}.bi-clipboard-minus::before{content:"";}.bi-clipboard-plus::before{content:"";}.bi-clipboard-x::before{content:"";}.bi-clipboard::before{content:"";}.bi-clock-fill::before{content:"";}.bi-clock-history::before{content:"";}.bi-clock::before{content:"";}.bi-cloud-arrow-down-fill::before{content:"";}.bi-cloud-arrow-down::before{content:"";}.bi-cloud-arrow-up-fill::before{content:"";}.bi-cloud-arrow-up::before{content:"";}.bi-cloud-check-fill::before{content:"";}.bi-cloud-check::before{content:"";}.bi-cloud-download-fill::before{content:"";}.bi-cloud-download::before{content:"";}.bi-cloud-drizzle-fill::before{content:"";}.bi-cloud-drizzle::before{content:"";}.bi-cloud-fill::before{content:"";}.bi-cloud-fog-fill::before{content:"";}.bi-cloud-fog::before{content:"";}.bi-cloud-fog2-fill::before{content:"";}.bi-cloud-fog2::before{content:"";}.bi-cloud-hail-fill::before{content:"";}.bi-cloud-hail::before{content:"";}.bi-cloud-haze-fill::before{content:"";}.bi-cloud-haze::before{content:"";}.bi-cloud-haze2-fill::before{content:"";}.bi-cloud-lightning-fill::before{content:"";}.bi-cloud-lightning-rain-fill::before{content:"";}.bi-cloud-lightning-rain::before{content:"";}.bi-cloud-lightning::before{content:"";}.bi-cloud-minus-fill::before{content:"";}.bi-cloud-minus::before{content:"";}.bi-cloud-moon-fill::before{content:"";}.bi-cloud-moon::before{content:"";}.bi-cloud-plus-fill::before{content:"";}.bi-cloud-plus::before{content:"";}.bi-cloud-rain-fill::before{content:"";}.bi-cloud-rain-heavy-fill::before{content:"";}.bi-cloud-rain-heavy::before{content:"";}.bi-cloud-rain::before{content:"";}.bi-cloud-slash-fill::before{content:"";}.bi-cloud-slash::before{content:"";}.bi-cloud-sleet-fill::before{content:"";}.bi-cloud-sleet::before{content:"";}.bi-cloud-snow-fill::before{content:"";}.bi-cloud-snow::before{content:"";}.bi-cloud-sun-fill::before{content:"";}.bi-cloud-sun::before{content:"";}.bi-cloud-upload-fill::before{content:"";}.bi-cloud-upload::before{content:"";}.bi-cloud::before{content:"";}.bi-clouds-fill::before{content:"";}.bi-clouds::before{content:"";}.bi-cloudy-fill::before{content:"";}.bi-cloudy::before{content:"";}.bi-code-slash::before{content:"";}.bi-code-square::before{content:"";}.bi-code::before{content:"";}.bi-collection-fill::before{content:"";}.bi-collection-play-fill::before{content:"";}.bi-collection-play::before{content:"";}.bi-collection::before{content:"";}.bi-columns-gap::before{content:"";}.bi-columns::before{content:"";}.bi-command::before{content:"";}.bi-compass-fill::before{content:"";}.bi-compass::before{content:"";}.bi-cone-striped::before{content:"";}.bi-cone::before{content:"";}.bi-controller::before{content:"";}.bi-cpu-fill::before{content:"";}.bi-cpu::before{content:"";}.bi-credit-card-2-back-fill::before{content:"";}.bi-credit-card-2-back::before{content:"";}.bi-credit-card-2-front-fill::before{content:"";}.bi-credit-card-2-front::before{content:"";}.bi-credit-card-fill::before{content:"";}.bi-credit-card::before{content:"";}.bi-crop::before{content:"";}.bi-cup-fill::before{content:"";}.bi-cup-straw::before{content:"";}.bi-cup::before{content:"";}.bi-cursor-fill::before{content:"";}.bi-cursor-text::before{content:"";}.bi-cursor::before{content:"";}.bi-dash-circle-dotted::before{content:"";}.bi-dash-circle-fill::before{content:"";}.bi-dash-circle::before{content:"";}.bi-dash-square-dotted::before{content:"";}.bi-dash-square-fill::before{content:"";}.bi-dash-square::before{content:"";}.bi-dash::before{content:"";}.bi-diagram-2-fill::before{content:"";}.bi-diagram-2::before{content:"";}.bi-diagram-3-fill::before{content:"";}.bi-diagram-3::before{content:"";}.bi-diamond-fill::before{content:"";}.bi-diamond-half::before{content:"";}.bi-diamond::before{content:"";}.bi-dice-1-fill::before{content:"";}.bi-dice-1::before{content:"";}.bi-dice-2-fill::before{content:"";}.bi-dice-2::before{content:"";}.bi-dice-3-fill::before{content:"";}.bi-dice-3::before{content:"";}.bi-dice-4-fill::before{content:"";}.bi-dice-4::before{content:"";}.bi-dice-5-fill::before{content:"";}.bi-dice-5::before{content:"";}.bi-dice-6-fill::before{content:"";}.bi-dice-6::before{content:"";}.bi-disc-fill::before{content:"";}.bi-disc::before{content:"";}.bi-discord::before{content:"";}.bi-display-fill::before{content:"";}.bi-display::before{content:"";}.bi-distribute-horizontal::before{content:"";}.bi-distribute-vertical::before{content:"";}.bi-door-closed-fill::before{content:"";}.bi-door-closed::before{content:"";}.bi-door-open-fill::before{content:"";}.bi-door-open::before{content:"";}.bi-dot::before{content:"";}.bi-download::before{content:"";}.bi-droplet-fill::before{content:"";}.bi-droplet-half::before{content:"";}.bi-droplet::before{content:"";}.bi-earbuds::before{content:"";}.bi-easel-fill::before{content:"";}.bi-easel::before{content:"";}.bi-egg-fill::before{content:"";}.bi-egg-fried::before{content:"";}.bi-egg::before{content:"";}.bi-eject-fill::before{content:"";}.bi-eject::before{content:"";}.bi-emoji-angry-fill::before{content:"";}.bi-emoji-angry::before{content:"";}.bi-emoji-dizzy-fill::before{content:"";}.bi-emoji-dizzy::before{content:"";}.bi-emoji-expressionless-fill::before{content:"";}.bi-emoji-expressionless::before{content:"";}.bi-emoji-frown-fill::before{content:"";}.bi-emoji-frown::before{content:"";}.bi-emoji-heart-eyes-fill::before{content:"";}.bi-emoji-heart-eyes::before{content:"";}.bi-emoji-laughing-fill::before{content:"";}.bi-emoji-laughing::before{content:"";}.bi-emoji-neutral-fill::before{content:"";}.bi-emoji-neutral::before{content:"";}.bi-emoji-smile-fill::before{content:"";}.bi-emoji-smile-upside-down-fill::before{content:"";}.bi-emoji-smile-upside-down::before{content:"";}.bi-emoji-smile::before{content:"";}.bi-emoji-sunglasses-fill::before{content:"";}.bi-emoji-sunglasses::before{content:"";}.bi-emoji-wink-fill::before{content:"";}.bi-emoji-wink::before{content:"";}.bi-envelope-fill::before{content:"";}.bi-envelope-open-fill::before{content:"";}.bi-envelope-open::before{content:"";}.bi-envelope::before{content:"";}.bi-eraser-fill::before{content:"";}.bi-eraser::before{content:"";}.bi-exclamation-circle-fill::before{content:"";}.bi-exclamation-circle::before{content:"";}.bi-exclamation-diamond-fill::before{content:"";}.bi-exclamation-diamond::before{content:"";}.bi-exclamation-octagon-fill::before{content:"";}.bi-exclamation-octagon::before{content:"";}.bi-exclamation-square-fill::before{content:"";}.bi-exclamation-square::before{content:"";}.bi-exclamation-triangle-fill::before{content:"";}.bi-exclamation-triangle::before{content:"";}.bi-exclamation::before{content:"";}.bi-exclude::before{content:"";}.bi-eye-fill::before{content:"";}.bi-eye-slash-fill::before{content:"";}.bi-eye-slash::before{content:"";}.bi-eye::before{content:"";}.bi-eyedropper::before{content:"";}.bi-eyeglasses::before{content:"";}.bi-facebook::before{content:"";}.bi-file-arrow-down-fill::before{content:"";}.bi-file-arrow-down::before{content:"";}.bi-file-arrow-up-fill::before{content:"";}.bi-file-arrow-up::before{content:"";}.bi-file-bar-graph-fill::before{content:"";}.bi-file-bar-graph::before{content:"";}.bi-file-binary-fill::before{content:"";}.bi-file-binary::before{content:"";}.bi-file-break-fill::before{content:"";}.bi-file-break::before{content:"";}.bi-file-check-fill::before{content:"";}.bi-file-check::before{content:"";}.bi-file-code-fill::before{content:"";}.bi-file-code::before{content:"";}.bi-file-diff-fill::before{content:"";}.bi-file-diff::before{content:"";}.bi-file-earmark-arrow-down-fill::before{content:"";}.bi-file-earmark-arrow-down::before{content:"";}.bi-file-earmark-arrow-up-fill::before{content:"";}.bi-file-earmark-arrow-up::before{content:"";}.bi-file-earmark-bar-graph-fill::before{content:"";}.bi-file-earmark-bar-graph::before{content:"";}.bi-file-earmark-binary-fill::before{content:"";}.bi-file-earmark-binary::before{content:"";}.bi-file-earmark-break-fill::before{content:"";}.bi-file-earmark-break::before{content:"";}.bi-file-earmark-check-fill::before{content:"";}.bi-file-earmark-check::before{content:"";}.bi-file-earmark-code-fill::before{content:"";}.bi-file-earmark-code::before{content:"";}.bi-file-earmark-diff-fill::before{content:"";}.bi-file-earmark-diff::before{content:"";}.bi-file-earmark-easel-fill::before{content:"";}.bi-file-earmark-easel::before{content:"";}.bi-file-earmark-excel-fill::before{content:"";}.bi-file-earmark-excel::before{content:"";}.bi-file-earmark-fill::before{content:"";}.bi-file-earmark-font-fill::before{content:"";}.bi-file-earmark-font::before{content:"";}.bi-file-earmark-image-fill::before{content:"";}.bi-file-earmark-image::before{content:"";}.bi-file-earmark-lock-fill::before{content:"";}.bi-file-earmark-lock::before{content:"";}.bi-file-earmark-lock2-fill::before{content:"";}.bi-file-earmark-lock2::before{content:"";}.bi-file-earmark-medical-fill::before{content:"";}.bi-file-earmark-medical::before{content:"";}.bi-file-earmark-minus-fill::before{content:"";}.bi-file-earmark-minus::before{content:"";}.bi-file-earmark-music-fill::before{content:"";}.bi-file-earmark-music::before{content:"";}.bi-file-earmark-person-fill::before{content:"";}.bi-file-earmark-person::before{content:"";}.bi-file-earmark-play-fill::before{content:"";}.bi-file-earmark-play::before{content:"";}.bi-file-earmark-plus-fill::before{content:"";}.bi-file-earmark-plus::before{content:"";}.bi-file-earmark-post-fill::before{content:"";}.bi-file-earmark-post::before{content:"";}.bi-file-earmark-ppt-fill::before{content:"";}.bi-file-earmark-ppt::before{content:"";}.bi-file-earmark-richtext-fill::before{content:"";}.bi-file-earmark-richtext::before{content:"";}.bi-file-earmark-ruled-fill::before{content:"";}.bi-file-earmark-ruled::before{content:"";}.bi-file-earmark-slides-fill::before{content:"";}.bi-file-earmark-slides::before{content:"";}.bi-file-earmark-spreadsheet-fill::before{content:"";}.bi-file-earmark-spreadsheet::before{content:"";}.bi-file-earmark-text-fill::before{content:"";}.bi-file-earmark-text::before{content:"";}.bi-file-earmark-word-fill::before{content:"";}.bi-file-earmark-word::before{content:"";}.bi-file-earmark-x-fill::before{content:"";}.bi-file-earmark-x::before{content:"";}.bi-file-earmark-zip-fill::before{content:"";}.bi-file-earmark-zip::before{content:"";}.bi-file-earmark::before{content:"";}.bi-file-easel-fill::before{content:"";}.bi-file-easel::before{content:"";}.bi-file-excel-fill::before{content:"";}.bi-file-excel::before{content:"";}.bi-file-fill::before{content:"";}.bi-file-font-fill::before{content:"";}.bi-file-font::before{content:"";}.bi-file-image-fill::before{content:"";}.bi-file-image::before{content:"";}.bi-file-lock-fill::before{content:"";}.bi-file-lock::before{content:"";}.bi-file-lock2-fill::before{content:"";}.bi-file-lock2::before{content:"";}.bi-file-medical-fill::before{content:"";}.bi-file-medical::before{content:"";}.bi-file-minus-fill::before{content:"";}.bi-file-minus::before{content:"";}.bi-file-music-fill::before{content:"";}.bi-file-music::before{content:"";}.bi-file-person-fill::before{content:"";}.bi-file-person::before{content:"";}.bi-file-play-fill::before{content:"";}.bi-file-play::before{content:"";}.bi-file-plus-fill::before{content:"";}.bi-file-plus::before{content:"";}.bi-file-post-fill::before{content:"";}.bi-file-post::before{content:"";}.bi-file-ppt-fill::before{content:"";}.bi-file-ppt::before{content:"";}.bi-file-richtext-fill::before{content:"";}.bi-file-richtext::before{content:"";}.bi-file-ruled-fill::before{content:"";}.bi-file-ruled::before{content:"";}.bi-file-slides-fill::before{content:"";}.bi-file-slides::before{content:"";}.bi-file-spreadsheet-fill::before{content:"";}.bi-file-spreadsheet::before{content:"";}.bi-file-text-fill::before{content:"";}.bi-file-text::before{content:"";}.bi-file-word-fill::before{content:"";}.bi-file-word::before{content:"";}.bi-file-x-fill::before{content:"";}.bi-file-x::before{content:"";}.bi-file-zip-fill::before{content:"";}.bi-file-zip::before{content:"";}.bi-file::before{content:"";}.bi-files-alt::before{content:"";}.bi-files::before{content:"";}.bi-film::before{content:"";}.bi-filter-circle-fill::before{content:"";}.bi-filter-circle::before{content:"";}.bi-filter-left::before{content:"";}.bi-filter-right::before{content:"";}.bi-filter-square-fill::before{content:"";}.bi-filter-square::before{content:"";}.bi-filter::before{content:"";}.bi-flag-fill::before{content:"";}.bi-flag::before{content:"";}.bi-flower1::before{content:"";}.bi-flower2::before{content:"";}.bi-flower3::before{content:"";}.bi-folder-check::before{content:"";}.bi-folder-fill::before{content:"";}.bi-folder-minus::before{content:"";}.bi-folder-plus::before{content:"";}.bi-folder-symlink-fill::before{content:"";}.bi-folder-symlink::before{content:"";}.bi-folder-x::before{content:"";}.bi-folder::before{content:"";}.bi-folder2-open::before{content:"";}.bi-folder2::before{content:"";}.bi-fonts::before{content:"";}.bi-forward-fill::before{content:"";}.bi-forward::before{content:"";}.bi-front::before{content:"";}.bi-fullscreen-exit::before{content:"";}.bi-fullscreen::before{content:"";}.bi-funnel-fill::before{content:"";}.bi-funnel::before{content:"";}.bi-gear-fill::before{content:"";}.bi-gear-wide-connected::before{content:"";}.bi-gear-wide::before{content:"";}.bi-gear::before{content:"";}.bi-gem::before{content:"";}.bi-geo-alt-fill::before{content:"";}.bi-geo-alt::before{content:"";}.bi-geo-fill::before{content:"";}.bi-geo::before{content:"";}.bi-gift-fill::before{content:"";}.bi-gift::before{content:"";}.bi-github::before{content:"";}.bi-globe::before{content:"";}.bi-globe2::before{content:"";}.bi-google::before{content:"";}.bi-graph-down::before{content:"";}.bi-graph-up::before{content:"";}.bi-grid-1x2-fill::before{content:"";}.bi-grid-1x2::before{content:"";}.bi-grid-3x2-gap-fill::before{content:"";}.bi-grid-3x2-gap::before{content:"";}.bi-grid-3x2::before{content:"";}.bi-grid-3x3-gap-fill::before{content:"";}.bi-grid-3x3-gap::before{content:"";}.bi-grid-3x3::before{content:"";}.bi-grid-fill::before{content:"";}.bi-grid::before{content:"";}.bi-grip-horizontal::before{content:"";}.bi-grip-vertical::before{content:"";}.bi-hammer::before{content:"";}.bi-hand-index-fill::before{content:"";}.bi-hand-index-thumb-fill::before{content:"";}.bi-hand-index-thumb::before{content:"";}.bi-hand-index::before{content:"";}.bi-hand-thumbs-down-fill::before{content:"";}.bi-hand-thumbs-down::before{content:"";}.bi-hand-thumbs-up-fill::before{content:"";}.bi-hand-thumbs-up::before{content:"";}.bi-handbag-fill::before{content:"";}.bi-handbag::before{content:"";}.bi-hash::before{content:"";}.bi-hdd-fill::before{content:"";}.bi-hdd-network-fill::before{content:"";}.bi-hdd-network::before{content:"";}.bi-hdd-rack-fill::before{content:"";}.bi-hdd-rack::before{content:"";}.bi-hdd-stack-fill::before{content:"";}.bi-hdd-stack::before{content:"";}.bi-hdd::before{content:"";}.bi-headphones::before{content:"";}.bi-headset::before{content:"";}.bi-heart-fill::before{content:"";}.bi-heart-half::before{content:"";}.bi-heart::before{content:"";}.bi-heptagon-fill::before{content:"";}.bi-heptagon-half::before{content:"";}.bi-heptagon::before{content:"";}.bi-hexagon-fill::before{content:"";}.bi-hexagon-half::before{content:"";}.bi-hexagon::before{content:"";}.bi-hourglass-bottom::before{content:"";}.bi-hourglass-split::before{content:"";}.bi-hourglass-top::before{content:"";}.bi-hourglass::before{content:"";}.bi-house-door-fill::before{content:"";}.bi-house-door::before{content:"";}.bi-house-fill::before{content:"";}.bi-house::before{content:"";}.bi-hr::before{content:"";}.bi-hurricane::before{content:"";}.bi-image-alt::before{content:"";}.bi-image-fill::before{content:"";}.bi-image::before{content:"";}.bi-images::before{content:"";}.bi-inbox-fill::before{content:"";}.bi-inbox::before{content:"";}.bi-inboxes-fill::before{content:"";}.bi-inboxes::before{content:"";}.bi-info-circle-fill::before{content:"";}.bi-info-circle::before{content:"";}.bi-info-square-fill::before{content:"";}.bi-info-square::before{content:"";}.bi-info::before{content:"";}.bi-input-cursor-text::before{content:"";}.bi-input-cursor::before{content:"";}.bi-instagram::before{content:"";}.bi-intersect::before{content:"";}.bi-journal-album::before{content:"";}.bi-journal-arrow-down::before{content:"";}.bi-journal-arrow-up::before{content:"";}.bi-journal-bookmark-fill::before{content:"";}.bi-journal-bookmark::before{content:"";}.bi-journal-check::before{content:"";}.bi-journal-code::before{content:"";}.bi-journal-medical::before{content:"";}.bi-journal-minus::before{content:"";}.bi-journal-plus::before{content:"";}.bi-journal-richtext::before{content:"";}.bi-journal-text::before{content:"";}.bi-journal-x::before{content:"";}.bi-journal::before{content:"";}.bi-journals::before{content:"";}.bi-joystick::before{content:"";}.bi-justify-left::before{content:"";}.bi-justify-right::before{content:"";}.bi-justify::before{content:"";}.bi-kanban-fill::before{content:"";}.bi-kanban::before{content:"";}.bi-key-fill::before{content:"";}.bi-key::before{content:"";}.bi-keyboard-fill::before{content:"";}.bi-keyboard::before{content:"";}.bi-ladder::before{content:"";}.bi-lamp-fill::before{content:"";}.bi-lamp::before{content:"";}.bi-laptop-fill::before{content:"";}.bi-laptop::before{content:"";}.bi-layer-backward::before{content:"";}.bi-layer-forward::before{content:"";}.bi-layers-fill::before{content:"";}.bi-layers-half::before{content:"";}.bi-layers::before{content:"";}.bi-layout-sidebar-inset-reverse::before{content:"";}.bi-layout-sidebar-inset::before{content:"";}.bi-layout-sidebar-reverse::before{content:"";}.bi-layout-sidebar::before{content:"";}.bi-layout-split::before{content:"";}.bi-layout-text-sidebar-reverse::before{content:"";}.bi-layout-text-sidebar::before{content:"";}.bi-layout-text-window-reverse::before{content:"";}.bi-layout-text-window::before{content:"";}.bi-layout-three-columns::before{content:"";}.bi-layout-wtf::before{content:"";}.bi-life-preserver::before{content:"";}.bi-lightbulb-fill::before{content:"";}.bi-lightbulb-off-fill::before{content:"";}.bi-lightbulb-off::before{content:"";}.bi-lightbulb::before{content:"";}.bi-lightning-charge-fill::before{content:"";}.bi-lightning-charge::before{content:"";}.bi-lightning-fill::before{content:"";}.bi-lightning::before{content:"";}.bi-link-45deg::before{content:"";}.bi-link::before{content:"";}.bi-linkedin::before{content:"";}.bi-list-check::before{content:"";}.bi-list-nested::before{content:"";}.bi-list-ol::before{content:"";}.bi-list-stars::before{content:"";}.bi-list-task::before{content:"";}.bi-list-ul::before{content:"";}.bi-list::before{content:"";}.bi-lock-fill::before{content:"";}.bi-lock::before{content:"";}.bi-mailbox::before{content:"";}.bi-mailbox2::before{content:"";}.bi-map-fill::before{content:"";}.bi-map::before{content:"";}.bi-markdown-fill::before{content:"";}.bi-markdown::before{content:"";}.bi-mask::before{content:"";}.bi-megaphone-fill::before{content:"";}.bi-megaphone::before{content:"";}.bi-menu-app-fill::before{content:"";}.bi-menu-app::before{content:"";}.bi-menu-button-fill::before{content:"";}.bi-menu-button-wide-fill::before{content:"";}.bi-menu-button-wide::before{content:"";}.bi-menu-button::before{content:"";}.bi-menu-down::before{content:"";}.bi-menu-up::before{content:"";}.bi-mic-fill::before{content:"";}.bi-mic-mute-fill::before{content:"";}.bi-mic-mute::before{content:"";}.bi-mic::before{content:"";}.bi-minecart-loaded::before{content:"";}.bi-minecart::before{content:"";}.bi-moisture::before{content:"";}.bi-moon-fill::before{content:"";}.bi-moon-stars-fill::before{content:"";}.bi-moon-stars::before{content:"";}.bi-moon::before{content:"";}.bi-mouse-fill::before{content:"";}.bi-mouse::before{content:"";}.bi-mouse2-fill::before{content:"";}.bi-mouse2::before{content:"";}.bi-mouse3-fill::before{content:"";}.bi-mouse3::before{content:"";}.bi-music-note-beamed::before{content:"";}.bi-music-note-list::before{content:"";}.bi-music-note::before{content:"";}.bi-music-player-fill::before{content:"";}.bi-music-player::before{content:"";}.bi-newspaper::before{content:"";}.bi-node-minus-fill::before{content:"";}.bi-node-minus::before{content:"";}.bi-node-plus-fill::before{content:"";}.bi-node-plus::before{content:"";}.bi-nut-fill::before{content:"";}.bi-nut::before{content:"";}.bi-octagon-fill::before{content:"";}.bi-octagon-half::before{content:"";}.bi-octagon::before{content:"";}.bi-option::before{content:"";}.bi-outlet::before{content:"";}.bi-paint-bucket::before{content:"";}.bi-palette-fill::before{content:"";}.bi-palette::before{content:"";}.bi-palette2::before{content:"";}.bi-paperclip::before{content:"";}.bi-paragraph::before{content:"";}.bi-patch-check-fill::before{content:"";}.bi-patch-check::before{content:"";}.bi-patch-exclamation-fill::before{content:"";}.bi-patch-exclamation::before{content:"";}.bi-patch-minus-fill::before{content:"";}.bi-patch-minus::before{content:"";}.bi-patch-plus-fill::before{content:"";}.bi-patch-plus::before{content:"";}.bi-patch-question-fill::before{content:"";}.bi-patch-question::before{content:"";}.bi-pause-btn-fill::before{content:"";}.bi-pause-btn::before{content:"";}.bi-pause-circle-fill::before{content:"";}.bi-pause-circle::before{content:"";}.bi-pause-fill::before{content:"";}.bi-pause::before{content:"";}.bi-peace-fill::before{content:"";}.bi-peace::before{content:"";}.bi-pen-fill::before{content:"";}.bi-pen::before{content:"";}.bi-pencil-fill::before{content:"";}.bi-pencil-square::before{content:"";}.bi-pencil::before{content:"";}.bi-pentagon-fill::before{content:"";}.bi-pentagon-half::before{content:"";}.bi-pentagon::before{content:"";}.bi-people-fill::before{content:"";}.bi-people::before{content:"";}.bi-percent::before{content:"";}.bi-person-badge-fill::before{content:"";}.bi-person-badge::before{content:"";}.bi-person-bounding-box::before{content:"";}.bi-person-check-fill::before{content:"";}.bi-person-check::before{content:"";}.bi-person-circle::before{content:"";}.bi-person-dash-fill::before{content:"";}.bi-person-dash::before{content:"";}.bi-person-fill::before{content:"";}.bi-person-lines-fill::before{content:"";}.bi-person-plus-fill::before{content:"";}.bi-person-plus::before{content:"";}.bi-person-square::before{content:"";}.bi-person-x-fill::before{content:"";}.bi-person-x::before{content:"";}.bi-person::before{content:"";}.bi-phone-fill::before{content:"";}.bi-phone-landscape-fill::before{content:"";}.bi-phone-landscape::before{content:"";}.bi-phone-vibrate-fill::before{content:"";}.bi-phone-vibrate::before{content:"";}.bi-phone::before{content:"";}.bi-pie-chart-fill::before{content:"";}.bi-pie-chart::before{content:"";}.bi-pin-angle-fill::before{content:"";}.bi-pin-angle::before{content:"";}.bi-pin-fill::before{content:"";}.bi-pin::before{content:"";}.bi-pip-fill::before{content:"";}.bi-pip::before{content:"";}.bi-play-btn-fill::before{content:"";}.bi-play-btn::before{content:"";}.bi-play-circle-fill::before{content:"";}.bi-play-circle::before{content:"";}.bi-play-fill::before{content:"";}.bi-play::before{content:"";}.bi-plug-fill::before{content:"";}.bi-plug::before{content:"";}.bi-plus-circle-dotted::before{content:"";}.bi-plus-circle-fill::before{content:"";}.bi-plus-circle::before{content:"";}.bi-plus-square-dotted::before{content:"";}.bi-plus-square-fill::before{content:"";}.bi-plus-square::before{content:"";}.bi-plus::before{content:"";}.bi-power::before{content:"";}.bi-printer-fill::before{content:"";}.bi-printer::before{content:"";}.bi-puzzle-fill::before{content:"";}.bi-puzzle::before{content:"";}.bi-question-circle-fill::before{content:"";}.bi-question-circle::before{content:"";}.bi-question-diamond-fill::before{content:"";}.bi-question-diamond::before{content:"";}.bi-question-octagon-fill::before{content:"";}.bi-question-octagon::before{content:"";}.bi-question-square-fill::before{content:"";}.bi-question-square::before{content:"";}.bi-question::before{content:"";}.bi-rainbow::before{content:"";}.bi-receipt-cutoff::before{content:"";}.bi-receipt::before{content:"";}.bi-reception-0::before{content:"";}.bi-reception-1::before{content:"";}.bi-reception-2::before{content:"";}.bi-reception-3::before{content:"";}.bi-reception-4::before{content:"";}.bi-record-btn-fill::before{content:"";}.bi-record-btn::before{content:"";}.bi-record-circle-fill::before{content:"";}.bi-record-circle::before{content:"";}.bi-record-fill::before{content:"";}.bi-record::before{content:"";}.bi-record2-fill::before{content:"";}.bi-record2::before{content:"";}.bi-reply-all-fill::before{content:"";}.bi-reply-all::before{content:"";}.bi-reply-fill::before{content:"";}.bi-reply::before{content:"";}.bi-rss-fill::before{content:"";}.bi-rss::before{content:"";}.bi-rulers::before{content:"";}.bi-save-fill::before{content:"";}.bi-save::before{content:"";}.bi-save2-fill::before{content:"";}.bi-save2::before{content:"";}.bi-scissors::before{content:"";}.bi-screwdriver::before{content:"";}.bi-search::before{content:"";}.bi-segmented-nav::before{content:"";}.bi-server::before{content:"";}.bi-share-fill::before{content:"";}.bi-share::before{content:"";}.bi-shield-check::before{content:"";}.bi-shield-exclamation::before{content:"";}.bi-shield-fill-check::before{content:"";}.bi-shield-fill-exclamation::before{content:"";}.bi-shield-fill-minus::before{content:"";}.bi-shield-fill-plus::before{content:"";}.bi-shield-fill-x::before{content:"";}.bi-shield-fill::before{content:"";}.bi-shield-lock-fill::before{content:"";}.bi-shield-lock::before{content:"";}.bi-shield-minus::before{content:"";}.bi-shield-plus::before{content:"";}.bi-shield-shaded::before{content:"";}.bi-shield-slash-fill::before{content:"";}.bi-shield-slash::before{content:"";}.bi-shield-x::before{content:"";}.bi-shield::before{content:"";}.bi-shift-fill::before{content:"";}.bi-shift::before{content:"";}.bi-shop-window::before{content:"";}.bi-shop::before{content:"";}.bi-shuffle::before{content:"";}.bi-signpost-2-fill::before{content:"";}.bi-signpost-2::before{content:"";}.bi-signpost-fill::before{content:"";}.bi-signpost-split-fill::before{content:"";}.bi-signpost-split::before{content:"";}.bi-signpost::before{content:"";}.bi-sim-fill::before{content:"";}.bi-sim::before{content:"";}.bi-skip-backward-btn-fill::before{content:"";}.bi-skip-backward-btn::before{content:"";}.bi-skip-backward-circle-fill::before{content:"";}.bi-skip-backward-circle::before{content:"";}.bi-skip-backward-fill::before{content:"";}.bi-skip-backward::before{content:"";}.bi-skip-end-btn-fill::before{content:"";}.bi-skip-end-btn::before{content:"";}.bi-skip-end-circle-fill::before{content:"";}.bi-skip-end-circle::before{content:"";}.bi-skip-end-fill::before{content:"";}.bi-skip-end::before{content:"";}.bi-skip-forward-btn-fill::before{content:"";}.bi-skip-forward-btn::before{content:"";}.bi-skip-forward-circle-fill::before{content:"";}.bi-skip-forward-circle::before{content:"";}.bi-skip-forward-fill::before{content:"";}.bi-skip-forward::before{content:"";}.bi-skip-start-btn-fill::before{content:"";}.bi-skip-start-btn::before{content:"";}.bi-skip-start-circle-fill::before{content:"";}.bi-skip-start-circle::before{content:"";}.bi-skip-start-fill::before{content:"";}.bi-skip-start::before{content:"";}.bi-slack::before{content:"";}.bi-slash-circle-fill::before{content:"";}.bi-slash-circle::before{content:"";}.bi-slash-square-fill::before{content:"";}.bi-slash-square::before{content:"";}.bi-slash::before{content:"";}.bi-sliders::before{content:"";}.bi-smartwatch::before{content:"";}.bi-snow::before{content:"";}.bi-snow2::before{content:"";}.bi-snow3::before{content:"";}.bi-sort-alpha-down-alt::before{content:"";}.bi-sort-alpha-down::before{content:"";}.bi-sort-alpha-up-alt::before{content:"";}.bi-sort-alpha-up::before{content:"";}.bi-sort-down-alt::before{content:"";}.bi-sort-down::before{content:"";}.bi-sort-numeric-down-alt::before{content:"";}.bi-sort-numeric-down::before{content:"";}.bi-sort-numeric-up-alt::before{content:"";}.bi-sort-numeric-up::before{content:"";}.bi-sort-up-alt::before{content:"";}.bi-sort-up::before{content:"";}.bi-soundwave::before{content:"";}.bi-speaker-fill::before{content:"";}.bi-speaker::before{content:"";}.bi-speedometer::before{content:"";}.bi-speedometer2::before{content:"";}.bi-spellcheck::before{content:"";}.bi-square-fill::before{content:"";}.bi-square-half::before{content:"";}.bi-square::before{content:"";}.bi-stack::before{content:"";}.bi-star-fill::before{content:"";}.bi-star-half::before{content:"";}.bi-star::before{content:"";}.bi-stars::before{content:"";}.bi-stickies-fill::before{content:"";}.bi-stickies::before{content:"";}.bi-sticky-fill::before{content:"";}.bi-sticky::before{content:"";}.bi-stop-btn-fill::before{content:"";}.bi-stop-btn::before{content:"";}.bi-stop-circle-fill::before{content:"";}.bi-stop-circle::before{content:"";}.bi-stop-fill::before{content:"";}.bi-stop::before{content:"";}.bi-stoplights-fill::before{content:"";}.bi-stoplights::before{content:"";}.bi-stopwatch-fill::before{content:"";}.bi-stopwatch::before{content:"";}.bi-subtract::before{content:"";}.bi-suit-club-fill::before{content:"";}.bi-suit-club::before{content:"";}.bi-suit-diamond-fill::before{content:"";}.bi-suit-diamond::before{content:"";}.bi-suit-heart-fill::before{content:"";}.bi-suit-heart::before{content:"";}.bi-suit-spade-fill::before{content:"";}.bi-suit-spade::before{content:"";}.bi-sun-fill::before{content:"";}.bi-sun::before{content:"";}.bi-sunglasses::before{content:"";}.bi-sunrise-fill::before{content:"";}.bi-sunrise::before{content:"";}.bi-sunset-fill::before{content:"";}.bi-sunset::before{content:"";}.bi-symmetry-horizontal::before{content:"";}.bi-symmetry-vertical::before{content:"";}.bi-table::before{content:"";}.bi-tablet-fill::before{content:"";}.bi-tablet-landscape-fill::before{content:"";}.bi-tablet-landscape::before{content:"";}.bi-tablet::before{content:"";}.bi-tag-fill::before{content:"";}.bi-tag::before{content:"";}.bi-tags-fill::before{content:"";}.bi-tags::before{content:"";}.bi-telegram::before{content:"";}.bi-telephone-fill::before{content:"";}.bi-telephone-forward-fill::before{content:"";}.bi-telephone-forward::before{content:"";}.bi-telephone-inbound-fill::before{content:"";}.bi-telephone-inbound::before{content:"";}.bi-telephone-minus-fill::before{content:"";}.bi-telephone-minus::before{content:"";}.bi-telephone-outbound-fill::before{content:"";}.bi-telephone-outbound::before{content:"";}.bi-telephone-plus-fill::before{content:"";}.bi-telephone-plus::before{content:"";}.bi-telephone-x-fill::before{content:"";}.bi-telephone-x::before{content:"";}.bi-telephone::before{content:"";}.bi-terminal-fill::before{content:"";}.bi-terminal::before{content:"";}.bi-text-center::before{content:"";}.bi-text-indent-left::before{content:"";}.bi-text-indent-right::before{content:"";}.bi-text-left::before{content:"";}.bi-text-paragraph::before{content:"";}.bi-text-right::before{content:"";}.bi-textarea-resize::before{content:"";}.bi-textarea-t::before{content:"";}.bi-textarea::before{content:"";}.bi-thermometer-half::before{content:"";}.bi-thermometer-high::before{content:"";}.bi-thermometer-low::before{content:"";}.bi-thermometer-snow::before{content:"";}.bi-thermometer-sun::before{content:"";}.bi-thermometer::before{content:"";}.bi-three-dots-vertical::before{content:"";}.bi-three-dots::before{content:"";}.bi-toggle-off::before{content:"";}.bi-toggle-on::before{content:"";}.bi-toggle2-off::before{content:"";}.bi-toggle2-on::before{content:"";}.bi-toggles::before{content:"";}.bi-toggles2::before{content:"";}.bi-tools::before{content:"";}.bi-tornado::before{content:"";}.bi-trash-fill::before{content:"";}.bi-trash::before{content:"";}.bi-trash2-fill::before{content:"";}.bi-trash2::before{content:"";}.bi-tree-fill::before{content:"";}.bi-tree::before{content:"";}.bi-triangle-fill::before{content:"";}.bi-triangle-half::before{content:"";}.bi-triangle::before{content:"";}.bi-trophy-fill::before{content:"";}.bi-trophy::before{content:"";}.bi-tropical-storm::before{content:"";}.bi-truck-flatbed::before{content:"";}.bi-truck::before{content:"";}.bi-tsunami::before{content:"";}.bi-tv-fill::before{content:"";}.bi-tv::before{content:"";}.bi-twitch::before{content:"";}.bi-twitter::before{content:"";}.bi-type-bold::before{content:"";}.bi-type-h1::before{content:"";}.bi-type-h2::before{content:"";}.bi-type-h3::before{content:"";}.bi-type-italic::before{content:"";}.bi-type-strikethrough::before{content:"";}.bi-type-underline::before{content:"";}.bi-type::before{content:"";}.bi-ui-checks-grid::before{content:"";}.bi-ui-checks::before{content:"";}.bi-ui-radios-grid::before{content:"";}.bi-ui-radios::before{content:"";}.bi-umbrella-fill::before{content:"";}.bi-umbrella::before{content:"";}.bi-union::before{content:"";}.bi-unlock-fill::before{content:"";}.bi-unlock::before{content:"";}.bi-upc-scan::before{content:"";}.bi-upc::before{content:"";}.bi-upload::before{content:"";}.bi-vector-pen::before{content:"";}.bi-view-list::before{content:"";}.bi-view-stacked::before{content:"";}.bi-vinyl-fill::before{content:"";}.bi-vinyl::before{content:"";}.bi-voicemail::before{content:"";}.bi-volume-down-fill::before{content:"";}.bi-volume-down::before{content:"";}.bi-volume-mute-fill::before{content:"";}.bi-volume-mute::before{content:"";}.bi-volume-off-fill::before{content:"";}.bi-volume-off::before{content:"";}.bi-volume-up-fill::before{content:"";}.bi-volume-up::before{content:"";}.bi-vr::before{content:"";}.bi-wallet-fill::before{content:"";}.bi-wallet::before{content:"";}.bi-wallet2::before{content:"";}.bi-watch::before{content:"";}.bi-water::before{content:"";}.bi-whatsapp::before{content:"";}.bi-wifi-1::before{content:"";}.bi-wifi-2::before{content:"";}.bi-wifi-off::before{content:"";}.bi-wifi::before{content:"";}.bi-wind::before{content:"";}.bi-window-dock::before{content:"";}.bi-window-sidebar::before{content:"";}.bi-window::before{content:"";}.bi-wrench::before{content:"";}.bi-x-circle-fill::before{content:"";}.bi-x-circle::before{content:"";}.bi-x-diamond-fill::before{content:"";}.bi-x-diamond::before{content:"";}.bi-x-octagon-fill::before{content:"";}.bi-x-octagon::before{content:"";}.bi-x-square-fill::before{content:"";}.bi-x-square::before{content:"";}.bi-x::before{content:"";}.bi-youtube::before{content:"";}.bi-zoom-in::before{content:"";}.bi-zoom-out::before{content:"";}.bi-bank::before{content:"";}.bi-bank2::before{content:"";}.bi-bell-slash-fill::before{content:"";}.bi-bell-slash::before{content:"";}.bi-cash-coin::before{content:"";}.bi-check-lg::before{content:"";}.bi-coin::before{content:"";}.bi-currency-bitcoin::before{content:"";}.bi-currency-dollar::before{content:"";}.bi-currency-euro::before{content:"";}.bi-currency-exchange::before{content:"";}.bi-currency-pound::before{content:"";}.bi-currency-yen::before{content:"";}.bi-dash-lg::before{content:"";}.bi-exclamation-lg::before{content:"";}.bi-file-earmark-pdf-fill::before{content:"";}.bi-file-earmark-pdf::before{content:"";}.bi-file-pdf-fill::before{content:"";}.bi-file-pdf::before{content:"";}.bi-gender-ambiguous::before{content:"";}.bi-gender-female::before{content:"";}.bi-gender-male::before{content:"";}.bi-gender-trans::before{content:"";}.bi-headset-vr::before{content:"";}.bi-info-lg::before{content:"";}.bi-mastodon::before{content:"";}.bi-messenger::before{content:"";}.bi-piggy-bank-fill::before{content:"";}.bi-piggy-bank::before{content:"";}.bi-pin-map-fill::before{content:"";}.bi-pin-map::before{content:"";}.bi-plus-lg::before{content:"";}.bi-question-lg::before{content:"";}.bi-recycle::before{content:"";}.bi-reddit::before{content:"";}.bi-safe-fill::before{content:"";}.bi-safe2-fill::before{content:"";}.bi-safe2::before{content:"";}.bi-sd-card-fill::before{content:"";}.bi-sd-card::before{content:"";}.bi-skype::before{content:"";}.bi-slash-lg::before{content:"";}.bi-translate::before{content:"";}.bi-x-lg::before{content:"";}.bi-safe::before{content:"";}.bi-apple::before{content:"";}.bi-microsoft::before{content:"";}.bi-windows::before{content:"";}.bi-behance::before{content:"";}.bi-dribbble::before{content:"";}.bi-line::before{content:"";}.bi-medium::before{content:"";}.bi-paypal::before{content:"";}.bi-pinterest::before{content:"";}.bi-signal::before{content:"";}.bi-snapchat::before{content:"";}.bi-spotify::before{content:"";}.bi-stack-overflow::before{content:"";}.bi-strava::before{content:"";}.bi-wordpress::before{content:"";}.bi-vimeo::before{content:"";}.bi-activity::before{content:"";}.bi-easel2-fill::before{content:"";}.bi-easel2::before{content:"";}.bi-easel3-fill::before{content:"";}.bi-easel3::before{content:"";}.bi-fan::before{content:"";}.bi-fingerprint::before{content:"";}.bi-graph-down-arrow::before{content:"";}.bi-graph-up-arrow::before{content:"";}.bi-hypnotize::before{content:"";}.bi-magic::before{content:"";}.bi-person-rolodex::before{content:"";}.bi-person-video::before{content:"";}.bi-person-video2::before{content:"";}.bi-person-video3::before{content:"";}.bi-person-workspace::before{content:"";}.bi-radioactive::before{content:"";}.bi-webcam-fill::before{content:"";}.bi-webcam::before{content:"";}.bi-yin-yang::before{content:"";}.bi-bandaid-fill::before{content:"";}.bi-bandaid::before{content:"";}.bi-bluetooth::before{content:"";}.bi-body-text::before{content:"";}.bi-boombox::before{content:"";}.bi-boxes::before{content:"";}.bi-dpad-fill::before{content:"";}.bi-dpad::before{content:"";}.bi-ear-fill::before{content:"";}.bi-ear::before{content:"";}.bi-envelope-check-fill::before{content:"";}.bi-envelope-check::before{content:"";}.bi-envelope-dash-fill::before{content:"";}.bi-envelope-dash::before{content:"";}.bi-envelope-exclamation-fill::before{content:"";}.bi-envelope-exclamation::before{content:"";}.bi-envelope-plus-fill::before{content:"";}.bi-envelope-plus::before{content:"";}.bi-envelope-slash-fill::before{content:"";}.bi-envelope-slash::before{content:"";}.bi-envelope-x-fill::before{content:"";}.bi-envelope-x::before{content:"";}.bi-explicit-fill::before{content:"";}.bi-explicit::before{content:"";}.bi-git::before{content:"";}.bi-infinity::before{content:"";}.bi-list-columns-reverse::before{content:"";}.bi-list-columns::before{content:"";}.bi-meta::before{content:"";}.bi-nintendo-switch::before{content:"";}.bi-pc-display-horizontal::before{content:"";}.bi-pc-display::before{content:"";}.bi-pc-horizontal::before{content:"";}.bi-pc::before{content:"";}.bi-playstation::before{content:"";}.bi-plus-slash-minus::before{content:"";}.bi-projector-fill::before{content:"";}.bi-projector::before{content:"";}.bi-qr-code-scan::before{content:"";}.bi-qr-code::before{content:"";}.bi-quora::before{content:"";}.bi-quote::before{content:"";}.bi-robot::before{content:"";}.bi-send-check-fill::before{content:"";}.bi-send-check::before{content:"";}.bi-send-dash-fill::before{content:"";}.bi-send-dash::before{content:"";}.bi-send-exclamation-fill::before{content:"";}.bi-send-exclamation::before{content:"";}.bi-send-fill::before{content:"";}.bi-send-plus-fill::before{content:"";}.bi-send-plus::before{content:"";}.bi-send-slash-fill::before{content:"";}.bi-send-slash::before{content:"";}.bi-send-x-fill::before{content:"";}.bi-send-x::before{content:"";}.bi-send::before{content:"";}.bi-steam::before{content:"";}.bi-terminal-dash::before{content:"";}.bi-terminal-plus::before{content:"";}.bi-terminal-split::before{content:"";}.bi-ticket-detailed-fill::before{content:"";}.bi-ticket-detailed::before{content:"";}.bi-ticket-fill::before{content:"";}.bi-ticket-perforated-fill::before{content:"";}.bi-ticket-perforated::before{content:"";}.bi-ticket::before{content:"";}.bi-tiktok::before{content:"";}.bi-window-dash::before{content:"";}.bi-window-desktop::before{content:"";}.bi-window-fullscreen::before{content:"";}.bi-window-plus::before{content:"";}.bi-window-split::before{content:"";}.bi-window-stack::before{content:"";}.bi-window-x::before{content:"";}.bi-xbox::before{content:"";}.bi-ethernet::before{content:"";}.bi-hdmi-fill::before{content:"";}.bi-hdmi::before{content:"";}.bi-usb-c-fill::before{content:"";}.bi-usb-c::before{content:"";}.bi-usb-fill::before{content:"";}.bi-usb-plug-fill::before{content:"";}.bi-usb-plug::before{content:"";}.bi-usb-symbol::before{content:"";}.bi-usb::before{content:"";}.bi-boombox-fill::before{content:"";}.bi-displayport::before{content:"";}.bi-gpu-card::before{content:"";}.bi-memory::before{content:"";}.bi-modem-fill::before{content:"";}.bi-modem::before{content:"";}.bi-motherboard-fill::before{content:"";}.bi-motherboard::before{content:"";}.bi-optical-audio-fill::before{content:"";}.bi-optical-audio::before{content:"";}.bi-pci-card::before{content:"";}.bi-router-fill::before{content:"";}.bi-router::before{content:"";}.bi-thunderbolt-fill::before{content:"";}.bi-thunderbolt::before{content:"";}.bi-usb-drive-fill::before{content:"";}.bi-usb-drive::before{content:"";}.bi-usb-micro-fill::before{content:"";}.bi-usb-micro::before{content:"";}.bi-usb-mini-fill::before{content:"";}.bi-usb-mini::before{content:"";}.bi-cloud-haze2::before{content:"";}.bi-device-hdd-fill::before{content:"";}.bi-device-hdd::before{content:"";}.bi-device-ssd-fill::before{content:"";}.bi-device-ssd::before{content:"";}.bi-displayport-fill::before{content:"";}.bi-mortarboard-fill::before{content:"";}.bi-mortarboard::before{content:"";}.bi-terminal-x::before{content:"";}.bi-arrow-through-heart-fill::before{content:"";}.bi-arrow-through-heart::before{content:"";}.bi-badge-sd-fill::before{content:"";}.bi-badge-sd::before{content:"";}.bi-bag-heart-fill::before{content:"";}.bi-bag-heart::before{content:"";}.bi-balloon-fill::before{content:"";}.bi-balloon-heart-fill::before{content:"";}.bi-balloon-heart::before{content:"";}.bi-balloon::before{content:"";}.bi-box2-fill::before{content:"";}.bi-box2-heart-fill::before{content:"";}.bi-box2-heart::before{content:"";}.bi-box2::before{content:"";}.bi-braces-asterisk::before{content:"";}.bi-calendar-heart-fill::before{content:"";}.bi-calendar-heart::before{content:"";}.bi-calendar2-heart-fill::before{content:"";}.bi-calendar2-heart::before{content:"";}.bi-chat-heart-fill::before{content:"";}.bi-chat-heart::before{content:"";}.bi-chat-left-heart-fill::before{content:"";}.bi-chat-left-heart::before{content:"";}.bi-chat-right-heart-fill::before{content:"";}.bi-chat-right-heart::before{content:"";}.bi-chat-square-heart-fill::before{content:"";}.bi-chat-square-heart::before{content:"";}.bi-clipboard-check-fill::before{content:"";}.bi-clipboard-data-fill::before{content:"";}.bi-clipboard-fill::before{content:"";}.bi-clipboard-heart-fill::before{content:"";}.bi-clipboard-heart::before{content:"";}.bi-clipboard-minus-fill::before{content:"";}.bi-clipboard-plus-fill::before{content:"";}.bi-clipboard-pulse::before{content:"";}.bi-clipboard-x-fill::before{content:"";}.bi-clipboard2-check-fill::before{content:"";}.bi-clipboard2-check::before{content:"";}.bi-clipboard2-data-fill::before{content:"";}.bi-clipboard2-data::before{content:"";}.bi-clipboard2-fill::before{content:"";}.bi-clipboard2-heart-fill::before{content:"";}.bi-clipboard2-heart::before{content:"";}.bi-clipboard2-minus-fill::before{content:"";}.bi-clipboard2-minus::before{content:"";}.bi-clipboard2-plus-fill::before{content:"";}.bi-clipboard2-plus::before{content:"";}.bi-clipboard2-pulse-fill::before{content:"";}.bi-clipboard2-pulse::before{content:"";}.bi-clipboard2-x-fill::before{content:"";}.bi-clipboard2-x::before{content:"";}.bi-clipboard2::before{content:"";}.bi-emoji-kiss-fill::before{content:"";}.bi-emoji-kiss::before{content:"";}.bi-envelope-heart-fill::before{content:"";}.bi-envelope-heart::before{content:"";}.bi-envelope-open-heart-fill::before{content:"";}.bi-envelope-open-heart::before{content:"";}.bi-envelope-paper-fill::before{content:"";}.bi-envelope-paper-heart-fill::before{content:"";}.bi-envelope-paper-heart::before{content:"";}.bi-envelope-paper::before{content:"";}.bi-filetype-aac::before{content:"";}.bi-filetype-ai::before{content:"";}.bi-filetype-bmp::before{content:"";}.bi-filetype-cs::before{content:"";}.bi-filetype-css::before{content:"";}.bi-filetype-csv::before{content:"";}.bi-filetype-doc::before{content:"";}.bi-filetype-docx::before{content:"";}.bi-filetype-exe::before{content:"";}.bi-filetype-gif::before{content:"";}.bi-filetype-heic::before{content:"";}.bi-filetype-html::before{content:"";}.bi-filetype-java::before{content:"";}.bi-filetype-jpg::before{content:"";}.bi-filetype-js::before{content:"";}.bi-filetype-jsx::before{content:"";}.bi-filetype-key::before{content:"";}.bi-filetype-m4p::before{content:"";}.bi-filetype-md::before{content:"";}.bi-filetype-mdx::before{content:"";}.bi-filetype-mov::before{content:"";}.bi-filetype-mp3::before{content:"";}.bi-filetype-mp4::before{content:"";}.bi-filetype-otf::before{content:"";}.bi-filetype-pdf::before{content:"";}.bi-filetype-php::before{content:"";}.bi-filetype-png::before{content:"";}.bi-filetype-ppt::before{content:"";}.bi-filetype-psd::before{content:"";}.bi-filetype-py::before{content:"";}.bi-filetype-raw::before{content:"";}.bi-filetype-rb::before{content:"";}.bi-filetype-sass::before{content:"";}.bi-filetype-scss::before{content:"";}.bi-filetype-sh::before{content:"";}.bi-filetype-svg::before{content:"";}.bi-filetype-tiff::before{content:"";}.bi-filetype-tsx::before{content:"";}.bi-filetype-ttf::before{content:"";}.bi-filetype-txt::before{content:"";}.bi-filetype-wav::before{content:"";}.bi-filetype-woff::before{content:"";}.bi-filetype-xls::before{content:"";}.bi-filetype-xml::before{content:"";}.bi-filetype-yml::before{content:"";}.bi-heart-arrow::before{content:"";}.bi-heart-pulse-fill::before{content:"";}.bi-heart-pulse::before{content:"";}.bi-heartbreak-fill::before{content:"";}.bi-heartbreak::before{content:"";}.bi-hearts::before{content:"";}.bi-hospital-fill::before{content:"";}.bi-hospital::before{content:"";}.bi-house-heart-fill::before{content:"";}.bi-house-heart::before{content:"";}.bi-incognito::before{content:"";}.bi-magnet-fill::before{content:"";}.bi-magnet::before{content:"";}.bi-person-heart::before{content:"";}.bi-person-hearts::before{content:"";}.bi-phone-flip::before{content:"";}.bi-plugin::before{content:"";}.bi-postage-fill::before{content:"";}.bi-postage-heart-fill::before{content:"";}.bi-postage-heart::before{content:"";}.bi-postage::before{content:"";}.bi-postcard-fill::before{content:"";}.bi-postcard-heart-fill::before{content:"";}.bi-postcard-heart::before{content:"";}.bi-postcard::before{content:"";}.bi-search-heart-fill::before{content:"";}.bi-search-heart::before{content:"";}.bi-sliders2-vertical::before{content:"";}.bi-sliders2::before{content:"";}.bi-trash3-fill::before{content:"";}.bi-trash3::before{content:"";}.bi-valentine::before{content:"";}.bi-valentine2::before{content:"";}.bi-wrench-adjustable-circle-fill::before{content:"";}.bi-wrench-adjustable-circle::before{content:"";}.bi-wrench-adjustable::before{content:"";}.bi-filetype-json::before{content:"";}.bi-filetype-pptx::before{content:"";}.bi-filetype-xlsx::before{content:"";}.bi-1-circle-fill::before{content:"";}.bi-1-circle::before{content:"";}.bi-1-square-fill::before{content:"";}.bi-1-square::before{content:"";}.bi-2-circle-fill::before{content:"";}.bi-2-circle::before{content:"";}.bi-2-square-fill::before{content:"";}.bi-2-square::before{content:"";}.bi-3-circle-fill::before{content:"";}.bi-3-circle::before{content:"";}.bi-3-square-fill::before{content:"";}.bi-3-square::before{content:"";}.bi-4-circle-fill::before{content:"";}.bi-4-circle::before{content:"";}.bi-4-square-fill::before{content:"";}.bi-4-square::before{content:"";}.bi-5-circle-fill::before{content:"";}.bi-5-circle::before{content:"";}.bi-5-square-fill::before{content:"";}.bi-5-square::before{content:"";}.bi-6-circle-fill::before{content:"";}.bi-6-circle::before{content:"";}.bi-6-square-fill::before{content:"";}.bi-6-square::before{content:"";}.bi-7-circle-fill::before{content:"";}.bi-7-circle::before{content:"";}.bi-7-square-fill::before{content:"";}.bi-7-square::before{content:"";}.bi-8-circle-fill::before{content:"";}.bi-8-circle::before{content:"";}.bi-8-square-fill::before{content:"";}.bi-8-square::before{content:"";}.bi-9-circle-fill::before{content:"";}.bi-9-circle::before{content:"";}.bi-9-square-fill::before{content:"";}.bi-9-square::before{content:"";}.bi-airplane-engines-fill::before{content:"";}.bi-airplane-engines::before{content:"";}.bi-airplane-fill::before{content:"";}.bi-airplane::before{content:"";}.bi-alexa::before{content:"";}.bi-alipay::before{content:"";}.bi-android::before{content:"";}.bi-android2::before{content:"";}.bi-box-fill::before{content:"";}.bi-box-seam-fill::before{content:"";}.bi-browser-chrome::before{content:"";}.bi-browser-edge::before{content:"";}.bi-browser-firefox::before{content:"";}.bi-browser-safari::before{content:"";}.bi-c-circle-fill::before{content:"";}.bi-c-circle::before{content:"";}.bi-c-square-fill::before{content:"";}.bi-c-square::before{content:"";}.bi-capsule-pill::before{content:"";}.bi-capsule::before{content:"";}.bi-car-front-fill::before{content:"";}.bi-car-front::before{content:"";}.bi-cassette-fill::before{content:"";}.bi-cassette::before{content:"";}.bi-cc-circle-fill::before{content:"";}.bi-cc-circle::before{content:"";}.bi-cc-square-fill::before{content:"";}.bi-cc-square::before{content:"";}.bi-cup-hot-fill::before{content:"";}.bi-cup-hot::before{content:"";}.bi-currency-rupee::before{content:"";}.bi-dropbox::before{content:"";}.bi-escape::before{content:"";}.bi-fast-forward-btn-fill::before{content:"";}.bi-fast-forward-btn::before{content:"";}.bi-fast-forward-circle-fill::before{content:"";}.bi-fast-forward-circle::before{content:"";}.bi-fast-forward-fill::before{content:"";}.bi-fast-forward::before{content:"";}.bi-filetype-sql::before{content:"";}.bi-fire::before{content:"";}.bi-google-play::before{content:"";}.bi-h-circle-fill::before{content:"";}.bi-h-circle::before{content:"";}.bi-h-square-fill::before{content:"";}.bi-h-square::before{content:"";}.bi-indent::before{content:"";}.bi-lungs-fill::before{content:"";}.bi-lungs::before{content:"";}.bi-microsoft-teams::before{content:"";}.bi-p-circle-fill::before{content:"";}.bi-p-circle::before{content:"";}.bi-p-square-fill::before{content:"";}.bi-p-square::before{content:"";}.bi-pass-fill::before{content:"";}.bi-pass::before{content:"";}.bi-prescription::before{content:"";}.bi-prescription2::before{content:"";}.bi-r-circle-fill::before{content:"";}.bi-r-circle::before{content:"";}.bi-r-square-fill::before{content:"";}.bi-r-square::before{content:"";}.bi-repeat-1::before{content:"";}.bi-repeat::before{content:"";}.bi-rewind-btn-fill::before{content:"";}.bi-rewind-btn::before{content:"";}.bi-rewind-circle-fill::before{content:"";}.bi-rewind-circle::before{content:"";}.bi-rewind-fill::before{content:"";}.bi-rewind::before{content:"";}.bi-train-freight-front-fill::before{content:"";}.bi-train-freight-front::before{content:"";}.bi-train-front-fill::before{content:"";}.bi-train-front::before{content:"";}.bi-train-lightrail-front-fill::before{content:"";}.bi-train-lightrail-front::before{content:"";}.bi-truck-front-fill::before{content:"";}.bi-truck-front::before{content:"";}.bi-ubuntu::before{content:"";}.bi-unindent::before{content:"";}.bi-unity::before{content:"";}.bi-universal-access-circle::before{content:"";}.bi-universal-access::before{content:"";}.bi-virus::before{content:"";}.bi-virus2::before{content:"";}.bi-wechat::before{content:"";}.bi-yelp::before{content:"";}.bi-sign-stop-fill::before{content:"";}.bi-sign-stop-lights-fill::before{content:"";}.bi-sign-stop-lights::before{content:"";}.bi-sign-stop::before{content:"";}.bi-sign-turn-left-fill::before{content:"";}.bi-sign-turn-left::before{content:"";}.bi-sign-turn-right-fill::before{content:"";}.bi-sign-turn-right::before{content:"";}.bi-sign-turn-slight-left-fill::before{content:"";}.bi-sign-turn-slight-left::before{content:"";}.bi-sign-turn-slight-right-fill::before{content:"";}.bi-sign-turn-slight-right::before{content:"";}.bi-sign-yield-fill::before{content:"";}.bi-sign-yield::before{content:"";}.bi-ev-station-fill::before{content:"";}.bi-ev-station::before{content:"";}.bi-fuel-pump-diesel-fill::before{content:"";}.bi-fuel-pump-diesel::before{content:"";}.bi-fuel-pump-fill::before{content:"";}.bi-fuel-pump::before{content:"";}.bi-0-circle-fill::before{content:"";}.bi-0-circle::before{content:"";}.bi-0-square-fill::before{content:"";}.bi-0-square::before{content:"";}.bi-rocket-fill::before{content:"";}.bi-rocket-takeoff-fill::before{content:"";}.bi-rocket-takeoff::before{content:"";}.bi-rocket::before{content:"";}.bi-stripe::before{content:"";}.bi-subscript::before{content:"";}.bi-superscript::before{content:"";}.bi-trello::before{content:"";}.bi-envelope-at-fill::before{content:"";}.bi-envelope-at::before{content:"";}.bi-regex::before{content:"";}.bi-text-wrap::before{content:"";}.bi-sign-dead-end-fill::before{content:"";}.bi-sign-dead-end::before{content:"";}.bi-sign-do-not-enter-fill::before{content:"";}.bi-sign-do-not-enter::before{content:"";}.bi-sign-intersection-fill::before{content:"";}.bi-sign-intersection-side-fill::before{content:"";}.bi-sign-intersection-side::before{content:"";}.bi-sign-intersection-t-fill::before{content:"";}.bi-sign-intersection-t::before{content:"";}.bi-sign-intersection-y-fill::before{content:"";}.bi-sign-intersection-y::before{content:"";}.bi-sign-intersection::before{content:"";}.bi-sign-merge-left-fill::before{content:"";}.bi-sign-merge-left::before{content:"";}.bi-sign-merge-right-fill::before{content:"";}.bi-sign-merge-right::before{content:"";}.bi-sign-no-left-turn-fill::before{content:"";}.bi-sign-no-left-turn::before{content:"";}.bi-sign-no-parking-fill::before{content:"";}.bi-sign-no-parking::before{content:"";}.bi-sign-no-right-turn-fill::before{content:"";}.bi-sign-no-right-turn::before{content:"";}.bi-sign-railroad-fill::before{content:"";}.bi-sign-railroad::before{content:"";}.bi-building-add::before{content:"";}.bi-building-check::before{content:"";}.bi-building-dash::before{content:"";}.bi-building-down::before{content:"";}.bi-building-exclamation::before{content:"";}.bi-building-fill-add::before{content:"";}.bi-building-fill-check::before{content:"";}.bi-building-fill-dash::before{content:"";}.bi-building-fill-down::before{content:"";}.bi-building-fill-exclamation::before{content:"";}.bi-building-fill-gear::before{content:"";}.bi-building-fill-lock::before{content:"";}.bi-building-fill-slash::before{content:"";}.bi-building-fill-up::before{content:"";}.bi-building-fill-x::before{content:"";}.bi-building-fill::before{content:"";}.bi-building-gear::before{content:"";}.bi-building-lock::before{content:"";}.bi-building-slash::before{content:"";}.bi-building-up::before{content:"";}.bi-building-x::before{content:"";}.bi-buildings-fill::before{content:"";}.bi-buildings::before{content:"";}.bi-bus-front-fill::before{content:"";}.bi-bus-front::before{content:"";}.bi-ev-front-fill::before{content:"";}.bi-ev-front::before{content:"";}.bi-globe-americas::before{content:"";}.bi-globe-asia-australia::before{content:"";}.bi-globe-central-south-asia::before{content:"";}.bi-globe-europe-africa::before{content:"";}.bi-house-add-fill::before{content:"";}.bi-house-add::before{content:"";}.bi-house-check-fill::before{content:"";}.bi-house-check::before{content:"";}.bi-house-dash-fill::before{content:"";}.bi-house-dash::before{content:"";}.bi-house-down-fill::before{content:"";}.bi-house-down::before{content:"";}.bi-house-exclamation-fill::before{content:"";}.bi-house-exclamation::before{content:"";}.bi-house-gear-fill::before{content:"";}.bi-house-gear::before{content:"";}.bi-house-lock-fill::before{content:"";}.bi-house-lock::before{content:"";}.bi-house-slash-fill::before{content:"";}.bi-house-slash::before{content:"";}.bi-house-up-fill::before{content:"";}.bi-house-up::before{content:"";}.bi-house-x-fill::before{content:"";}.bi-house-x::before{content:"";}.bi-person-add::before{content:"";}.bi-person-down::before{content:"";}.bi-person-exclamation::before{content:"";}.bi-person-fill-add::before{content:"";}.bi-person-fill-check::before{content:"";}.bi-person-fill-dash::before{content:"";}.bi-person-fill-down::before{content:"";}.bi-person-fill-exclamation::before{content:"";}.bi-person-fill-gear::before{content:"";}.bi-person-fill-lock::before{content:"";}.bi-person-fill-slash::before{content:"";}.bi-person-fill-up::before{content:"";}.bi-person-fill-x::before{content:"";}.bi-person-gear::before{content:"";}.bi-person-lock::before{content:"";}.bi-person-slash::before{content:"";}.bi-person-up::before{content:"";}.bi-scooter::before{content:"";}.bi-taxi-front-fill::before{content:"";}.bi-taxi-front::before{content:"";}.bi-amd::before{content:"";}.bi-database-add::before{content:"";}.bi-database-check::before{content:"";}.bi-database-dash::before{content:"";}.bi-database-down::before{content:"";}.bi-database-exclamation::before{content:"";}.bi-database-fill-add::before{content:"";}.bi-database-fill-check::before{content:"";}.bi-database-fill-dash::before{content:"";}.bi-database-fill-down::before{content:"";}.bi-database-fill-exclamation::before{content:"";}.bi-database-fill-gear::before{content:"";}.bi-database-fill-lock::before{content:"";}.bi-database-fill-slash::before{content:"";}.bi-database-fill-up::before{content:"";}.bi-database-fill-x::before{content:"";}.bi-database-fill::before{content:"";}.bi-database-gear::before{content:"";}.bi-database-lock::before{content:"";}.bi-database-slash::before{content:"";}.bi-database-up::before{content:"";}.bi-database-x::before{content:"";}.bi-database::before{content:"";}.bi-houses-fill::before{content:"";}.bi-houses::before{content:"";}.bi-nvidia::before{content:"";}.bi-person-vcard-fill::before{content:"";}.bi-person-vcard::before{content:"";}.bi-sina-weibo::before{content:"";}.bi-tencent-qq::before{content:"";}.bi-wikipedia::before{content:"";}.bi-alphabet-uppercase::before{content:"";}.bi-alphabet::before{content:"";}.bi-amazon::before{content:"";}.bi-arrows-collapse-vertical::before{content:"";}.bi-arrows-expand-vertical::before{content:"";}.bi-arrows-vertical::before{content:"";}.bi-arrows::before{content:"";}.bi-ban-fill::before{content:"";}.bi-ban::before{content:"";}.bi-bing::before{content:"";}.bi-cake::before{content:"";}.bi-cake2::before{content:"";}.bi-cookie::before{content:"";}.bi-copy::before{content:"";}.bi-crosshair::before{content:"";}.bi-crosshair2::before{content:"";}.bi-emoji-astonished-fill::before{content:"";}.bi-emoji-astonished::before{content:"";}.bi-emoji-grimace-fill::before{content:"";}.bi-emoji-grimace::before{content:"";}.bi-emoji-grin-fill::before{content:"";}.bi-emoji-grin::before{content:"";}.bi-emoji-surprise-fill::before{content:"";}.bi-emoji-surprise::before{content:"";}.bi-emoji-tear-fill::before{content:"";}.bi-emoji-tear::before{content:"";}.bi-envelope-arrow-down-fill::before{content:"";}.bi-envelope-arrow-down::before{content:"";}.bi-envelope-arrow-up-fill::before{content:"";}.bi-envelope-arrow-up::before{content:"";}.bi-feather::before{content:"";}.bi-feather2::before{content:"";}.bi-floppy-fill::before{content:"";}.bi-floppy::before{content:"";}.bi-floppy2-fill::before{content:"";}.bi-floppy2::before{content:"";}.bi-gitlab::before{content:"";}.bi-highlighter::before{content:"";}.bi-marker-tip::before{content:"";}.bi-nvme-fill::before{content:"";}.bi-nvme::before{content:"";}.bi-opencollective::before{content:"";}.bi-pci-card-network::before{content:"";}.bi-pci-card-sound::before{content:"";}.bi-radar::before{content:"";}.bi-send-arrow-down-fill::before{content:"";}.bi-send-arrow-down::before{content:"";}.bi-send-arrow-up-fill::before{content:"";}.bi-send-arrow-up::before{content:"";}.bi-sim-slash-fill::before{content:"";}.bi-sim-slash::before{content:"";}.bi-sourceforge::before{content:"";}.bi-substack::before{content:"";}.bi-threads-fill::before{content:"";}.bi-threads::before{content:"";}.bi-transparency::before{content:"";}.bi-twitter-x::before{content:"";}.bi-type-h4::before{content:"";}.bi-type-h5::before{content:"";}.bi-type-h6::before{content:"";}.bi-backpack-fill::before{content:"";}.bi-backpack::before{content:"";}.bi-backpack2-fill::before{content:"";}.bi-backpack2::before{content:"";}.bi-backpack3-fill::before{content:"";}.bi-backpack3::before{content:"";}.bi-backpack4-fill::before{content:"";}.bi-backpack4::before{content:"";}.bi-brilliance::before{content:"";}.bi-cake-fill::before{content:"";}.bi-cake2-fill::before{content:"";}.bi-duffle-fill::before{content:"";}.bi-duffle::before{content:"";}.bi-exposure::before{content:"";}.bi-gender-neuter::before{content:"";}.bi-highlights::before{content:"";}.bi-luggage-fill::before{content:"";}.bi-luggage::before{content:"";}.bi-mailbox-flag::before{content:"";}.bi-mailbox2-flag::before{content:"";}.bi-noise-reduction::before{content:"";}.bi-passport-fill::before{content:"";}.bi-passport::before{content:"";}.bi-person-arms-up::before{content:"";}.bi-person-raised-hand::before{content:"";}.bi-person-standing-dress::before{content:"";}.bi-person-standing::before{content:"";}.bi-person-walking::before{content:"";}.bi-person-wheelchair::before{content:"";}.bi-shadows::before{content:"";}.bi-suitcase-fill::before{content:"";}.bi-suitcase-lg-fill::before{content:"";}.bi-suitcase-lg::before{content:"";}.bi-suitcase::before{content:"豈";}.bi-suitcase2-fill::before{content:"更";}.bi-suitcase2::before{content:"車";}.bi-vignette::before{content:"賈";} +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-accent:#9cec5b;--bs-cinereous:#837569;--bs-verdigris:#50c5b7;--bs-icterine:#f0f465;--bs-mute:#6c757d;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#8c68cd;--bs-secondary:#3c8cd6;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-accent:#9cec5b;--bs-cinereous:#837569;--bs-verdigris:#50c5b7;--bs-icterine:#f0f465;--bs-mute:#6c757d;--bs-primary-rgb:140,104,205;--bs-secondary-rgb:60,140,214;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-accent-rgb:156,236,91;--bs-cinereous-rgb:131,117,105;--bs-verdigris-rgb:80,197,183;--bs-icterine-rgb:240,244,101;--bs-mute-rgb:108,117,125;--bs-primary-text-emphasis:#382a52;--bs-secondary-text-emphasis:#183856;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#7d59bd;--bs-secondary-bg-subtle:#d8e8f7;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-accent-bg-subtle:#9cec5b;--bs-cinereous-bg-subtle:#837569;--bs-verdigris-bg-subtle:#50c5b7;--bs-icterine-bg-subtle:#f0f465;--bs-mute-bg-subtle:#6c757d;--bs-primary-border-subtle:#714db1;--bs-secondary-border-subtle:#b1d1ef;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-accent-border-subtle:#9cec5b;--bs-cinereous-border-subtle:#837569;--bs-verdigris-border-subtle:#50c5b7;--bs-icterine-border-subtle:#f0f465;--bs-mute-border-subtle:#6c757d;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg,rgba(255,255,255,.15),rgba(255,255,255,0));--bs-root-font-size:15px;--bs-body-font-family:"Inconsolata",monospace;--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33,37,41,.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33,37,41,.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#8c68cd;--bs-link-color-rgb:140,104,205;--bs-link-decoration:underline;--bs-link-hover-color:#7053a4;--bs-link-hover-color-rgb:112,83,164;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0,0,0,.175);--bs-border-radius:.375rem;--bs-border-radius-sm:.25rem;--bs-border-radius-lg:.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 .5rem 1rem rgba(0,0,0,.15);--bs-box-shadow-sm:0 .125rem .25rem rgba(0,0,0,.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0,0,0,.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0,0,0,.075);--bs-focus-ring-width:.25rem;--bs-focus-ring-opacity:.25;--bs-focus-ring-color:rgba(140,104,205,.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545;}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222,226,230,.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222,226,230,.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#baa4e1;--bs-secondary-text-emphasis:#8abae6;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-accent-text-emphasis:#9cec5b;--bs-cinereous-text-emphasis:#837569;--bs-verdigris-text-emphasis:#50c5b7;--bs-icterine-text-emphasis:#f0f465;--bs-mute-text-emphasis:#6c757d;--bs-primary-bg-subtle:#7d59bd;--bs-secondary-bg-subtle:#0c1c2b;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-accent-bg-subtle:#9cec5b;--bs-cinereous-bg-subtle:#837569;--bs-verdigris-bg-subtle:#50c5b7;--bs-icterine-bg-subtle:#f0f465;--bs-mute-bg-subtle:#6c757d;--bs-primary-border-subtle:#714db1;--bs-secondary-border-subtle:#245480;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-accent-border-subtle:#9cec5b;--bs-cinereous-border-subtle:#837569;--bs-verdigris-border-subtle:#50c5b7;--bs-icterine-border-subtle:#f0f465;--bs-mute-border-subtle:#6c757d;--bs-heading-color:inherit;--bs-link-color:#8c68cd;--bs-link-hover-color:#c5b3e6;--bs-link-color-rgb:140,104,205;--bs-link-hover-color-rgb:197,179,230;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255,255,255,.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f;}*,*::before,*::after{box-sizing:border-box;}:root{font-size:var(--bs-root-font-size);}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth;}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25;}h6,.h6,h5,.h5,h4,.h4,h3,.h3,h2,.h2,h1,.h1{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color);}h1,.h1{font-size:calc(1.375rem + 1.5vw);}@media(min-width:1200px){h1,.h1{font-size:2.5rem;}}h2,.h2{font-size:calc(1.325rem + .9vw);}@media(min-width:1200px){h2,.h2{font-size:2rem;}}h3,.h3{font-size:calc(1.3rem + .6vw);}@media(min-width:1200px){h3,.h3{font-size:1.75rem;}}h4,.h4{font-size:calc(1.275rem + .3vw);}@media(min-width:1200px){h4,.h4{font-size:1.5rem;}}h5,.h5{font-size:1.25rem;}h6,.h6{font-size:1rem;}p{margin-top:0;margin-bottom:1rem;}abbr[title]{text-decoration:underline dotted;cursor:help;text-decoration-skip-ink:none;}address{margin-bottom:1rem;font-style:normal;line-height:inherit;}ol,ul{padding-left:2rem;}ol,ul,dl{margin-top:0;margin-bottom:1rem;}ol ol,ul ul,ol ul,ul ol{margin-bottom:0;}dt{font-weight:700;}dd{margin-bottom:.5rem;margin-left:0;}blockquote{margin:0 0 1rem;}b,strong{font-weight:bolder;}small,.small{font-size:.875em;}mark,.mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg);}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline;}sub{bottom:-.25em;}sup{top:-.5em;}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline;}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb);}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none;}pre,code,kbd,samp{font-family:var(--bs-font-monospace);font-size:1em;}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em;}pre code{font-size:inherit;color:inherit;word-break:normal;}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word;}a>code{color:inherit;}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem;}kbd kbd{padding:0;font-size:1em;}figure{margin:0 0 1rem;}img,svg{vertical-align:middle;}table{caption-side:bottom;border-collapse:collapse;}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left;}th{text-align:inherit;text-align:-webkit-match-parent;}thead,tbody,tfoot,tr,td,th{border-color:inherit;border-style:solid;border-width:0;}label{display:inline-block;}button{border-radius:0;}button:focus:not(:focus-visible){outline:0;}input,button,select,optgroup,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit;}button,select{text-transform:none;}[role=button]{cursor:pointer;}select{word-wrap:normal;}select:disabled{opacity:1;}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important;}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;}button:not(:disabled),[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled){cursor:pointer;}::-moz-focus-inner{padding:0;border-style:none;}textarea{resize:vertical;}fieldset{min-width:0;padding:0;margin:0;border:0;}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit;}@media(min-width:1200px){legend{font-size:1.5rem;}}legend+*{clear:left;}::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-text,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-year-field{padding:0;}::-webkit-inner-spin-button{height:auto;}[type=search]{-webkit-appearance:textfield;outline-offset:-2px;}::-webkit-search-decoration{-webkit-appearance:none;}::-webkit-color-swatch-wrapper{padding:0;}::file-selector-button{font:inherit;-webkit-appearance:button;}output{display:inline-block;}iframe{border:0;}summary{display:list-item;cursor:pointer;}progress{vertical-align:baseline;}[hidden]{display:none!important;}.lead{font-size:1.25rem;font-weight:300;}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-1{font-size:5rem;}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-2{font-size:4.5rem;}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-3{font-size:4rem;}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-4{font-size:3.5rem;}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-5{font-size:3rem;}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2;}@media(min-width:1200px){.display-6{font-size:2.5rem;}}.list-unstyled{padding-left:0;list-style:none;}.list-inline{padding-left:0;list-style:none;}.list-inline-item{display:inline-block;}.list-inline-item:not(:last-child){margin-right:.5rem;}.initialism{font-size:.875em;text-transform:uppercase;}.blockquote{margin-bottom:1rem;font-size:1.25rem;}.blockquote>:last-child{margin-bottom:0;}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d;}.blockquote-footer::before{content:"— ";}.img-fluid{max-width:100%;height:auto;}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto;}.figure{display:inline-block;}.figure-img{margin-bottom:.5rem;line-height:1;}.figure-caption{font-size:.875em;color:var(--bs-secondary-color);}.container,.container-fluid,.container-xxl,.container-xl,.container-lg,.container-md,.container-sm{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x)*.5);padding-left:calc(var(--bs-gutter-x)*.5);margin-right:auto;margin-left:auto;}@media(min-width:576px){.container-sm,.container{max-width:540px;}}@media(min-width:768px){.container-md,.container-sm,.container{max-width:720px;}}@media(min-width:992px){.container-lg,.container-md,.container-sm,.container{max-width:960px;}}@media(min-width:1200px){.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1140px;}}@media(min-width:1400px){.container-xxl,.container-xl,.container-lg,.container-md,.container-sm,.container{max-width:1320px;}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px;}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1*var(--bs-gutter-y));margin-right:calc(-.5*var(--bs-gutter-x));margin-left:calc(-.5*var(--bs-gutter-x));}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x)*.5);padding-left:calc(var(--bs-gutter-x)*.5);margin-top:var(--bs-gutter-y);}.col{flex:1 0 0%;}.row-cols-auto>*{flex:0 0 auto;width:auto;}.row-cols-1>*{flex:0 0 auto;width:100%;}.row-cols-2>*{flex:0 0 auto;width:50%;}.row-cols-3>*{flex:0 0 auto;width:33.33333333%;}.row-cols-4>*{flex:0 0 auto;width:25%;}.row-cols-5>*{flex:0 0 auto;width:20%;}.row-cols-6>*{flex:0 0 auto;width:16.66666667%;}.col-auto{flex:0 0 auto;width:auto;}.col-1{flex:0 0 auto;width:8.33333333%;}.col-2{flex:0 0 auto;width:16.66666667%;}.col-3{flex:0 0 auto;width:25%;}.col-4{flex:0 0 auto;width:33.33333333%;}.col-5{flex:0 0 auto;width:41.66666667%;}.col-6{flex:0 0 auto;width:50%;}.col-7{flex:0 0 auto;width:58.33333333%;}.col-8{flex:0 0 auto;width:66.66666667%;}.col-9{flex:0 0 auto;width:75%;}.col-10{flex:0 0 auto;width:83.33333333%;}.col-11{flex:0 0 auto;width:91.66666667%;}.col-12{flex:0 0 auto;width:100%;}.offset-1{margin-left:8.33333333%;}.offset-2{margin-left:16.66666667%;}.offset-3{margin-left:25%;}.offset-4{margin-left:33.33333333%;}.offset-5{margin-left:41.66666667%;}.offset-6{margin-left:50%;}.offset-7{margin-left:58.33333333%;}.offset-8{margin-left:66.66666667%;}.offset-9{margin-left:75%;}.offset-10{margin-left:83.33333333%;}.offset-11{margin-left:91.66666667%;}.g-0,.gx-0{--bs-gutter-x:0;}.g-0,.gy-0{--bs-gutter-y:0;}.g-1,.gx-1{--bs-gutter-x:.25rem;}.g-1,.gy-1{--bs-gutter-y:.25rem;}.g-2,.gx-2{--bs-gutter-x:.5rem;}.g-2,.gy-2{--bs-gutter-y:.5rem;}.g-3,.gx-3{--bs-gutter-x:1rem;}.g-3,.gy-3{--bs-gutter-y:1rem;}.g-4,.gx-4{--bs-gutter-x:1.5rem;}.g-4,.gy-4{--bs-gutter-y:1.5rem;}.g-5,.gx-5{--bs-gutter-x:3rem;}.g-5,.gy-5{--bs-gutter-y:3rem;}@media(min-width:576px){.col-sm{flex:1 0 0%;}.row-cols-sm-auto>*{flex:0 0 auto;width:auto;}.row-cols-sm-1>*{flex:0 0 auto;width:100%;}.row-cols-sm-2>*{flex:0 0 auto;width:50%;}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%;}.row-cols-sm-4>*{flex:0 0 auto;width:25%;}.row-cols-sm-5>*{flex:0 0 auto;width:20%;}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%;}.col-sm-auto{flex:0 0 auto;width:auto;}.col-sm-1{flex:0 0 auto;width:8.33333333%;}.col-sm-2{flex:0 0 auto;width:16.66666667%;}.col-sm-3{flex:0 0 auto;width:25%;}.col-sm-4{flex:0 0 auto;width:33.33333333%;}.col-sm-5{flex:0 0 auto;width:41.66666667%;}.col-sm-6{flex:0 0 auto;width:50%;}.col-sm-7{flex:0 0 auto;width:58.33333333%;}.col-sm-8{flex:0 0 auto;width:66.66666667%;}.col-sm-9{flex:0 0 auto;width:75%;}.col-sm-10{flex:0 0 auto;width:83.33333333%;}.col-sm-11{flex:0 0 auto;width:91.66666667%;}.col-sm-12{flex:0 0 auto;width:100%;}.offset-sm-0{margin-left:0;}.offset-sm-1{margin-left:8.33333333%;}.offset-sm-2{margin-left:16.66666667%;}.offset-sm-3{margin-left:25%;}.offset-sm-4{margin-left:33.33333333%;}.offset-sm-5{margin-left:41.66666667%;}.offset-sm-6{margin-left:50%;}.offset-sm-7{margin-left:58.33333333%;}.offset-sm-8{margin-left:66.66666667%;}.offset-sm-9{margin-left:75%;}.offset-sm-10{margin-left:83.33333333%;}.offset-sm-11{margin-left:91.66666667%;}.g-sm-0,.gx-sm-0{--bs-gutter-x:0;}.g-sm-0,.gy-sm-0{--bs-gutter-y:0;}.g-sm-1,.gx-sm-1{--bs-gutter-x:.25rem;}.g-sm-1,.gy-sm-1{--bs-gutter-y:.25rem;}.g-sm-2,.gx-sm-2{--bs-gutter-x:.5rem;}.g-sm-2,.gy-sm-2{--bs-gutter-y:.5rem;}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem;}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem;}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem;}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem;}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem;}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem;}}@media(min-width:768px){.col-md{flex:1 0 0%;}.row-cols-md-auto>*{flex:0 0 auto;width:auto;}.row-cols-md-1>*{flex:0 0 auto;width:100%;}.row-cols-md-2>*{flex:0 0 auto;width:50%;}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%;}.row-cols-md-4>*{flex:0 0 auto;width:25%;}.row-cols-md-5>*{flex:0 0 auto;width:20%;}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%;}.col-md-auto{flex:0 0 auto;width:auto;}.col-md-1{flex:0 0 auto;width:8.33333333%;}.col-md-2{flex:0 0 auto;width:16.66666667%;}.col-md-3{flex:0 0 auto;width:25%;}.col-md-4{flex:0 0 auto;width:33.33333333%;}.col-md-5{flex:0 0 auto;width:41.66666667%;}.col-md-6{flex:0 0 auto;width:50%;}.col-md-7{flex:0 0 auto;width:58.33333333%;}.col-md-8{flex:0 0 auto;width:66.66666667%;}.col-md-9{flex:0 0 auto;width:75%;}.col-md-10{flex:0 0 auto;width:83.33333333%;}.col-md-11{flex:0 0 auto;width:91.66666667%;}.col-md-12{flex:0 0 auto;width:100%;}.offset-md-0{margin-left:0;}.offset-md-1{margin-left:8.33333333%;}.offset-md-2{margin-left:16.66666667%;}.offset-md-3{margin-left:25%;}.offset-md-4{margin-left:33.33333333%;}.offset-md-5{margin-left:41.66666667%;}.offset-md-6{margin-left:50%;}.offset-md-7{margin-left:58.33333333%;}.offset-md-8{margin-left:66.66666667%;}.offset-md-9{margin-left:75%;}.offset-md-10{margin-left:83.33333333%;}.offset-md-11{margin-left:91.66666667%;}.g-md-0,.gx-md-0{--bs-gutter-x:0;}.g-md-0,.gy-md-0{--bs-gutter-y:0;}.g-md-1,.gx-md-1{--bs-gutter-x:.25rem;}.g-md-1,.gy-md-1{--bs-gutter-y:.25rem;}.g-md-2,.gx-md-2{--bs-gutter-x:.5rem;}.g-md-2,.gy-md-2{--bs-gutter-y:.5rem;}.g-md-3,.gx-md-3{--bs-gutter-x:1rem;}.g-md-3,.gy-md-3{--bs-gutter-y:1rem;}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem;}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem;}.g-md-5,.gx-md-5{--bs-gutter-x:3rem;}.g-md-5,.gy-md-5{--bs-gutter-y:3rem;}}@media(min-width:992px){.col-lg{flex:1 0 0%;}.row-cols-lg-auto>*{flex:0 0 auto;width:auto;}.row-cols-lg-1>*{flex:0 0 auto;width:100%;}.row-cols-lg-2>*{flex:0 0 auto;width:50%;}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%;}.row-cols-lg-4>*{flex:0 0 auto;width:25%;}.row-cols-lg-5>*{flex:0 0 auto;width:20%;}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%;}.col-lg-auto{flex:0 0 auto;width:auto;}.col-lg-1{flex:0 0 auto;width:8.33333333%;}.col-lg-2{flex:0 0 auto;width:16.66666667%;}.col-lg-3{flex:0 0 auto;width:25%;}.col-lg-4{flex:0 0 auto;width:33.33333333%;}.col-lg-5{flex:0 0 auto;width:41.66666667%;}.col-lg-6{flex:0 0 auto;width:50%;}.col-lg-7{flex:0 0 auto;width:58.33333333%;}.col-lg-8{flex:0 0 auto;width:66.66666667%;}.col-lg-9{flex:0 0 auto;width:75%;}.col-lg-10{flex:0 0 auto;width:83.33333333%;}.col-lg-11{flex:0 0 auto;width:91.66666667%;}.col-lg-12{flex:0 0 auto;width:100%;}.offset-lg-0{margin-left:0;}.offset-lg-1{margin-left:8.33333333%;}.offset-lg-2{margin-left:16.66666667%;}.offset-lg-3{margin-left:25%;}.offset-lg-4{margin-left:33.33333333%;}.offset-lg-5{margin-left:41.66666667%;}.offset-lg-6{margin-left:50%;}.offset-lg-7{margin-left:58.33333333%;}.offset-lg-8{margin-left:66.66666667%;}.offset-lg-9{margin-left:75%;}.offset-lg-10{margin-left:83.33333333%;}.offset-lg-11{margin-left:91.66666667%;}.g-lg-0,.gx-lg-0{--bs-gutter-x:0;}.g-lg-0,.gy-lg-0{--bs-gutter-y:0;}.g-lg-1,.gx-lg-1{--bs-gutter-x:.25rem;}.g-lg-1,.gy-lg-1{--bs-gutter-y:.25rem;}.g-lg-2,.gx-lg-2{--bs-gutter-x:.5rem;}.g-lg-2,.gy-lg-2{--bs-gutter-y:.5rem;}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem;}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem;}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem;}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem;}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem;}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem;}}@media(min-width:1200px){.col-xl{flex:1 0 0%;}.row-cols-xl-auto>*{flex:0 0 auto;width:auto;}.row-cols-xl-1>*{flex:0 0 auto;width:100%;}.row-cols-xl-2>*{flex:0 0 auto;width:50%;}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%;}.row-cols-xl-4>*{flex:0 0 auto;width:25%;}.row-cols-xl-5>*{flex:0 0 auto;width:20%;}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%;}.col-xl-auto{flex:0 0 auto;width:auto;}.col-xl-1{flex:0 0 auto;width:8.33333333%;}.col-xl-2{flex:0 0 auto;width:16.66666667%;}.col-xl-3{flex:0 0 auto;width:25%;}.col-xl-4{flex:0 0 auto;width:33.33333333%;}.col-xl-5{flex:0 0 auto;width:41.66666667%;}.col-xl-6{flex:0 0 auto;width:50%;}.col-xl-7{flex:0 0 auto;width:58.33333333%;}.col-xl-8{flex:0 0 auto;width:66.66666667%;}.col-xl-9{flex:0 0 auto;width:75%;}.col-xl-10{flex:0 0 auto;width:83.33333333%;}.col-xl-11{flex:0 0 auto;width:91.66666667%;}.col-xl-12{flex:0 0 auto;width:100%;}.offset-xl-0{margin-left:0;}.offset-xl-1{margin-left:8.33333333%;}.offset-xl-2{margin-left:16.66666667%;}.offset-xl-3{margin-left:25%;}.offset-xl-4{margin-left:33.33333333%;}.offset-xl-5{margin-left:41.66666667%;}.offset-xl-6{margin-left:50%;}.offset-xl-7{margin-left:58.33333333%;}.offset-xl-8{margin-left:66.66666667%;}.offset-xl-9{margin-left:75%;}.offset-xl-10{margin-left:83.33333333%;}.offset-xl-11{margin-left:91.66666667%;}.g-xl-0,.gx-xl-0{--bs-gutter-x:0;}.g-xl-0,.gy-xl-0{--bs-gutter-y:0;}.g-xl-1,.gx-xl-1{--bs-gutter-x:.25rem;}.g-xl-1,.gy-xl-1{--bs-gutter-y:.25rem;}.g-xl-2,.gx-xl-2{--bs-gutter-x:.5rem;}.g-xl-2,.gy-xl-2{--bs-gutter-y:.5rem;}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem;}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem;}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem;}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem;}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem;}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem;}}@media(min-width:1400px){.col-xxl{flex:1 0 0%;}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto;}.row-cols-xxl-1>*{flex:0 0 auto;width:100%;}.row-cols-xxl-2>*{flex:0 0 auto;width:50%;}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%;}.row-cols-xxl-4>*{flex:0 0 auto;width:25%;}.row-cols-xxl-5>*{flex:0 0 auto;width:20%;}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%;}.col-xxl-auto{flex:0 0 auto;width:auto;}.col-xxl-1{flex:0 0 auto;width:8.33333333%;}.col-xxl-2{flex:0 0 auto;width:16.66666667%;}.col-xxl-3{flex:0 0 auto;width:25%;}.col-xxl-4{flex:0 0 auto;width:33.33333333%;}.col-xxl-5{flex:0 0 auto;width:41.66666667%;}.col-xxl-6{flex:0 0 auto;width:50%;}.col-xxl-7{flex:0 0 auto;width:58.33333333%;}.col-xxl-8{flex:0 0 auto;width:66.66666667%;}.col-xxl-9{flex:0 0 auto;width:75%;}.col-xxl-10{flex:0 0 auto;width:83.33333333%;}.col-xxl-11{flex:0 0 auto;width:91.66666667%;}.col-xxl-12{flex:0 0 auto;width:100%;}.offset-xxl-0{margin-left:0;}.offset-xxl-1{margin-left:8.33333333%;}.offset-xxl-2{margin-left:16.66666667%;}.offset-xxl-3{margin-left:25%;}.offset-xxl-4{margin-left:33.33333333%;}.offset-xxl-5{margin-left:41.66666667%;}.offset-xxl-6{margin-left:50%;}.offset-xxl-7{margin-left:58.33333333%;}.offset-xxl-8{margin-left:66.66666667%;}.offset-xxl-9{margin-left:75%;}.offset-xxl-10{margin-left:83.33333333%;}.offset-xxl-11{margin-left:91.66666667%;}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0;}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0;}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:.25rem;}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:.25rem;}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:.5rem;}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:.5rem;}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem;}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem;}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem;}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem;}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem;}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem;}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:#dee2e6;--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb),.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb),.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb),.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color);}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)));}.table>tbody{vertical-align:inherit;}.table>thead{vertical-align:bottom;}.table-group-divider{border-top:calc(var(--bs-border-width)*2) solid currentcolor;}.caption-top{caption-side:top;}.table-sm>:not(caption)>*>*{padding:.25rem .25rem;}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0;}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width);}.table-borderless>:not(caption)>*>*{border-bottom-width:0;}.table-borderless>:not(:first-child){border-top-width:0;}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg);}.table-striped-columns>:not(caption)>tr>:nth-child(even){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg);}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg);}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg);}.table-primary{--bs-table-color:#000;--bs-table-bg:#e8e1f5;--bs-table-border-color:#bab4c4;--bs-table-striped-bg:#dcd6e9;--bs-table-striped-color:#000;--bs-table-active-bg:#d1cbdd;--bs-table-active-color:#000;--bs-table-hover-bg:#d7d0e3;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-secondary{--bs-table-color:#000;--bs-table-bg:#d8e8f7;--bs-table-border-color:#adbac6;--bs-table-striped-bg:#cddceb;--bs-table-striped-color:#000;--bs-table-active-bg:#c2d1de;--bs-table-active-color:#000;--bs-table-hover-bg:#c8d7e4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color);}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch;}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch;}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch;}}.form-label{margin-bottom:.5rem;}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5;}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem;}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem;}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color);}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-control{transition:none;}}.form-control[type=file]{overflow:hidden;}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer;}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#c6b4e6;outline:0;box-shadow:0 0 0 .25rem rgba(140,104,205,.25);}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0;}.form-control::-webkit-datetime-edit{display:block;padding:0;}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1;}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1;}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none;}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg);}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0;}.form-control-plaintext:focus{outline:0;}.form-control-plaintext.form-control-sm,.form-control-plaintext.form-control-lg{padding-right:0;padding-left:0;}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width)*2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm);}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;margin-inline-end:.5rem;}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width)*2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg);}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;margin-inline-end:1rem;}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width)*2));}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width)*2));}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width)*2));}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width)*2));padding:.375rem;}.form-control-color:not(:disabled):not([readonly]){cursor:pointer;}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius);}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius);}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width)*2));}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width)*2));}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-select{transition:none;}}.form-select:focus{border-color:#c6b4e6;outline:0;box-shadow:0 0 0 .25rem rgba(140,104,205,.25);}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none;}.form-select:disabled{background-color:var(--bs-secondary-bg);}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color);}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm);}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg);}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem;}.form-check .form-check-input{float:left;margin-left:-1.5em;}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right;}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0;}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);print-color-adjust:exact;}.form-check-input[type=checkbox]{border-radius:.25em;}.form-check-input[type=radio]{border-radius:50%;}.form-check-input:active{filter:brightness(90%);}.form-check-input:focus{border-color:#c6b4e6;outline:0;box-shadow:0 0 0 .25rem rgba(140,104,205,.25);}.form-check-input:checked{background-color:#8c68cd;border-color:#8c68cd;}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");}.form-check-input[type=checkbox]:indeterminate{background-color:#8c68cd;border-color:#8c68cd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5;}.form-check-input[disabled]~.form-check-label,.form-check-input:disabled~.form-check-label{cursor:default;opacity:.5;}.form-switch{padding-left:2.5em;}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none;}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23c6b4e6'/%3e%3c/svg%3e");}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0;}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0;}.form-check-inline{display:inline-block;margin-right:1rem;}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none;}.btn-check[disabled]+.btn,.btn-check:disabled+.btn{pointer-events:none;filter:none;opacity:.65;}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e");}.form-range{width:100%;height:1.5rem;padding:0;appearance:none;background-color:transparent;}.form-range:focus{outline:0;}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(140,104,205,.25);}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(140,104,205,.25);}.form-range::-moz-focus-outer{border:0;}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;appearance:none;background-color:#8c68cd;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{transition:none;}}.form-range::-webkit-slider-thumb:active{background-color:#ddd2f0;}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem;}.form-range::-moz-range-thumb{width:1rem;height:1rem;appearance:none;background-color:#8c68cd;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{transition:none;}}.form-range::-moz-range-thumb:active{background-color:#ddd2f0;}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem;}.form-range:disabled{pointer-events:none;}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color);}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color);}.form-floating{position:relative;}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width)*2));min-height:calc(3.5rem + calc(var(--bs-border-width)*2));line-height:1.25;}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out;}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none;}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem;}.form-floating>.form-control::placeholder,.form-floating>.form-control-plaintext::placeholder{color:transparent;}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown),.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem;}.form-floating>.form-control:-webkit-autofill,.form-floating>.form-control-plaintext:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem;}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-control-plaintext~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem);}.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-control-plaintext~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem .375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius);}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem);}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0;}.form-floating>:disabled~label,.form-floating>.form-control:disabled~label{color:#6c757d;}.form-floating>:disabled~label::after,.form-floating>.form-control:disabled~label::after{background-color:var(--bs-secondary-bg);}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%;}.input-group>.form-control,.input-group>.form-select,.input-group>.form-floating{position:relative;flex:1 1 auto;width:1%;min-width:0;}.input-group>.form-control:focus,.input-group>.form-select:focus,.input-group>.form-floating:focus-within{z-index:5;}.input-group .btn{position:relative;z-index:2;}.input-group .btn:focus{z-index:5;}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);}.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text,.input-group-lg>.btn{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg);}.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text,.input-group-sm>.btn{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm);}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem;}.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0;}.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select{border-top-right-radius:0;border-bottom-right-radius:0;}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width)*-1);border-top-left-radius:0;border-bottom-left-radius:0;}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0;}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color);}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius);}.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip,.is-valid~.valid-feedback,.is-valid~.valid-tooltip{display:block;}.was-validated .form-control:valid,.form-control.is-valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-control:valid:focus,.form-control.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25);}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem);}.was-validated .form-select:valid,.form-select.is-valid{border-color:var(--bs-form-valid-border-color);}.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"],.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-select:valid:focus,.form-select.is-valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25);}.was-validated .form-control-color:valid,.form-control-color.is-valid{width:calc(3rem + calc(1.5em + .75rem));}.was-validated .form-check-input:valid,.form-check-input.is-valid{border-color:var(--bs-form-valid-border-color);}.was-validated .form-check-input:valid:checked,.form-check-input.is-valid:checked{background-color:var(--bs-form-valid-color);}.was-validated .form-check-input:valid:focus,.form-check-input.is-valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25);}.was-validated .form-check-input:valid~.form-check-label,.form-check-input.is-valid~.form-check-label{color:var(--bs-form-valid-color);}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em;}.was-validated .input-group>.form-control:not(:focus):valid,.input-group>.form-control:not(:focus).is-valid,.was-validated .input-group>.form-select:not(:focus):valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.input-group>.form-floating:not(:focus-within).is-valid{z-index:3;}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color);}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius);}.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip,.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip{display:block;}.was-validated .form-control:invalid,.form-control.is-invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-control:invalid:focus,.form-control.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25);}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem);}.was-validated .form-select:invalid,.form-select.is-invalid{border-color:var(--bs-form-invalid-border-color);}.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"],.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem);}.was-validated .form-select:invalid:focus,.form-select.is-invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25);}.was-validated .form-control-color:invalid,.form-control-color.is-invalid{width:calc(3rem + calc(1.5em + .75rem));}.was-validated .form-check-input:invalid,.form-check-input.is-invalid{border-color:var(--bs-form-invalid-border-color);}.was-validated .form-check-input:invalid:checked,.form-check-input.is-invalid:checked{background-color:var(--bs-form-invalid-color);}.was-validated .form-check-input:invalid:focus,.form-check-input.is-invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25);}.was-validated .form-check-input:invalid~.form-check-label,.form-check-input.is-invalid~.form-check-label{color:var(--bs-form-invalid-color);}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em;}.was-validated .input-group>.form-control:not(:focus):invalid,.input-group>.form-control:not(:focus).is-invalid,.was-validated .input-group>.form-select:not(:focus):invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.input-group>.form-floating:not(:focus-within).is-invalid{z-index:4;}.btn{--bs-btn-padding-x:.75rem;--bs-btn-padding-y:.375rem;--bs-btn-font-family:;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);--bs-btn-disabled-opacity:.65;--bs-btn-focus-box-shadow:0 0 0 .25rem rgba(var(--bs-btn-focus-shadow-rgb),.5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.btn{transition:none;}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color);}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow);}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow);}.btn-check:checked+.btn,:not(.btn-check)+.btn:active,.btn:first-child:active,.btn.active,.btn.show{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color);}.btn-check:checked+.btn:focus-visible,:not(.btn-check)+.btn:active:focus-visible,.btn:first-child:active:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow);}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow);}.btn:disabled,.btn.disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity);}.btn-primary{--bs-btn-color:#000;--bs-btn-bg:#8c68cd;--bs-btn-border-color:#8c68cd;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#9d7fd5;--bs-btn-hover-border-color:#9877d2;--bs-btn-focus-shadow-rgb:119,88,174;--bs-btn-active-color:#000;--bs-btn-active-bg:#a386d7;--bs-btn-active-border-color:#9877d2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#8c68cd;--bs-btn-disabled-border-color:#8c68cd;}.btn-secondary{--bs-btn-color:#000;--bs-btn-bg:#3c8cd6;--bs-btn-border-color:#3c8cd6;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#599ddc;--bs-btn-hover-border-color:#5098da;--bs-btn-focus-shadow-rgb:51,119,182;--bs-btn-active-color:#000;--bs-btn-active-bg:#63a3de;--bs-btn-active-border-color:#5098da;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#3c8cd6;--bs-btn-disabled-border-color:#3c8cd6;}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754;}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0;}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107;}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545;}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa;}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529;}.btn-accent{--bs-btn-color:#000;--bs-btn-bg:#9cec5b;--bs-btn-border-color:#9cec5b;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#abef74;--bs-btn-hover-border-color:#a6ee6b;--bs-btn-focus-shadow-rgb:133,201,77;--bs-btn-active-color:#000;--bs-btn-active-bg:#b0f07c;--bs-btn-active-border-color:#a6ee6b;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#9cec5b;--bs-btn-disabled-border-color:#9cec5b;}.btn-cinereous{--bs-btn-color:#000;--bs-btn-bg:#837569;--bs-btn-border-color:#837569;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#968a80;--bs-btn-hover-border-color:#8f8378;--bs-btn-focus-shadow-rgb:111,99,89;--bs-btn-active-color:#000;--bs-btn-active-bg:#9c9187;--bs-btn-active-border-color:#8f8378;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#837569;--bs-btn-disabled-border-color:#837569;}.btn-verdigris{--bs-btn-color:#000;--bs-btn-bg:#50c5b7;--bs-btn-border-color:#50c5b7;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#6acec2;--bs-btn-hover-border-color:#62cbbe;--bs-btn-focus-shadow-rgb:68,167,156;--bs-btn-active-color:#000;--bs-btn-active-bg:#73d1c5;--bs-btn-active-border-color:#62cbbe;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#50c5b7;--bs-btn-disabled-border-color:#50c5b7;}.btn-icterine{--bs-btn-color:#000;--bs-btn-bg:#f0f465;--bs-btn-border-color:#f0f465;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f2f67c;--bs-btn-hover-border-color:#f2f574;--bs-btn-focus-shadow-rgb:204,207,86;--bs-btn-active-color:#000;--bs-btn-active-bg:#f3f684;--bs-btn-active-border-color:#f2f574;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f0f465;--bs-btn-disabled-border-color:#f0f465;}.btn-mute{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d;}.btn-outline-primary{--bs-btn-color:#8c68cd;--bs-btn-border-color:#8c68cd;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#8c68cd;--bs-btn-hover-border-color:#8c68cd;--bs-btn-focus-shadow-rgb:140,104,205;--bs-btn-active-color:#000;--bs-btn-active-bg:#8c68cd;--bs-btn-active-border-color:#8c68cd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#8c68cd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#8c68cd;--bs-gradient:none;}.btn-outline-secondary{--bs-btn-color:#3c8cd6;--bs-btn-border-color:#3c8cd6;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#3c8cd6;--bs-btn-hover-border-color:#3c8cd6;--bs-btn-focus-shadow-rgb:60,140,214;--bs-btn-active-color:#000;--bs-btn-active-bg:#3c8cd6;--bs-btn-active-border-color:#3c8cd6;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#3c8cd6;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#3c8cd6;--bs-gradient:none;}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none;}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none;}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none;}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none;}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none;}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none;}.btn-outline-accent{--bs-btn-color:#9cec5b;--bs-btn-border-color:#9cec5b;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#9cec5b;--bs-btn-hover-border-color:#9cec5b;--bs-btn-focus-shadow-rgb:156,236,91;--bs-btn-active-color:#000;--bs-btn-active-bg:#9cec5b;--bs-btn-active-border-color:#9cec5b;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#9cec5b;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#9cec5b;--bs-gradient:none;}.btn-outline-cinereous{--bs-btn-color:#837569;--bs-btn-border-color:#837569;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#837569;--bs-btn-hover-border-color:#837569;--bs-btn-focus-shadow-rgb:131,117,105;--bs-btn-active-color:#000;--bs-btn-active-bg:#837569;--bs-btn-active-border-color:#837569;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#837569;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#837569;--bs-gradient:none;}.btn-outline-verdigris{--bs-btn-color:#50c5b7;--bs-btn-border-color:#50c5b7;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#50c5b7;--bs-btn-hover-border-color:#50c5b7;--bs-btn-focus-shadow-rgb:80,197,183;--bs-btn-active-color:#000;--bs-btn-active-bg:#50c5b7;--bs-btn-active-border-color:#50c5b7;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#50c5b7;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#50c5b7;--bs-gradient:none;}.btn-outline-icterine{--bs-btn-color:#f0f465;--bs-btn-border-color:#f0f465;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f0f465;--bs-btn-hover-border-color:#f0f465;--bs-btn-focus-shadow-rgb:240,244,101;--bs-btn-active-color:#000;--bs-btn-active-bg:#f0f465;--bs-btn-active-border-color:#f0f465;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#f0f465;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f0f465;--bs-gradient:none;}.btn-outline-mute{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none;}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:119,88,174;text-decoration:underline;}.btn-link:focus-visible{color:var(--bs-btn-color);}.btn-link:hover{color:var(--bs-btn-hover-color);}.btn-lg,.btn-group-lg>.btn{--bs-btn-padding-y:.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg);}.btn-sm,.btn-group-sm>.btn{--bs-btn-padding-y:.25rem;--bs-btn-padding-x:.5rem;--bs-btn-font-size:.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm);}.fade{transition:opacity .15s linear;}@media(prefers-reduced-motion:reduce){.fade{transition:none;}}.fade:not(.show){opacity:0;}.collapse:not(.show){display:none;}.collapsing{height:0;overflow:hidden;transition:height .35s ease;}@media(prefers-reduced-motion:reduce){.collapsing{transition:none;}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease;}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none;}}.dropup,.dropend,.dropdown,.dropstart,.dropup-center,.dropdown-center{position:relative;}.dropdown-toggle{white-space:nowrap;}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent;}.dropdown-toggle:empty::after{margin-left:0;}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:.5rem;--bs-dropdown-spacer:.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#8c68cd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius);}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer);}.dropdown-menu-start{--bs-position:start;}.dropdown-menu-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-end{--bs-position:end;}.dropdown-menu-end[data-bs-popper]{right:0;left:auto;}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position:start;}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-sm-end{--bs-position:end;}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position:start;}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-md-end{--bs-position:end;}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position:start;}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-lg-end{--bs-position:end;}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position:start;}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-xl-end{--bs-position:end;}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto;}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start;}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0;}.dropdown-menu-xxl-end{--bs-position:end;}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto;}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer);}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent;}.dropup .dropdown-toggle:empty::after{margin-left:0;}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer);}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid;}.dropend .dropdown-toggle:empty::after{margin-left:0;}.dropend .dropdown-toggle::after{vertical-align:0;}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer);}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";}.dropstart .dropdown-toggle::after{display:none;}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent;}.dropstart .dropdown-toggle:empty::after{margin-left:0;}.dropstart .dropdown-toggle::before{vertical-align:0;}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1;}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0);}.dropdown-item:hover,.dropdown-item:focus{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg);}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg);}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent;}.dropdown-menu.show{display:block;}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap;}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color);}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow:;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255,255,255,.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#8c68cd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd;}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle;}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;flex:1 1 auto;}.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn:hover,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn.active{z-index:1;}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start;}.btn-toolbar .input-group{width:auto;}.btn-group{border-radius:var(--bs-border-radius);}.btn-group>:not(.btn-check:first-child)+.btn,.btn-group>.btn-group:not(:first-child){margin-left:calc(var(--bs-border-width)*-1);}.btn-group>.btn:not(:last-child):not(.dropdown-toggle),.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0;}.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn,.btn-group>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0;}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem;}.dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after{margin-left:0;}.dropstart .dropdown-toggle-split::before{margin-right:0;}.btn-sm+.dropdown-toggle-split,.btn-group-sm>.btn+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem;}.btn-lg+.dropdown-toggle-split,.btn-group-lg>.btn+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem;}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center;}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%;}.btn-group-vertical>.btn:not(:first-child),.btn-group-vertical>.btn-group:not(:first-child){margin-top:calc(var(--bs-border-width)*-1);}.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),.btn-group-vertical>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-bottom-left-radius:0;}.btn-group-vertical>.btn~.btn,.btn-group-vertical>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-top-right-radius:0;}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:.5rem;--bs-nav-link-font-weight:;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none;}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:none;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.nav-link{transition:none;}}.nav-link:hover,.nav-link:focus{color:var(--bs-nav-link-hover-color);}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(140,104,205,.25);}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default;}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color);}.nav-tabs .nav-link{margin-bottom:calc(-1*var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius);}.nav-tabs .nav-link:hover,.nav-tabs .nav-link:focus{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color);}.nav-tabs .nav-link.active,.nav-tabs .nav-item.show .nav-link{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color);}.nav-tabs .dropdown-menu{margin-top:calc(-1*var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0;}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#8c68cd;}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius);}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg);}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap);}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent;}.nav-underline .nav-link:hover,.nav-underline .nav-link:focus{border-bottom-color:currentcolor;}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor;}.nav-fill>.nav-link,.nav-fill .nav-item{flex:1 1 auto;text-align:center;}.nav-justified>.nav-link,.nav-justified .nav-item{flex-basis:0;flex-grow:1;text-align:center;}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%;}.tab-content>.tab-pane{display:none;}.tab-content>.active{display:block;}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb),.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb),.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb),.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb),1);--bs-navbar-brand-padding-y:.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb),1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb),1);--bs-navbar-nav-link-padding-x:.5rem;--bs-navbar-toggler-padding-y:.25rem;--bs-navbar-toggler-padding-x:.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb),.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:.25rem;--bs-navbar-toggler-transition:box-shadow .15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x);}.navbar>.container,.navbar>.container-fluid,.navbar>.container-sm,.navbar>.container-md,.navbar>.container-lg,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between;}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap;}.navbar-brand:hover,.navbar-brand:focus{color:var(--bs-navbar-brand-hover-color);}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:.5rem;--bs-nav-link-font-weight:;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none;}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color);}.navbar-nav .dropdown-menu{position:static;}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color);}.navbar-text a,.navbar-text a:hover,.navbar-text a:focus{color:var(--bs-navbar-active-color);}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center;}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition);}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none;}}.navbar-toggler:hover{text-decoration:none;}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width);}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%;}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto;}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-sm .navbar-nav{flex-direction:row;}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x);}.navbar-expand-sm .navbar-nav-scroll{overflow:visible;}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-sm .navbar-toggler{display:none;}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none;}.navbar-expand-sm .offcanvas .offcanvas-header{display:none;}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-md .navbar-nav{flex-direction:row;}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x);}.navbar-expand-md .navbar-nav-scroll{overflow:visible;}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-md .navbar-toggler{display:none;}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none;}.navbar-expand-md .offcanvas .offcanvas-header{display:none;}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-lg .navbar-nav{flex-direction:row;}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x);}.navbar-expand-lg .navbar-nav-scroll{overflow:visible;}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-lg .navbar-toggler{display:none;}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none;}.navbar-expand-lg .offcanvas .offcanvas-header{display:none;}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-xl .navbar-nav{flex-direction:row;}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x);}.navbar-expand-xl .navbar-nav-scroll{overflow:visible;}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-xl .navbar-toggler{display:none;}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none;}.navbar-expand-xl .offcanvas .offcanvas-header{display:none;}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand-xxl .navbar-nav{flex-direction:row;}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x);}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible;}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand-xxl .navbar-toggler{display:none;}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none;}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none;}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start;}.navbar-expand .navbar-nav{flex-direction:row;}.navbar-expand .navbar-nav .dropdown-menu{position:absolute;}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x);}.navbar-expand .navbar-nav-scroll{overflow:visible;}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto;}.navbar-expand .navbar-toggler{display:none;}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none;}.navbar-expand .offcanvas .offcanvas-header{display:none;}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255,255,255,.55);--bs-navbar-hover-color:rgba(255,255,255,.75);--bs-navbar-disabled-color:rgba(255,255,255,.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255,255,255,.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:.5rem;--bs-card-title-color:;--bs-card-subtitle-color:;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow:;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb),.03);--bs-card-cap-color:;--bs-card-height:;--bs-card-color:;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius);}.card>hr{margin-right:0;margin-left:0;}.card>.list-group{border-top:inherit;border-bottom:inherit;}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius);}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius);}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0;}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color);}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color);}.card-subtitle{margin-top:calc(-.5*var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color);}.card-text:last-child{margin-bottom:0;}.card-link+.card-link{margin-left:var(--bs-card-spacer-x);}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color);}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0;}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color);}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius);}.card-header-tabs{margin-right:calc(-.5*var(--bs-card-cap-padding-x));margin-bottom:calc(-1*var(--bs-card-cap-padding-y));margin-left:calc(-.5*var(--bs-card-cap-padding-x));border-bottom:0;}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg);}.card-header-pills{margin-right:calc(-.5*var(--bs-card-cap-padding-x));margin-left:calc(-.5*var(--bs-card-cap-padding-x));}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius);}.card-img,.card-img-top,.card-img-bottom{width:100%;}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius);}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius);}.card-group>.card{margin-bottom:var(--bs-card-group-margin);}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap;}.card-group>.card{flex:1 0 0%;margin-bottom:0;}.card-group>.card+.card{margin-left:0;border-left:0;}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0;}.card-group>.card:not(:last-child) .card-img-top,.card-group>.card:not(:last-child) .card-header{border-top-right-radius:0;}.card-group>.card:not(:last-child) .card-img-bottom,.card-group>.card:not(:last-child) .card-footer{border-bottom-right-radius:0;}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0;}.card-group>.card:not(:first-child) .card-img-top,.card-group>.card:not(:first-child) .card-header{border-top-left-radius:0;}.card-group>.card:not(:first-child) .card-img-bottom,.card-group>.card:not(:first-child) .card-footer{border-bottom-left-radius:0;}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform .2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23382a52' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 .25rem rgba(140,104,205,.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle);}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition);}@media(prefers-reduced-motion:reduce){.accordion-button{transition:none;}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1*var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color);}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform);}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition);}@media(prefers-reduced-motion:reduce){.accordion-button::after{transition:none;}}.accordion-button:hover{z-index:2;}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow);}.accordion-header{margin-bottom:0;}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color);}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius);}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius);}.accordion-item:not(:first-of-type){border-top:0;}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius);}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius);}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius);}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x);}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0;}.accordion-flush>.accordion-item:first-child{border-top:0;}.accordion-flush>.accordion-item:last-child{border-bottom:0;}.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0;}.accordion-flush>.accordion-item>.accordion-collapse{border-radius:0;}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23baa4e1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23baa4e1'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg:;--bs-breadcrumb-border-radius:;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius);}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x);}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider,"/");}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color);}.pagination{--bs-pagination-padding-x:.75rem;--bs-pagination-padding-y:.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 .25rem rgba(140,104,205,.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#8c68cd;--bs-pagination-active-border-color:#8c68cd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none;}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;}@media(prefers-reduced-motion:reduce){.page-link{transition:none;}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color);}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow);}.page-link.active,.active>.page-link{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color);}.page-link.disabled,.disabled>.page-link{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color);}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width)*-1);}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius);}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius);}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg);}.pagination-sm{--bs-pagination-padding-x:.5rem;--bs-pagination-padding-y:.25rem;--bs-pagination-font-size:.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm);}.badge{--bs-badge-padding-x:.65em;--bs-badge-padding-y:.35em;--bs-badge-font-size:.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius);}.badge:empty{display:none;}.btn .badge{position:relative;top:-1px;}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius);}.alert-heading{color:inherit;}.alert-link{font-weight:700;color:var(--bs-alert-link-color);}.alert-dismissible{padding-right:3rem;}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem;}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis);}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis);}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis);}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis);}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis);}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis);}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis);}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis);}.alert-accent{--bs-alert-color:var(--bs-accent-text-emphasis);--bs-alert-bg:var(--bs-accent-bg-subtle);--bs-alert-border-color:var(--bs-accent-border-subtle);--bs-alert-link-color:var(--bs-accent-text-emphasis);}.alert-cinereous{--bs-alert-color:var(--bs-cinereous-text-emphasis);--bs-alert-bg:var(--bs-cinereous-bg-subtle);--bs-alert-border-color:var(--bs-cinereous-border-subtle);--bs-alert-link-color:var(--bs-cinereous-text-emphasis);}.alert-verdigris{--bs-alert-color:var(--bs-verdigris-text-emphasis);--bs-alert-bg:var(--bs-verdigris-bg-subtle);--bs-alert-border-color:var(--bs-verdigris-border-subtle);--bs-alert-link-color:var(--bs-verdigris-text-emphasis);}.alert-icterine{--bs-alert-color:var(--bs-icterine-text-emphasis);--bs-alert-bg:var(--bs-icterine-bg-subtle);--bs-alert-border-color:var(--bs-icterine-border-subtle);--bs-alert-link-color:var(--bs-icterine-text-emphasis);}.alert-mute{--bs-alert-color:var(--bs-mute-text-emphasis);--bs-alert-bg:var(--bs-mute-bg-subtle);--bs-alert-border-color:var(--bs-mute-border-subtle);--bs-alert-link-color:var(--bs-mute-text-emphasis);}@keyframes progress-bar-stripes{0%{background-position-x:1rem;}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#8c68cd;--bs-progress-bar-transition:width .6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius);}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition);}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none;}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height);}.progress-stacked>.progress{overflow:visible;}.progress-stacked>.progress>.progress-bar{width:100%;}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes;}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none;}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#8c68cd;--bs-list-group-active-border-color:#8c68cd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius);}.list-group-numbered{list-style-type:none;counter-reset:section;}.list-group-numbered>.list-group-item::before{content:counters(section,".") ". ";counter-increment:section;}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit;}.list-group-item-action:hover,.list-group-item-action:focus{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg);}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg);}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color);}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit;}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit;}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg);}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color);}.list-group-item+.list-group-item{border-top-width:0;}.list-group-item+.list-group-item.active{margin-top:calc(-1*var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width);}.list-group-horizontal{flex-direction:row;}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0;}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0;}.list-group-horizontal>.list-group-item.active{margin-top:0;}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0;}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width);}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row;}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0;}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0;}.list-group-horizontal-sm>.list-group-item.active{margin-top:0;}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0;}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width);}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row;}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0;}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0;}.list-group-horizontal-md>.list-group-item.active{margin-top:0;}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0;}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width);}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row;}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0;}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0;}.list-group-horizontal-lg>.list-group-item.active{margin-top:0;}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0;}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width);}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row;}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0;}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0;}.list-group-horizontal-xl>.list-group-item.active{margin-top:0;}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0;}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width);}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row;}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0;}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0;}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0;}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0;}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1*var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width);}}.list-group-flush{border-radius:0;}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width);}.list-group-flush>.list-group-item:last-child{border-bottom-width:0;}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis);}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis);}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis);}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis);}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis);}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis);}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis);}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis);}.list-group-item-accent{--bs-list-group-color:var(--bs-accent-text-emphasis);--bs-list-group-bg:var(--bs-accent-bg-subtle);--bs-list-group-border-color:var(--bs-accent-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-accent-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-accent-border-subtle);--bs-list-group-active-color:var(--bs-accent-bg-subtle);--bs-list-group-active-bg:var(--bs-accent-text-emphasis);--bs-list-group-active-border-color:var(--bs-accent-text-emphasis);}.list-group-item-cinereous{--bs-list-group-color:var(--bs-cinereous-text-emphasis);--bs-list-group-bg:var(--bs-cinereous-bg-subtle);--bs-list-group-border-color:var(--bs-cinereous-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-cinereous-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-cinereous-border-subtle);--bs-list-group-active-color:var(--bs-cinereous-bg-subtle);--bs-list-group-active-bg:var(--bs-cinereous-text-emphasis);--bs-list-group-active-border-color:var(--bs-cinereous-text-emphasis);}.list-group-item-verdigris{--bs-list-group-color:var(--bs-verdigris-text-emphasis);--bs-list-group-bg:var(--bs-verdigris-bg-subtle);--bs-list-group-border-color:var(--bs-verdigris-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-verdigris-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-verdigris-border-subtle);--bs-list-group-active-color:var(--bs-verdigris-bg-subtle);--bs-list-group-active-bg:var(--bs-verdigris-text-emphasis);--bs-list-group-active-border-color:var(--bs-verdigris-text-emphasis);}.list-group-item-icterine{--bs-list-group-color:var(--bs-icterine-text-emphasis);--bs-list-group-bg:var(--bs-icterine-bg-subtle);--bs-list-group-border-color:var(--bs-icterine-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-icterine-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-icterine-border-subtle);--bs-list-group-active-color:var(--bs-icterine-bg-subtle);--bs-list-group-active-bg:var(--bs-icterine-text-emphasis);--bs-list-group-active-border-color:var(--bs-icterine-text-emphasis);}.list-group-item-mute{--bs-list-group-color:var(--bs-mute-text-emphasis);--bs-list-group-bg:var(--bs-mute-bg-subtle);--bs-list-group-border-color:var(--bs-mute-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-mute-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-mute-border-subtle);--bs-list-group-active-color:var(--bs-mute-bg-subtle);--bs-list-group-active-bg:var(--bs-mute-text-emphasis);--bs-list-group-active-border-color:var(--bs-mute-text-emphasis);}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:.5;--bs-btn-close-hover-opacity:.75;--bs-btn-close-focus-shadow:0 0 0 .25rem rgba(140,104,205,.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity);}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity);}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity);}.btn-close:disabled,.btn-close.disabled{pointer-events:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity);}.btn-close-white{filter:var(--bs-btn-close-white-filter);}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter);}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:.75rem;--bs-toast-padding-y:.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:.875rem;--bs-toast-color:;--bs-toast-bg:rgba(var(--bs-body-bg-rgb),.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb),.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius);}.toast.showing{opacity:0;}.toast:not(.show){display:none;}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:max-content;max-width:100%;pointer-events:none;}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing);}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));}.toast-header .btn-close{margin-right:calc(-.5*var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x);}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word;}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:.5rem;--bs-modal-color:;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:.5rem;--bs-modal-footer-bg:;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0;}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none;}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px);}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none;}}.modal.show .modal-dialog{transform:none;}.modal.modal-static .modal-dialog{transform:scale(1.02);}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin)*2);}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden;}.modal-dialog-scrollable .modal-body{overflow-y:auto;}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin)*2);}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0;}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg);}.modal-backdrop.fade{opacity:0;}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity);}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius);}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y)*.5) calc(var(--bs-modal-header-padding-x)*.5);margin:calc(-.5*var(--bs-modal-header-padding-y)) calc(-.5*var(--bs-modal-header-padding-x)) calc(-.5*var(--bs-modal-header-padding-y)) auto;}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height);}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding);}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap)*.5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius);}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap)*.5);}@media(min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow);}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto;}.modal-sm{--bs-modal-width:300px;}}@media(min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px;}}@media(min-width:1200px){.modal-xl{--bs-modal-width:1140px;}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen .modal-header,.modal-fullscreen .modal-footer{border-radius:0;}.modal-fullscreen .modal-body{overflow-y:auto;}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-sm-down .modal-header,.modal-fullscreen-sm-down .modal-footer{border-radius:0;}.modal-fullscreen-sm-down .modal-body{overflow-y:auto;}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-md-down .modal-header,.modal-fullscreen-md-down .modal-footer{border-radius:0;}.modal-fullscreen-md-down .modal-body{overflow-y:auto;}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-lg-down .modal-header,.modal-fullscreen-lg-down .modal-footer{border-radius:0;}.modal-fullscreen-lg-down .modal-body{overflow-y:auto;}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-xl-down .modal-header,.modal-fullscreen-xl-down .modal-footer{border-radius:0;}.modal-fullscreen-xl-down .modal-body{overflow-y:auto;}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0;}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0;}.modal-fullscreen-xxl-down .modal-header,.modal-fullscreen-xxl-down .modal-footer{border-radius:0;}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto;}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:.5rem;--bs-tooltip-padding-y:.25rem;--bs-tooltip-margin:;--bs-tooltip-font-size:.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:.9;--bs-tooltip-arrow-width:.8rem;--bs-tooltip-arrow-height:.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:"Inconsolata",monospace;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0;}.tooltip.show{opacity:var(--bs-tooltip-opacity);}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height);}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid;}.bs-tooltip-top .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow{bottom:calc(-1*var(--bs-tooltip-arrow-height));}.bs-tooltip-top .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-top-color:var(--bs-tooltip-bg);}.bs-tooltip-end .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow{left:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width);}.bs-tooltip-end .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width)*.5) 0;border-right-color:var(--bs-tooltip-bg);}.bs-tooltip-bottom .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow{top:calc(-1*var(--bs-tooltip-arrow-height));}.bs-tooltip-bottom .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg);}.bs-tooltip-start .tooltip-arrow,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow{right:calc(-1*var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width);}.bs-tooltip-start .tooltip-arrow::before,.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width)*.5) 0 calc(var(--bs-tooltip-arrow-width)*.5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg);}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius);}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:"Inconsolata",monospace;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius);}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height);}.popover .popover-arrow::before,.popover .popover-arrow::after{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0;}.bs-popover-top>.popover-arrow,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow{bottom:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0;}.bs-popover-top>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border);}.bs-popover-top>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg);}.bs-popover-end>.popover-arrow,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow{left:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width);}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width)*.5) 0;}.bs-popover-end>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border);}.bs-popover-end>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg);}.bs-popover-bottom>.popover-arrow,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow{top:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{border-width:0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height);}.bs-popover-bottom>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border);}.bs-popover-bottom>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg);}.bs-popover-bottom .popover-header::before,.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5*var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg);}.bs-popover-start>.popover-arrow,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow{right:calc(-1*(var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width);}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{border-width:calc(var(--bs-popover-arrow-width)*.5) 0 calc(var(--bs-popover-arrow-width)*.5) var(--bs-popover-arrow-height);}.bs-popover-start>.popover-arrow::before,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border);}.bs-popover-start>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg);}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius);}.popover-header:empty{display:none;}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color);}.carousel{position:relative;}.carousel.pointer-event{touch-action:pan-y;}.carousel-inner{position:relative;width:100%;overflow:hidden;}.carousel-inner::after{display:block;clear:both;content:"";}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;backface-visibility:hidden;transition:transform .6s ease-in-out;}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none;}}.carousel-item.active,.carousel-item-next,.carousel-item-prev{display:block;}.carousel-item-next:not(.carousel-item-start),.active.carousel-item-end{transform:translateX(100%);}.carousel-item-prev:not(.carousel-item-end),.active.carousel-item-start{transform:translateX(-100%);}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none;}.carousel-fade .carousel-item.active,.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end{z-index:1;opacity:1;}.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{z-index:0;opacity:0;transition:opacity 0s .6s;}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-start,.carousel-fade .active.carousel-item-end{transition:none;}}.carousel-control-prev,.carousel-control-next{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:none;border:0;opacity:.5;transition:opacity .15s ease;}@media(prefers-reduced-motion:reduce){.carousel-control-prev,.carousel-control-next{transition:none;}}.carousel-control-prev:hover,.carousel-control-prev:focus,.carousel-control-next:hover,.carousel-control-next:focus{color:#fff;text-decoration:none;outline:0;opacity:.9;}.carousel-control-prev{left:0;}.carousel-control-next{right:0;}.carousel-control-prev-icon,.carousel-control-next-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%;}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e");}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease;}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none;}}.carousel-indicators .active{opacity:1;}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center;}.carousel-dark .carousel-control-prev-icon,.carousel-dark .carousel-control-next-icon{filter:invert(1) grayscale(100);}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000;}.carousel-dark .carousel-caption{color:#000;}[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon{filter:invert(1) grayscale(100);}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000;}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000;}.spinner-grow,.spinner-border{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name);}@keyframes spinner-border{to{transform:rotate(360deg);}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-.125em;--bs-spinner-border-width:.25em;--bs-spinner-animation-speed:.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent;}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:.2em;}@keyframes spinner-grow{0%{transform:scale(0);}50%{opacity:1;transform:none;}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-.125em;--bs-spinner-animation-speed:.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0;}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s;}}.offcanvas,.offcanvas-xxl,.offcanvas-xl,.offcanvas-lg,.offcanvas-md,.offcanvas-sm{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform .3s ease-in-out;--bs-offcanvas-title-line-height:1.5;}@media(max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition);}}@media(max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none;}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%);}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%);}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%);}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%);}}@media(max-width:575.98px){.offcanvas-sm.showing,.offcanvas-sm.show:not(.hiding){transform:none;}}@media(max-width:575.98px){.offcanvas-sm.showing,.offcanvas-sm.hiding,.offcanvas-sm.show{visibility:visible;}}@media(min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important;}.offcanvas-sm .offcanvas-header{display:none;}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important;}}@media(max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition);}}@media(max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none;}}@media(max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%);}}@media(max-width:767.98px){.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%);}}@media(max-width:767.98px){.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%);}}@media(max-width:767.98px){.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%);}}@media(max-width:767.98px){.offcanvas-md.showing,.offcanvas-md.show:not(.hiding){transform:none;}}@media(max-width:767.98px){.offcanvas-md.showing,.offcanvas-md.hiding,.offcanvas-md.show{visibility:visible;}}@media(min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important;}.offcanvas-md .offcanvas-header{display:none;}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important;}}@media(max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition);}}@media(max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none;}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%);}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%);}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%);}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%);}}@media(max-width:991.98px){.offcanvas-lg.showing,.offcanvas-lg.show:not(.hiding){transform:none;}}@media(max-width:991.98px){.offcanvas-lg.showing,.offcanvas-lg.hiding,.offcanvas-lg.show{visibility:visible;}}@media(min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important;}.offcanvas-lg .offcanvas-header{display:none;}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important;}}@media(max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition);}}@media(max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none;}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%);}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%);}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%);}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%);}}@media(max-width:1199.98px){.offcanvas-xl.showing,.offcanvas-xl.show:not(.hiding){transform:none;}}@media(max-width:1199.98px){.offcanvas-xl.showing,.offcanvas-xl.hiding,.offcanvas-xl.show{visibility:visible;}}@media(min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important;}.offcanvas-xl .offcanvas-header{display:none;}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important;}}@media(max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition);}}@media(max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none;}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%);}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%);}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%);}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%);}}@media(max-width:1399.98px){.offcanvas-xxl.showing,.offcanvas-xxl.show:not(.hiding){transform:none;}}@media(max-width:1399.98px){.offcanvas-xxl.showing,.offcanvas-xxl.hiding,.offcanvas-xxl.show{visibility:visible;}}@media(min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important;}.offcanvas-xxl .offcanvas-header{display:none;}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important;}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition);}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none;}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%);}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%);}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%);}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%);}.offcanvas.showing,.offcanvas.show:not(.hiding){transform:none;}.offcanvas.showing,.offcanvas.hiding,.offcanvas.show{visibility:visible;}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000;}.offcanvas-backdrop.fade{opacity:0;}.offcanvas-backdrop.show{opacity:.5;}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y)*.5) calc(var(--bs-offcanvas-padding-x)*.5);margin:calc(-.5*var(--bs-offcanvas-padding-y)) calc(-.5*var(--bs-offcanvas-padding-x)) calc(-.5*var(--bs-offcanvas-padding-y)) auto;}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height);}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto;}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5;}.placeholder.btn::before{display:inline-block;content:"";}.placeholder-xs{min-height:.6em;}.placeholder-sm{min-height:.8em;}.placeholder-lg{min-height:1.2em;}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite;}@keyframes placeholder-glow{50%{opacity:.2;}}.placeholder-wave{mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);mask-size:200% 100%;animation:placeholder-wave 2s linear infinite;}@keyframes placeholder-wave{100%{mask-position:-200% 0%;}}.clearfix::after{display:block;clear:both;content:"";}.text-bg-primary{color:#000!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-secondary{color:#000!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-accent{color:#000!important;background-color:RGBA(var(--bs-accent-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-cinereous{color:#000!important;background-color:RGBA(var(--bs-cinereous-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-verdigris{color:#000!important;background-color:RGBA(var(--bs-verdigris-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-icterine{color:#000!important;background-color:RGBA(var(--bs-icterine-rgb),var(--bs-bg-opacity,1))!important;}.text-bg-mute{color:#fff!important;background-color:RGBA(var(--bs-mute-rgb),var(--bs-bg-opacity,1))!important;}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;}.link-primary:hover,.link-primary:focus{color:RGBA(163,134,215,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(163,134,215,var(--bs-link-underline-opacity,1))!important;}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;}.link-secondary:hover,.link-secondary:focus{color:RGBA(99,163,222,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(99,163,222,var(--bs-link-underline-opacity,1))!important;}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;}.link-success:hover,.link-success:focus{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;}.link-info:hover,.link-info:focus{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;}.link-warning:hover,.link-warning:focus{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;}.link-danger:hover,.link-danger:focus{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;}.link-light:hover,.link-light:focus{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;}.link-dark:hover,.link-dark:focus{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;}.link-accent{color:RGBA(var(--bs-accent-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-accent-rgb),var(--bs-link-underline-opacity,1))!important;}.link-accent:hover,.link-accent:focus{color:RGBA(176,240,124,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(176,240,124,var(--bs-link-underline-opacity,1))!important;}.link-cinereous{color:RGBA(var(--bs-cinereous-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-cinereous-rgb),var(--bs-link-underline-opacity,1))!important;}.link-cinereous:hover,.link-cinereous:focus{color:RGBA(156,145,135,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(156,145,135,var(--bs-link-underline-opacity,1))!important;}.link-verdigris{color:RGBA(var(--bs-verdigris-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-verdigris-rgb),var(--bs-link-underline-opacity,1))!important;}.link-verdigris:hover,.link-verdigris:focus{color:RGBA(115,209,197,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(115,209,197,var(--bs-link-underline-opacity,1))!important;}.link-icterine{color:RGBA(var(--bs-icterine-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-icterine-rgb),var(--bs-link-underline-opacity,1))!important;}.link-icterine:hover,.link-icterine:focus{color:RGBA(243,246,132,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(243,246,132,var(--bs-link-underline-opacity,1))!important;}.link-mute{color:RGBA(var(--bs-mute-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-mute-rgb),var(--bs-link-underline-opacity,1))!important;}.link-mute:hover,.link-mute:focus{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;}.link-body-emphasis:hover,.link-body-emphasis:focus{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,.75))!important;}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color);}.icon-link{display:inline-flex;gap:.375rem;align-items:center;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,.5));text-underline-offset:.25em;backface-visibility:hidden;}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform;}@media(prefers-reduced-motion:reduce){.icon-link>.bi{transition:none;}}.icon-link-hover:hover>.bi,.icon-link-hover:focus-visible>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0));}.ratio{position:relative;width:100%;}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:"";}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%;}.ratio-1x1{--bs-aspect-ratio:100%;}.ratio-4x3{--bs-aspect-ratio:75%;}.ratio-16x9{--bs-aspect-ratio:56.25%;}.ratio-21x9{--bs-aspect-ratio:42.8571428571%;}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030;}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030;}.sticky-top{position:sticky;top:0;z-index:1020;}.sticky-bottom{position:sticky;bottom:0;z-index:1020;}@media(min-width:576px){.sticky-sm-top{position:sticky;top:0;z-index:1020;}.sticky-sm-bottom{position:sticky;bottom:0;z-index:1020;}}@media(min-width:768px){.sticky-md-top{position:sticky;top:0;z-index:1020;}.sticky-md-bottom{position:sticky;bottom:0;z-index:1020;}}@media(min-width:992px){.sticky-lg-top{position:sticky;top:0;z-index:1020;}.sticky-lg-bottom{position:sticky;bottom:0;z-index:1020;}}@media(min-width:1200px){.sticky-xl-top{position:sticky;top:0;z-index:1020;}.sticky-xl-bottom{position:sticky;bottom:0;z-index:1020;}}@media(min-width:1400px){.sticky-xxl-top{position:sticky;top:0;z-index:1020;}.sticky-xxl-bottom{position:sticky;bottom:0;z-index:1020;}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch;}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch;}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important;}.visually-hidden:not(caption),.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption){position:absolute!important;}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:"";}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25;}.align-baseline{vertical-align:baseline!important;}.align-top{vertical-align:top!important;}.align-middle{vertical-align:middle!important;}.align-bottom{vertical-align:bottom!important;}.align-text-bottom{vertical-align:text-bottom!important;}.align-text-top{vertical-align:text-top!important;}.float-start{float:left!important;}.float-end{float:right!important;}.float-none{float:none!important;}.object-fit-contain{object-fit:contain!important;}.object-fit-cover{object-fit:cover!important;}.object-fit-fill{object-fit:fill!important;}.object-fit-scale{object-fit:scale-down!important;}.object-fit-none{object-fit:none!important;}.opacity-0{opacity:0!important;}.opacity-25{opacity:.25!important;}.opacity-50{opacity:.5!important;}.opacity-75{opacity:.75!important;}.opacity-100{opacity:1!important;}.overflow-auto{overflow:auto!important;}.overflow-hidden{overflow:hidden!important;}.overflow-visible{overflow:visible!important;}.overflow-scroll{overflow:scroll!important;}.overflow-x-auto{overflow-x:auto!important;}.overflow-x-hidden{overflow-x:hidden!important;}.overflow-x-visible{overflow-x:visible!important;}.overflow-x-scroll{overflow-x:scroll!important;}.overflow-y-auto{overflow-y:auto!important;}.overflow-y-hidden{overflow-y:hidden!important;}.overflow-y-visible{overflow-y:visible!important;}.overflow-y-scroll{overflow-y:scroll!important;}.d-inline{display:inline!important;}.d-inline-block{display:inline-block!important;}.d-block{display:block!important;}.d-grid{display:grid!important;}.d-inline-grid{display:inline-grid!important;}.d-table{display:table!important;}.d-table-row{display:table-row!important;}.d-table-cell{display:table-cell!important;}.d-flex{display:flex!important;}.d-inline-flex{display:inline-flex!important;}.d-none{display:none!important;}.shadow{box-shadow:var(--bs-box-shadow)!important;}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important;}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important;}.shadow-none{box-shadow:none!important;}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb),var(--bs-focus-ring-opacity));}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb),var(--bs-focus-ring-opacity));}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb),var(--bs-focus-ring-opacity));}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb),var(--bs-focus-ring-opacity));}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb),var(--bs-focus-ring-opacity));}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb),var(--bs-focus-ring-opacity));}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb),var(--bs-focus-ring-opacity));}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb),var(--bs-focus-ring-opacity));}.focus-ring-accent{--bs-focus-ring-color:rgba(var(--bs-accent-rgb),var(--bs-focus-ring-opacity));}.focus-ring-cinereous{--bs-focus-ring-color:rgba(var(--bs-cinereous-rgb),var(--bs-focus-ring-opacity));}.focus-ring-verdigris{--bs-focus-ring-color:rgba(var(--bs-verdigris-rgb),var(--bs-focus-ring-opacity));}.focus-ring-icterine{--bs-focus-ring-color:rgba(var(--bs-icterine-rgb),var(--bs-focus-ring-opacity));}.focus-ring-mute{--bs-focus-ring-color:rgba(var(--bs-mute-rgb),var(--bs-focus-ring-opacity));}.position-static{position:static!important;}.position-relative{position:relative!important;}.position-absolute{position:absolute!important;}.position-fixed{position:fixed!important;}.position-sticky{position:sticky!important;}.top-0{top:0!important;}.top-50{top:50%!important;}.top-100{top:100%!important;}.bottom-0{bottom:0!important;}.bottom-50{bottom:50%!important;}.bottom-100{bottom:100%!important;}.start-0{left:0!important;}.start-50{left:50%!important;}.start-100{left:100%!important;}.end-0{right:0!important;}.end-50{right:50%!important;}.end-100{right:100%!important;}.translate-middle{transform:translate(-50%,-50%)!important;}.translate-middle-x{transform:translateX(-50%)!important;}.translate-middle-y{transform:translateY(-50%)!important;}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important;}.border-0{border:0!important;}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important;}.border-top-0{border-top:0!important;}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important;}.border-end-0{border-right:0!important;}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important;}.border-bottom-0{border-bottom:0!important;}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important;}.border-start-0{border-left:0!important;}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important;}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important;}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important;}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important;}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important;}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important;}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important;}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important;}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important;}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important;}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important;}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important;}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important;}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important;}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important;}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important;}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important;}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important;}.border-accent{border-color:#9cec5b!important;}.border-cinereous{border-color:#837569!important;}.border-verdigris{border-color:#50c5b7!important;}.border-icterine{border-color:#f0f465!important;}.border-mute{border-color:#6c757d!important;}.border-1{border-width:1px!important;}.border-2{border-width:2px!important;}.border-3{border-width:3px!important;}.border-4{border-width:4px!important;}.border-5{border-width:5px!important;}.border-opacity-10{--bs-border-opacity:.1;}.border-opacity-25{--bs-border-opacity:.25;}.border-opacity-50{--bs-border-opacity:.5;}.border-opacity-75{--bs-border-opacity:.75;}.border-opacity-100{--bs-border-opacity:1;}.w-25{width:25%!important;}.w-50{width:50%!important;}.w-75{width:75%!important;}.w-100{width:100%!important;}.w-auto{width:auto!important;}.mw-100{max-width:100%!important;}.vw-100{width:100vw!important;}.min-vw-100{min-width:100vw!important;}.h-25{height:25%!important;}.h-50{height:50%!important;}.h-75{height:75%!important;}.h-100{height:100%!important;}.h-auto{height:auto!important;}.mh-100{max-height:100%!important;}.vh-100{height:100vh!important;}.min-vh-100{min-height:100vh!important;}.flex-fill{flex:1 1 auto!important;}.flex-row{flex-direction:row!important;}.flex-column{flex-direction:column!important;}.flex-row-reverse{flex-direction:row-reverse!important;}.flex-column-reverse{flex-direction:column-reverse!important;}.flex-grow-0{flex-grow:0!important;}.flex-grow-1{flex-grow:1!important;}.flex-shrink-0{flex-shrink:0!important;}.flex-shrink-1{flex-shrink:1!important;}.flex-wrap{flex-wrap:wrap!important;}.flex-nowrap{flex-wrap:nowrap!important;}.flex-wrap-reverse{flex-wrap:wrap-reverse!important;}.justify-content-start{justify-content:flex-start!important;}.justify-content-end{justify-content:flex-end!important;}.justify-content-center{justify-content:center!important;}.justify-content-between{justify-content:space-between!important;}.justify-content-around{justify-content:space-around!important;}.justify-content-evenly{justify-content:space-evenly!important;}.align-items-start{align-items:flex-start!important;}.align-items-end{align-items:flex-end!important;}.align-items-center{align-items:center!important;}.align-items-baseline{align-items:baseline!important;}.align-items-stretch{align-items:stretch!important;}.align-content-start{align-content:flex-start!important;}.align-content-end{align-content:flex-end!important;}.align-content-center{align-content:center!important;}.align-content-between{align-content:space-between!important;}.align-content-around{align-content:space-around!important;}.align-content-stretch{align-content:stretch!important;}.align-self-auto{align-self:auto!important;}.align-self-start{align-self:flex-start!important;}.align-self-end{align-self:flex-end!important;}.align-self-center{align-self:center!important;}.align-self-baseline{align-self:baseline!important;}.align-self-stretch{align-self:stretch!important;}.order-first{order:-1!important;}.order-0{order:0!important;}.order-1{order:1!important;}.order-2{order:2!important;}.order-3{order:3!important;}.order-4{order:4!important;}.order-5{order:5!important;}.order-last{order:6!important;}.m-0{margin:0!important;}.m-1{margin:.25rem!important;}.m-2{margin:.5rem!important;}.m-3{margin:1rem!important;}.m-4{margin:1.5rem!important;}.m-5{margin:3rem!important;}.m-auto{margin:auto!important;}.mx-0{margin-right:0!important;margin-left:0!important;}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-auto{margin-right:auto!important;margin-left:auto!important;}.my-0{margin-top:0!important;margin-bottom:0!important;}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-0{margin-top:0!important;}.mt-1{margin-top:.25rem!important;}.mt-2{margin-top:.5rem!important;}.mt-3{margin-top:1rem!important;}.mt-4{margin-top:1.5rem!important;}.mt-5{margin-top:3rem!important;}.mt-auto{margin-top:auto!important;}.me-0{margin-right:0!important;}.me-1{margin-right:.25rem!important;}.me-2{margin-right:.5rem!important;}.me-3{margin-right:1rem!important;}.me-4{margin-right:1.5rem!important;}.me-5{margin-right:3rem!important;}.me-auto{margin-right:auto!important;}.mb-0{margin-bottom:0!important;}.mb-1{margin-bottom:.25rem!important;}.mb-2{margin-bottom:.5rem!important;}.mb-3{margin-bottom:1rem!important;}.mb-4{margin-bottom:1.5rem!important;}.mb-5{margin-bottom:3rem!important;}.mb-auto{margin-bottom:auto!important;}.ms-0{margin-left:0!important;}.ms-1{margin-left:.25rem!important;}.ms-2{margin-left:.5rem!important;}.ms-3{margin-left:1rem!important;}.ms-4{margin-left:1.5rem!important;}.ms-5{margin-left:3rem!important;}.ms-auto{margin-left:auto!important;}.p-0{padding:0!important;}.p-1{padding:.25rem!important;}.p-2{padding:.5rem!important;}.p-3{padding:1rem!important;}.p-4{padding:1.5rem!important;}.p-5{padding:3rem!important;}.px-0{padding-right:0!important;padding-left:0!important;}.px-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-3{padding-right:1rem!important;padding-left:1rem!important;}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-5{padding-right:3rem!important;padding-left:3rem!important;}.py-0{padding-top:0!important;padding-bottom:0!important;}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-0{padding-top:0!important;}.pt-1{padding-top:.25rem!important;}.pt-2{padding-top:.5rem!important;}.pt-3{padding-top:1rem!important;}.pt-4{padding-top:1.5rem!important;}.pt-5{padding-top:3rem!important;}.pe-0{padding-right:0!important;}.pe-1{padding-right:.25rem!important;}.pe-2{padding-right:.5rem!important;}.pe-3{padding-right:1rem!important;}.pe-4{padding-right:1.5rem!important;}.pe-5{padding-right:3rem!important;}.pb-0{padding-bottom:0!important;}.pb-1{padding-bottom:.25rem!important;}.pb-2{padding-bottom:.5rem!important;}.pb-3{padding-bottom:1rem!important;}.pb-4{padding-bottom:1.5rem!important;}.pb-5{padding-bottom:3rem!important;}.ps-0{padding-left:0!important;}.ps-1{padding-left:.25rem!important;}.ps-2{padding-left:.5rem!important;}.ps-3{padding-left:1rem!important;}.ps-4{padding-left:1.5rem!important;}.ps-5{padding-left:3rem!important;}.gap-0{gap:0!important;}.gap-1{gap:.25rem!important;}.gap-2{gap:.5rem!important;}.gap-3{gap:1rem!important;}.gap-4{gap:1.5rem!important;}.gap-5{gap:3rem!important;}.row-gap-0{row-gap:0!important;}.row-gap-1{row-gap:.25rem!important;}.row-gap-2{row-gap:.5rem!important;}.row-gap-3{row-gap:1rem!important;}.row-gap-4{row-gap:1.5rem!important;}.row-gap-5{row-gap:3rem!important;}.column-gap-0{column-gap:0!important;}.column-gap-1{column-gap:.25rem!important;}.column-gap-2{column-gap:.5rem!important;}.column-gap-3{column-gap:1rem!important;}.column-gap-4{column-gap:1.5rem!important;}.column-gap-5{column-gap:3rem!important;}.font-monospace{font-family:var(--bs-font-monospace)!important;}.fs-1{font-size:calc(1.375rem + 1.5vw)!important;}.fs-2{font-size:calc(1.325rem + .9vw)!important;}.fs-3{font-size:calc(1.3rem + .6vw)!important;}.fs-4{font-size:calc(1.275rem + .3vw)!important;}.fs-5{font-size:1.25rem!important;}.fs-6{font-size:1rem!important;}.fst-italic{font-style:italic!important;}.fst-normal{font-style:normal!important;}.fw-lighter{font-weight:lighter!important;}.fw-light{font-weight:300!important;}.fw-normal{font-weight:400!important;}.fw-medium{font-weight:500!important;}.fw-semibold{font-weight:600!important;}.fw-bold{font-weight:700!important;}.fw-bolder{font-weight:bolder!important;}.lh-1{line-height:1!important;}.lh-sm{line-height:1.25!important;}.lh-base{line-height:1.5!important;}.lh-lg{line-height:2!important;}.text-start{text-align:left!important;}.text-end{text-align:right!important;}.text-center{text-align:center!important;}.text-decoration-none{text-decoration:none!important;}.text-decoration-underline{text-decoration:underline!important;}.text-decoration-line-through{text-decoration:line-through!important;}.text-lowercase{text-transform:lowercase!important;}.text-uppercase{text-transform:uppercase!important;}.text-capitalize{text-transform:capitalize!important;}.text-wrap{white-space:normal!important;}.text-nowrap{white-space:nowrap!important;}.text-break{word-wrap:break-word!important;word-break:break-word!important;}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important;}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important;}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important;}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important;}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important;}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important;}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important;}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important;}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important;}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important;}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important;}.text-accent{--bs-text-opacity:1;color:rgba(var(--bs-accent-rgb),var(--bs-text-opacity))!important;}.text-cinereous{--bs-text-opacity:1;color:rgba(var(--bs-cinereous-rgb),var(--bs-text-opacity))!important;}.text-verdigris{--bs-text-opacity:1;color:rgba(var(--bs-verdigris-rgb),var(--bs-text-opacity))!important;}.text-icterine{--bs-text-opacity:1;color:rgba(var(--bs-icterine-rgb),var(--bs-text-opacity))!important;}.text-mute{--bs-text-opacity:1;color:rgba(var(--bs-mute-rgb),var(--bs-text-opacity))!important;}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important;}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important;}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important;}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important;}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important;}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important;}.text-reset{--bs-text-opacity:1;color:inherit!important;}.text-opacity-25{--bs-text-opacity:.25;}.text-opacity-50{--bs-text-opacity:.5;}.text-opacity-75{--bs-text-opacity:.75;}.text-opacity-100{--bs-text-opacity:1;}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important;}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important;}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important;}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important;}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important;}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important;}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important;}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important;}.text-accent{color:#9cec5b!important;}.text-cinereous{color:#837569!important;}.text-verdigris{color:#50c5b7!important;}.text-icterine{color:#f0f465!important;}.text-mute{color:#6c757d!important;}.link-opacity-10{--bs-link-opacity:.1;}.link-opacity-10-hover:hover{--bs-link-opacity:.1;}.link-opacity-25{--bs-link-opacity:.25;}.link-opacity-25-hover:hover{--bs-link-opacity:.25;}.link-opacity-50{--bs-link-opacity:.5;}.link-opacity-50-hover:hover{--bs-link-opacity:.5;}.link-opacity-75{--bs-link-opacity:.75;}.link-opacity-75-hover:hover{--bs-link-opacity:.75;}.link-opacity-100{--bs-link-opacity:1;}.link-opacity-100-hover:hover{--bs-link-opacity:1;}.link-offset-1{text-underline-offset:.125em!important;}.link-offset-1-hover:hover{text-underline-offset:.125em!important;}.link-offset-2{text-underline-offset:.25em!important;}.link-offset-2-hover:hover{text-underline-offset:.25em!important;}.link-offset-3{text-underline-offset:.375em!important;}.link-offset-3-hover:hover{text-underline-offset:.375em!important;}.link-underline-primary{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;}.link-underline-secondary{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;}.link-underline-success{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;}.link-underline-info{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;}.link-underline-warning{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;}.link-underline-danger{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;}.link-underline-light{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;}.link-underline-dark{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;}.link-underline{--bs-link-underline-opacity:1;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;}.link-underline-opacity-0{--bs-link-underline-opacity:0;}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0;}.link-underline-opacity-10{--bs-link-underline-opacity:.1;}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:.1;}.link-underline-opacity-25{--bs-link-underline-opacity:.25;}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:.25;}.link-underline-opacity-50{--bs-link-underline-opacity:.5;}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:.5;}.link-underline-opacity-75{--bs-link-underline-opacity:.75;}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:.75;}.link-underline-opacity-100{--bs-link-underline-opacity:1;}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1;}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important;}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important;}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important;}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important;}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important;}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important;}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important;}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important;}.bg-accent{--bs-bg-opacity:1;background-color:rgba(var(--bs-accent-rgb),var(--bs-bg-opacity))!important;}.bg-cinereous{--bs-bg-opacity:1;background-color:rgba(var(--bs-cinereous-rgb),var(--bs-bg-opacity))!important;}.bg-verdigris{--bs-bg-opacity:1;background-color:rgba(var(--bs-verdigris-rgb),var(--bs-bg-opacity))!important;}.bg-icterine{--bs-bg-opacity:1;background-color:rgba(var(--bs-icterine-rgb),var(--bs-bg-opacity))!important;}.bg-mute{--bs-bg-opacity:1;background-color:rgba(var(--bs-mute-rgb),var(--bs-bg-opacity))!important;}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important;}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important;}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important;}.bg-opacity-10{--bs-bg-opacity:.1;}.bg-opacity-25{--bs-bg-opacity:.25;}.bg-opacity-50{--bs-bg-opacity:.5;}.bg-opacity-75{--bs-bg-opacity:.75;}.bg-opacity-100{--bs-bg-opacity:1;}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important;}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important;}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important;}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important;}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important;}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important;}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important;}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important;}.bg-accent{background-color:#9cec5b!important;}.bg-cinereous{background-color:#837569!important;}.bg-verdigris{background-color:#50c5b7!important;}.bg-icterine{background-color:#f0f465!important;}.bg-mute{background-color:#6c757d!important;}.bg-gradient{background-image:var(--bs-gradient)!important;}.user-select-all{user-select:all!important;}.user-select-auto{user-select:auto!important;}.user-select-none{user-select:none!important;}.pe-none{pointer-events:none!important;}.pe-auto{pointer-events:auto!important;}.rounded{border-radius:var(--bs-border-radius)!important;}.rounded-0{border-radius:0!important;}.rounded-1{border-radius:var(--bs-border-radius-sm)!important;}.rounded-2{border-radius:var(--bs-border-radius)!important;}.rounded-3{border-radius:var(--bs-border-radius-lg)!important;}.rounded-4{border-radius:var(--bs-border-radius-xl)!important;}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important;}.rounded-circle{border-radius:50%!important;}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important;}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important;}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important;}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important;}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important;}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important;}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important;}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important;}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important;}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important;}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important;}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important;}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important;}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important;}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important;}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important;}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important;}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important;}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important;}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important;}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important;}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important;}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important;}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important;}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important;}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important;}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important;}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important;}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important;}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important;}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important;}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important;}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important;}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important;}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important;}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important;}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important;}.visible{visibility:visible!important;}.invisible{visibility:hidden!important;}.z-n1{z-index:-1!important;}.z-0{z-index:0!important;}.z-1{z-index:1!important;}.z-2{z-index:2!important;}.z-3{z-index:3!important;}@media(min-width:576px){.float-sm-start{float:left!important;}.float-sm-end{float:right!important;}.float-sm-none{float:none!important;}.object-fit-sm-contain{object-fit:contain!important;}.object-fit-sm-cover{object-fit:cover!important;}.object-fit-sm-fill{object-fit:fill!important;}.object-fit-sm-scale{object-fit:scale-down!important;}.object-fit-sm-none{object-fit:none!important;}.d-sm-inline{display:inline!important;}.d-sm-inline-block{display:inline-block!important;}.d-sm-block{display:block!important;}.d-sm-grid{display:grid!important;}.d-sm-inline-grid{display:inline-grid!important;}.d-sm-table{display:table!important;}.d-sm-table-row{display:table-row!important;}.d-sm-table-cell{display:table-cell!important;}.d-sm-flex{display:flex!important;}.d-sm-inline-flex{display:inline-flex!important;}.d-sm-none{display:none!important;}.flex-sm-fill{flex:1 1 auto!important;}.flex-sm-row{flex-direction:row!important;}.flex-sm-column{flex-direction:column!important;}.flex-sm-row-reverse{flex-direction:row-reverse!important;}.flex-sm-column-reverse{flex-direction:column-reverse!important;}.flex-sm-grow-0{flex-grow:0!important;}.flex-sm-grow-1{flex-grow:1!important;}.flex-sm-shrink-0{flex-shrink:0!important;}.flex-sm-shrink-1{flex-shrink:1!important;}.flex-sm-wrap{flex-wrap:wrap!important;}.flex-sm-nowrap{flex-wrap:nowrap!important;}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important;}.justify-content-sm-start{justify-content:flex-start!important;}.justify-content-sm-end{justify-content:flex-end!important;}.justify-content-sm-center{justify-content:center!important;}.justify-content-sm-between{justify-content:space-between!important;}.justify-content-sm-around{justify-content:space-around!important;}.justify-content-sm-evenly{justify-content:space-evenly!important;}.align-items-sm-start{align-items:flex-start!important;}.align-items-sm-end{align-items:flex-end!important;}.align-items-sm-center{align-items:center!important;}.align-items-sm-baseline{align-items:baseline!important;}.align-items-sm-stretch{align-items:stretch!important;}.align-content-sm-start{align-content:flex-start!important;}.align-content-sm-end{align-content:flex-end!important;}.align-content-sm-center{align-content:center!important;}.align-content-sm-between{align-content:space-between!important;}.align-content-sm-around{align-content:space-around!important;}.align-content-sm-stretch{align-content:stretch!important;}.align-self-sm-auto{align-self:auto!important;}.align-self-sm-start{align-self:flex-start!important;}.align-self-sm-end{align-self:flex-end!important;}.align-self-sm-center{align-self:center!important;}.align-self-sm-baseline{align-self:baseline!important;}.align-self-sm-stretch{align-self:stretch!important;}.order-sm-first{order:-1!important;}.order-sm-0{order:0!important;}.order-sm-1{order:1!important;}.order-sm-2{order:2!important;}.order-sm-3{order:3!important;}.order-sm-4{order:4!important;}.order-sm-5{order:5!important;}.order-sm-last{order:6!important;}.m-sm-0{margin:0!important;}.m-sm-1{margin:.25rem!important;}.m-sm-2{margin:.5rem!important;}.m-sm-3{margin:1rem!important;}.m-sm-4{margin:1.5rem!important;}.m-sm-5{margin:3rem!important;}.m-sm-auto{margin:auto!important;}.mx-sm-0{margin-right:0!important;margin-left:0!important;}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important;}.my-sm-0{margin-top:0!important;margin-bottom:0!important;}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-sm-0{margin-top:0!important;}.mt-sm-1{margin-top:.25rem!important;}.mt-sm-2{margin-top:.5rem!important;}.mt-sm-3{margin-top:1rem!important;}.mt-sm-4{margin-top:1.5rem!important;}.mt-sm-5{margin-top:3rem!important;}.mt-sm-auto{margin-top:auto!important;}.me-sm-0{margin-right:0!important;}.me-sm-1{margin-right:.25rem!important;}.me-sm-2{margin-right:.5rem!important;}.me-sm-3{margin-right:1rem!important;}.me-sm-4{margin-right:1.5rem!important;}.me-sm-5{margin-right:3rem!important;}.me-sm-auto{margin-right:auto!important;}.mb-sm-0{margin-bottom:0!important;}.mb-sm-1{margin-bottom:.25rem!important;}.mb-sm-2{margin-bottom:.5rem!important;}.mb-sm-3{margin-bottom:1rem!important;}.mb-sm-4{margin-bottom:1.5rem!important;}.mb-sm-5{margin-bottom:3rem!important;}.mb-sm-auto{margin-bottom:auto!important;}.ms-sm-0{margin-left:0!important;}.ms-sm-1{margin-left:.25rem!important;}.ms-sm-2{margin-left:.5rem!important;}.ms-sm-3{margin-left:1rem!important;}.ms-sm-4{margin-left:1.5rem!important;}.ms-sm-5{margin-left:3rem!important;}.ms-sm-auto{margin-left:auto!important;}.p-sm-0{padding:0!important;}.p-sm-1{padding:.25rem!important;}.p-sm-2{padding:.5rem!important;}.p-sm-3{padding:1rem!important;}.p-sm-4{padding:1.5rem!important;}.p-sm-5{padding:3rem!important;}.px-sm-0{padding-right:0!important;padding-left:0!important;}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important;}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important;}.py-sm-0{padding-top:0!important;padding-bottom:0!important;}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-sm-0{padding-top:0!important;}.pt-sm-1{padding-top:.25rem!important;}.pt-sm-2{padding-top:.5rem!important;}.pt-sm-3{padding-top:1rem!important;}.pt-sm-4{padding-top:1.5rem!important;}.pt-sm-5{padding-top:3rem!important;}.pe-sm-0{padding-right:0!important;}.pe-sm-1{padding-right:.25rem!important;}.pe-sm-2{padding-right:.5rem!important;}.pe-sm-3{padding-right:1rem!important;}.pe-sm-4{padding-right:1.5rem!important;}.pe-sm-5{padding-right:3rem!important;}.pb-sm-0{padding-bottom:0!important;}.pb-sm-1{padding-bottom:.25rem!important;}.pb-sm-2{padding-bottom:.5rem!important;}.pb-sm-3{padding-bottom:1rem!important;}.pb-sm-4{padding-bottom:1.5rem!important;}.pb-sm-5{padding-bottom:3rem!important;}.ps-sm-0{padding-left:0!important;}.ps-sm-1{padding-left:.25rem!important;}.ps-sm-2{padding-left:.5rem!important;}.ps-sm-3{padding-left:1rem!important;}.ps-sm-4{padding-left:1.5rem!important;}.ps-sm-5{padding-left:3rem!important;}.gap-sm-0{gap:0!important;}.gap-sm-1{gap:.25rem!important;}.gap-sm-2{gap:.5rem!important;}.gap-sm-3{gap:1rem!important;}.gap-sm-4{gap:1.5rem!important;}.gap-sm-5{gap:3rem!important;}.row-gap-sm-0{row-gap:0!important;}.row-gap-sm-1{row-gap:.25rem!important;}.row-gap-sm-2{row-gap:.5rem!important;}.row-gap-sm-3{row-gap:1rem!important;}.row-gap-sm-4{row-gap:1.5rem!important;}.row-gap-sm-5{row-gap:3rem!important;}.column-gap-sm-0{column-gap:0!important;}.column-gap-sm-1{column-gap:.25rem!important;}.column-gap-sm-2{column-gap:.5rem!important;}.column-gap-sm-3{column-gap:1rem!important;}.column-gap-sm-4{column-gap:1.5rem!important;}.column-gap-sm-5{column-gap:3rem!important;}.text-sm-start{text-align:left!important;}.text-sm-end{text-align:right!important;}.text-sm-center{text-align:center!important;}}@media(min-width:768px){.float-md-start{float:left!important;}.float-md-end{float:right!important;}.float-md-none{float:none!important;}.object-fit-md-contain{object-fit:contain!important;}.object-fit-md-cover{object-fit:cover!important;}.object-fit-md-fill{object-fit:fill!important;}.object-fit-md-scale{object-fit:scale-down!important;}.object-fit-md-none{object-fit:none!important;}.d-md-inline{display:inline!important;}.d-md-inline-block{display:inline-block!important;}.d-md-block{display:block!important;}.d-md-grid{display:grid!important;}.d-md-inline-grid{display:inline-grid!important;}.d-md-table{display:table!important;}.d-md-table-row{display:table-row!important;}.d-md-table-cell{display:table-cell!important;}.d-md-flex{display:flex!important;}.d-md-inline-flex{display:inline-flex!important;}.d-md-none{display:none!important;}.flex-md-fill{flex:1 1 auto!important;}.flex-md-row{flex-direction:row!important;}.flex-md-column{flex-direction:column!important;}.flex-md-row-reverse{flex-direction:row-reverse!important;}.flex-md-column-reverse{flex-direction:column-reverse!important;}.flex-md-grow-0{flex-grow:0!important;}.flex-md-grow-1{flex-grow:1!important;}.flex-md-shrink-0{flex-shrink:0!important;}.flex-md-shrink-1{flex-shrink:1!important;}.flex-md-wrap{flex-wrap:wrap!important;}.flex-md-nowrap{flex-wrap:nowrap!important;}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important;}.justify-content-md-start{justify-content:flex-start!important;}.justify-content-md-end{justify-content:flex-end!important;}.justify-content-md-center{justify-content:center!important;}.justify-content-md-between{justify-content:space-between!important;}.justify-content-md-around{justify-content:space-around!important;}.justify-content-md-evenly{justify-content:space-evenly!important;}.align-items-md-start{align-items:flex-start!important;}.align-items-md-end{align-items:flex-end!important;}.align-items-md-center{align-items:center!important;}.align-items-md-baseline{align-items:baseline!important;}.align-items-md-stretch{align-items:stretch!important;}.align-content-md-start{align-content:flex-start!important;}.align-content-md-end{align-content:flex-end!important;}.align-content-md-center{align-content:center!important;}.align-content-md-between{align-content:space-between!important;}.align-content-md-around{align-content:space-around!important;}.align-content-md-stretch{align-content:stretch!important;}.align-self-md-auto{align-self:auto!important;}.align-self-md-start{align-self:flex-start!important;}.align-self-md-end{align-self:flex-end!important;}.align-self-md-center{align-self:center!important;}.align-self-md-baseline{align-self:baseline!important;}.align-self-md-stretch{align-self:stretch!important;}.order-md-first{order:-1!important;}.order-md-0{order:0!important;}.order-md-1{order:1!important;}.order-md-2{order:2!important;}.order-md-3{order:3!important;}.order-md-4{order:4!important;}.order-md-5{order:5!important;}.order-md-last{order:6!important;}.m-md-0{margin:0!important;}.m-md-1{margin:.25rem!important;}.m-md-2{margin:.5rem!important;}.m-md-3{margin:1rem!important;}.m-md-4{margin:1.5rem!important;}.m-md-5{margin:3rem!important;}.m-md-auto{margin:auto!important;}.mx-md-0{margin-right:0!important;margin-left:0!important;}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-md-auto{margin-right:auto!important;margin-left:auto!important;}.my-md-0{margin-top:0!important;margin-bottom:0!important;}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-md-0{margin-top:0!important;}.mt-md-1{margin-top:.25rem!important;}.mt-md-2{margin-top:.5rem!important;}.mt-md-3{margin-top:1rem!important;}.mt-md-4{margin-top:1.5rem!important;}.mt-md-5{margin-top:3rem!important;}.mt-md-auto{margin-top:auto!important;}.me-md-0{margin-right:0!important;}.me-md-1{margin-right:.25rem!important;}.me-md-2{margin-right:.5rem!important;}.me-md-3{margin-right:1rem!important;}.me-md-4{margin-right:1.5rem!important;}.me-md-5{margin-right:3rem!important;}.me-md-auto{margin-right:auto!important;}.mb-md-0{margin-bottom:0!important;}.mb-md-1{margin-bottom:.25rem!important;}.mb-md-2{margin-bottom:.5rem!important;}.mb-md-3{margin-bottom:1rem!important;}.mb-md-4{margin-bottom:1.5rem!important;}.mb-md-5{margin-bottom:3rem!important;}.mb-md-auto{margin-bottom:auto!important;}.ms-md-0{margin-left:0!important;}.ms-md-1{margin-left:.25rem!important;}.ms-md-2{margin-left:.5rem!important;}.ms-md-3{margin-left:1rem!important;}.ms-md-4{margin-left:1.5rem!important;}.ms-md-5{margin-left:3rem!important;}.ms-md-auto{margin-left:auto!important;}.p-md-0{padding:0!important;}.p-md-1{padding:.25rem!important;}.p-md-2{padding:.5rem!important;}.p-md-3{padding:1rem!important;}.p-md-4{padding:1.5rem!important;}.p-md-5{padding:3rem!important;}.px-md-0{padding-right:0!important;padding-left:0!important;}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-md-3{padding-right:1rem!important;padding-left:1rem!important;}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-md-5{padding-right:3rem!important;padding-left:3rem!important;}.py-md-0{padding-top:0!important;padding-bottom:0!important;}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-md-0{padding-top:0!important;}.pt-md-1{padding-top:.25rem!important;}.pt-md-2{padding-top:.5rem!important;}.pt-md-3{padding-top:1rem!important;}.pt-md-4{padding-top:1.5rem!important;}.pt-md-5{padding-top:3rem!important;}.pe-md-0{padding-right:0!important;}.pe-md-1{padding-right:.25rem!important;}.pe-md-2{padding-right:.5rem!important;}.pe-md-3{padding-right:1rem!important;}.pe-md-4{padding-right:1.5rem!important;}.pe-md-5{padding-right:3rem!important;}.pb-md-0{padding-bottom:0!important;}.pb-md-1{padding-bottom:.25rem!important;}.pb-md-2{padding-bottom:.5rem!important;}.pb-md-3{padding-bottom:1rem!important;}.pb-md-4{padding-bottom:1.5rem!important;}.pb-md-5{padding-bottom:3rem!important;}.ps-md-0{padding-left:0!important;}.ps-md-1{padding-left:.25rem!important;}.ps-md-2{padding-left:.5rem!important;}.ps-md-3{padding-left:1rem!important;}.ps-md-4{padding-left:1.5rem!important;}.ps-md-5{padding-left:3rem!important;}.gap-md-0{gap:0!important;}.gap-md-1{gap:.25rem!important;}.gap-md-2{gap:.5rem!important;}.gap-md-3{gap:1rem!important;}.gap-md-4{gap:1.5rem!important;}.gap-md-5{gap:3rem!important;}.row-gap-md-0{row-gap:0!important;}.row-gap-md-1{row-gap:.25rem!important;}.row-gap-md-2{row-gap:.5rem!important;}.row-gap-md-3{row-gap:1rem!important;}.row-gap-md-4{row-gap:1.5rem!important;}.row-gap-md-5{row-gap:3rem!important;}.column-gap-md-0{column-gap:0!important;}.column-gap-md-1{column-gap:.25rem!important;}.column-gap-md-2{column-gap:.5rem!important;}.column-gap-md-3{column-gap:1rem!important;}.column-gap-md-4{column-gap:1.5rem!important;}.column-gap-md-5{column-gap:3rem!important;}.text-md-start{text-align:left!important;}.text-md-end{text-align:right!important;}.text-md-center{text-align:center!important;}}@media(min-width:992px){.float-lg-start{float:left!important;}.float-lg-end{float:right!important;}.float-lg-none{float:none!important;}.object-fit-lg-contain{object-fit:contain!important;}.object-fit-lg-cover{object-fit:cover!important;}.object-fit-lg-fill{object-fit:fill!important;}.object-fit-lg-scale{object-fit:scale-down!important;}.object-fit-lg-none{object-fit:none!important;}.d-lg-inline{display:inline!important;}.d-lg-inline-block{display:inline-block!important;}.d-lg-block{display:block!important;}.d-lg-grid{display:grid!important;}.d-lg-inline-grid{display:inline-grid!important;}.d-lg-table{display:table!important;}.d-lg-table-row{display:table-row!important;}.d-lg-table-cell{display:table-cell!important;}.d-lg-flex{display:flex!important;}.d-lg-inline-flex{display:inline-flex!important;}.d-lg-none{display:none!important;}.flex-lg-fill{flex:1 1 auto!important;}.flex-lg-row{flex-direction:row!important;}.flex-lg-column{flex-direction:column!important;}.flex-lg-row-reverse{flex-direction:row-reverse!important;}.flex-lg-column-reverse{flex-direction:column-reverse!important;}.flex-lg-grow-0{flex-grow:0!important;}.flex-lg-grow-1{flex-grow:1!important;}.flex-lg-shrink-0{flex-shrink:0!important;}.flex-lg-shrink-1{flex-shrink:1!important;}.flex-lg-wrap{flex-wrap:wrap!important;}.flex-lg-nowrap{flex-wrap:nowrap!important;}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important;}.justify-content-lg-start{justify-content:flex-start!important;}.justify-content-lg-end{justify-content:flex-end!important;}.justify-content-lg-center{justify-content:center!important;}.justify-content-lg-between{justify-content:space-between!important;}.justify-content-lg-around{justify-content:space-around!important;}.justify-content-lg-evenly{justify-content:space-evenly!important;}.align-items-lg-start{align-items:flex-start!important;}.align-items-lg-end{align-items:flex-end!important;}.align-items-lg-center{align-items:center!important;}.align-items-lg-baseline{align-items:baseline!important;}.align-items-lg-stretch{align-items:stretch!important;}.align-content-lg-start{align-content:flex-start!important;}.align-content-lg-end{align-content:flex-end!important;}.align-content-lg-center{align-content:center!important;}.align-content-lg-between{align-content:space-between!important;}.align-content-lg-around{align-content:space-around!important;}.align-content-lg-stretch{align-content:stretch!important;}.align-self-lg-auto{align-self:auto!important;}.align-self-lg-start{align-self:flex-start!important;}.align-self-lg-end{align-self:flex-end!important;}.align-self-lg-center{align-self:center!important;}.align-self-lg-baseline{align-self:baseline!important;}.align-self-lg-stretch{align-self:stretch!important;}.order-lg-first{order:-1!important;}.order-lg-0{order:0!important;}.order-lg-1{order:1!important;}.order-lg-2{order:2!important;}.order-lg-3{order:3!important;}.order-lg-4{order:4!important;}.order-lg-5{order:5!important;}.order-lg-last{order:6!important;}.m-lg-0{margin:0!important;}.m-lg-1{margin:.25rem!important;}.m-lg-2{margin:.5rem!important;}.m-lg-3{margin:1rem!important;}.m-lg-4{margin:1.5rem!important;}.m-lg-5{margin:3rem!important;}.m-lg-auto{margin:auto!important;}.mx-lg-0{margin-right:0!important;margin-left:0!important;}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important;}.my-lg-0{margin-top:0!important;margin-bottom:0!important;}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-lg-0{margin-top:0!important;}.mt-lg-1{margin-top:.25rem!important;}.mt-lg-2{margin-top:.5rem!important;}.mt-lg-3{margin-top:1rem!important;}.mt-lg-4{margin-top:1.5rem!important;}.mt-lg-5{margin-top:3rem!important;}.mt-lg-auto{margin-top:auto!important;}.me-lg-0{margin-right:0!important;}.me-lg-1{margin-right:.25rem!important;}.me-lg-2{margin-right:.5rem!important;}.me-lg-3{margin-right:1rem!important;}.me-lg-4{margin-right:1.5rem!important;}.me-lg-5{margin-right:3rem!important;}.me-lg-auto{margin-right:auto!important;}.mb-lg-0{margin-bottom:0!important;}.mb-lg-1{margin-bottom:.25rem!important;}.mb-lg-2{margin-bottom:.5rem!important;}.mb-lg-3{margin-bottom:1rem!important;}.mb-lg-4{margin-bottom:1.5rem!important;}.mb-lg-5{margin-bottom:3rem!important;}.mb-lg-auto{margin-bottom:auto!important;}.ms-lg-0{margin-left:0!important;}.ms-lg-1{margin-left:.25rem!important;}.ms-lg-2{margin-left:.5rem!important;}.ms-lg-3{margin-left:1rem!important;}.ms-lg-4{margin-left:1.5rem!important;}.ms-lg-5{margin-left:3rem!important;}.ms-lg-auto{margin-left:auto!important;}.p-lg-0{padding:0!important;}.p-lg-1{padding:.25rem!important;}.p-lg-2{padding:.5rem!important;}.p-lg-3{padding:1rem!important;}.p-lg-4{padding:1.5rem!important;}.p-lg-5{padding:3rem!important;}.px-lg-0{padding-right:0!important;padding-left:0!important;}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important;}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important;}.py-lg-0{padding-top:0!important;padding-bottom:0!important;}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-lg-0{padding-top:0!important;}.pt-lg-1{padding-top:.25rem!important;}.pt-lg-2{padding-top:.5rem!important;}.pt-lg-3{padding-top:1rem!important;}.pt-lg-4{padding-top:1.5rem!important;}.pt-lg-5{padding-top:3rem!important;}.pe-lg-0{padding-right:0!important;}.pe-lg-1{padding-right:.25rem!important;}.pe-lg-2{padding-right:.5rem!important;}.pe-lg-3{padding-right:1rem!important;}.pe-lg-4{padding-right:1.5rem!important;}.pe-lg-5{padding-right:3rem!important;}.pb-lg-0{padding-bottom:0!important;}.pb-lg-1{padding-bottom:.25rem!important;}.pb-lg-2{padding-bottom:.5rem!important;}.pb-lg-3{padding-bottom:1rem!important;}.pb-lg-4{padding-bottom:1.5rem!important;}.pb-lg-5{padding-bottom:3rem!important;}.ps-lg-0{padding-left:0!important;}.ps-lg-1{padding-left:.25rem!important;}.ps-lg-2{padding-left:.5rem!important;}.ps-lg-3{padding-left:1rem!important;}.ps-lg-4{padding-left:1.5rem!important;}.ps-lg-5{padding-left:3rem!important;}.gap-lg-0{gap:0!important;}.gap-lg-1{gap:.25rem!important;}.gap-lg-2{gap:.5rem!important;}.gap-lg-3{gap:1rem!important;}.gap-lg-4{gap:1.5rem!important;}.gap-lg-5{gap:3rem!important;}.row-gap-lg-0{row-gap:0!important;}.row-gap-lg-1{row-gap:.25rem!important;}.row-gap-lg-2{row-gap:.5rem!important;}.row-gap-lg-3{row-gap:1rem!important;}.row-gap-lg-4{row-gap:1.5rem!important;}.row-gap-lg-5{row-gap:3rem!important;}.column-gap-lg-0{column-gap:0!important;}.column-gap-lg-1{column-gap:.25rem!important;}.column-gap-lg-2{column-gap:.5rem!important;}.column-gap-lg-3{column-gap:1rem!important;}.column-gap-lg-4{column-gap:1.5rem!important;}.column-gap-lg-5{column-gap:3rem!important;}.text-lg-start{text-align:left!important;}.text-lg-end{text-align:right!important;}.text-lg-center{text-align:center!important;}}@media(min-width:1200px){.float-xl-start{float:left!important;}.float-xl-end{float:right!important;}.float-xl-none{float:none!important;}.object-fit-xl-contain{object-fit:contain!important;}.object-fit-xl-cover{object-fit:cover!important;}.object-fit-xl-fill{object-fit:fill!important;}.object-fit-xl-scale{object-fit:scale-down!important;}.object-fit-xl-none{object-fit:none!important;}.d-xl-inline{display:inline!important;}.d-xl-inline-block{display:inline-block!important;}.d-xl-block{display:block!important;}.d-xl-grid{display:grid!important;}.d-xl-inline-grid{display:inline-grid!important;}.d-xl-table{display:table!important;}.d-xl-table-row{display:table-row!important;}.d-xl-table-cell{display:table-cell!important;}.d-xl-flex{display:flex!important;}.d-xl-inline-flex{display:inline-flex!important;}.d-xl-none{display:none!important;}.flex-xl-fill{flex:1 1 auto!important;}.flex-xl-row{flex-direction:row!important;}.flex-xl-column{flex-direction:column!important;}.flex-xl-row-reverse{flex-direction:row-reverse!important;}.flex-xl-column-reverse{flex-direction:column-reverse!important;}.flex-xl-grow-0{flex-grow:0!important;}.flex-xl-grow-1{flex-grow:1!important;}.flex-xl-shrink-0{flex-shrink:0!important;}.flex-xl-shrink-1{flex-shrink:1!important;}.flex-xl-wrap{flex-wrap:wrap!important;}.flex-xl-nowrap{flex-wrap:nowrap!important;}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important;}.justify-content-xl-start{justify-content:flex-start!important;}.justify-content-xl-end{justify-content:flex-end!important;}.justify-content-xl-center{justify-content:center!important;}.justify-content-xl-between{justify-content:space-between!important;}.justify-content-xl-around{justify-content:space-around!important;}.justify-content-xl-evenly{justify-content:space-evenly!important;}.align-items-xl-start{align-items:flex-start!important;}.align-items-xl-end{align-items:flex-end!important;}.align-items-xl-center{align-items:center!important;}.align-items-xl-baseline{align-items:baseline!important;}.align-items-xl-stretch{align-items:stretch!important;}.align-content-xl-start{align-content:flex-start!important;}.align-content-xl-end{align-content:flex-end!important;}.align-content-xl-center{align-content:center!important;}.align-content-xl-between{align-content:space-between!important;}.align-content-xl-around{align-content:space-around!important;}.align-content-xl-stretch{align-content:stretch!important;}.align-self-xl-auto{align-self:auto!important;}.align-self-xl-start{align-self:flex-start!important;}.align-self-xl-end{align-self:flex-end!important;}.align-self-xl-center{align-self:center!important;}.align-self-xl-baseline{align-self:baseline!important;}.align-self-xl-stretch{align-self:stretch!important;}.order-xl-first{order:-1!important;}.order-xl-0{order:0!important;}.order-xl-1{order:1!important;}.order-xl-2{order:2!important;}.order-xl-3{order:3!important;}.order-xl-4{order:4!important;}.order-xl-5{order:5!important;}.order-xl-last{order:6!important;}.m-xl-0{margin:0!important;}.m-xl-1{margin:.25rem!important;}.m-xl-2{margin:.5rem!important;}.m-xl-3{margin:1rem!important;}.m-xl-4{margin:1.5rem!important;}.m-xl-5{margin:3rem!important;}.m-xl-auto{margin:auto!important;}.mx-xl-0{margin-right:0!important;margin-left:0!important;}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important;}.my-xl-0{margin-top:0!important;margin-bottom:0!important;}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-xl-0{margin-top:0!important;}.mt-xl-1{margin-top:.25rem!important;}.mt-xl-2{margin-top:.5rem!important;}.mt-xl-3{margin-top:1rem!important;}.mt-xl-4{margin-top:1.5rem!important;}.mt-xl-5{margin-top:3rem!important;}.mt-xl-auto{margin-top:auto!important;}.me-xl-0{margin-right:0!important;}.me-xl-1{margin-right:.25rem!important;}.me-xl-2{margin-right:.5rem!important;}.me-xl-3{margin-right:1rem!important;}.me-xl-4{margin-right:1.5rem!important;}.me-xl-5{margin-right:3rem!important;}.me-xl-auto{margin-right:auto!important;}.mb-xl-0{margin-bottom:0!important;}.mb-xl-1{margin-bottom:.25rem!important;}.mb-xl-2{margin-bottom:.5rem!important;}.mb-xl-3{margin-bottom:1rem!important;}.mb-xl-4{margin-bottom:1.5rem!important;}.mb-xl-5{margin-bottom:3rem!important;}.mb-xl-auto{margin-bottom:auto!important;}.ms-xl-0{margin-left:0!important;}.ms-xl-1{margin-left:.25rem!important;}.ms-xl-2{margin-left:.5rem!important;}.ms-xl-3{margin-left:1rem!important;}.ms-xl-4{margin-left:1.5rem!important;}.ms-xl-5{margin-left:3rem!important;}.ms-xl-auto{margin-left:auto!important;}.p-xl-0{padding:0!important;}.p-xl-1{padding:.25rem!important;}.p-xl-2{padding:.5rem!important;}.p-xl-3{padding:1rem!important;}.p-xl-4{padding:1.5rem!important;}.p-xl-5{padding:3rem!important;}.px-xl-0{padding-right:0!important;padding-left:0!important;}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important;}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important;}.py-xl-0{padding-top:0!important;padding-bottom:0!important;}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-xl-0{padding-top:0!important;}.pt-xl-1{padding-top:.25rem!important;}.pt-xl-2{padding-top:.5rem!important;}.pt-xl-3{padding-top:1rem!important;}.pt-xl-4{padding-top:1.5rem!important;}.pt-xl-5{padding-top:3rem!important;}.pe-xl-0{padding-right:0!important;}.pe-xl-1{padding-right:.25rem!important;}.pe-xl-2{padding-right:.5rem!important;}.pe-xl-3{padding-right:1rem!important;}.pe-xl-4{padding-right:1.5rem!important;}.pe-xl-5{padding-right:3rem!important;}.pb-xl-0{padding-bottom:0!important;}.pb-xl-1{padding-bottom:.25rem!important;}.pb-xl-2{padding-bottom:.5rem!important;}.pb-xl-3{padding-bottom:1rem!important;}.pb-xl-4{padding-bottom:1.5rem!important;}.pb-xl-5{padding-bottom:3rem!important;}.ps-xl-0{padding-left:0!important;}.ps-xl-1{padding-left:.25rem!important;}.ps-xl-2{padding-left:.5rem!important;}.ps-xl-3{padding-left:1rem!important;}.ps-xl-4{padding-left:1.5rem!important;}.ps-xl-5{padding-left:3rem!important;}.gap-xl-0{gap:0!important;}.gap-xl-1{gap:.25rem!important;}.gap-xl-2{gap:.5rem!important;}.gap-xl-3{gap:1rem!important;}.gap-xl-4{gap:1.5rem!important;}.gap-xl-5{gap:3rem!important;}.row-gap-xl-0{row-gap:0!important;}.row-gap-xl-1{row-gap:.25rem!important;}.row-gap-xl-2{row-gap:.5rem!important;}.row-gap-xl-3{row-gap:1rem!important;}.row-gap-xl-4{row-gap:1.5rem!important;}.row-gap-xl-5{row-gap:3rem!important;}.column-gap-xl-0{column-gap:0!important;}.column-gap-xl-1{column-gap:.25rem!important;}.column-gap-xl-2{column-gap:.5rem!important;}.column-gap-xl-3{column-gap:1rem!important;}.column-gap-xl-4{column-gap:1.5rem!important;}.column-gap-xl-5{column-gap:3rem!important;}.text-xl-start{text-align:left!important;}.text-xl-end{text-align:right!important;}.text-xl-center{text-align:center!important;}}@media(min-width:1400px){.float-xxl-start{float:left!important;}.float-xxl-end{float:right!important;}.float-xxl-none{float:none!important;}.object-fit-xxl-contain{object-fit:contain!important;}.object-fit-xxl-cover{object-fit:cover!important;}.object-fit-xxl-fill{object-fit:fill!important;}.object-fit-xxl-scale{object-fit:scale-down!important;}.object-fit-xxl-none{object-fit:none!important;}.d-xxl-inline{display:inline!important;}.d-xxl-inline-block{display:inline-block!important;}.d-xxl-block{display:block!important;}.d-xxl-grid{display:grid!important;}.d-xxl-inline-grid{display:inline-grid!important;}.d-xxl-table{display:table!important;}.d-xxl-table-row{display:table-row!important;}.d-xxl-table-cell{display:table-cell!important;}.d-xxl-flex{display:flex!important;}.d-xxl-inline-flex{display:inline-flex!important;}.d-xxl-none{display:none!important;}.flex-xxl-fill{flex:1 1 auto!important;}.flex-xxl-row{flex-direction:row!important;}.flex-xxl-column{flex-direction:column!important;}.flex-xxl-row-reverse{flex-direction:row-reverse!important;}.flex-xxl-column-reverse{flex-direction:column-reverse!important;}.flex-xxl-grow-0{flex-grow:0!important;}.flex-xxl-grow-1{flex-grow:1!important;}.flex-xxl-shrink-0{flex-shrink:0!important;}.flex-xxl-shrink-1{flex-shrink:1!important;}.flex-xxl-wrap{flex-wrap:wrap!important;}.flex-xxl-nowrap{flex-wrap:nowrap!important;}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important;}.justify-content-xxl-start{justify-content:flex-start!important;}.justify-content-xxl-end{justify-content:flex-end!important;}.justify-content-xxl-center{justify-content:center!important;}.justify-content-xxl-between{justify-content:space-between!important;}.justify-content-xxl-around{justify-content:space-around!important;}.justify-content-xxl-evenly{justify-content:space-evenly!important;}.align-items-xxl-start{align-items:flex-start!important;}.align-items-xxl-end{align-items:flex-end!important;}.align-items-xxl-center{align-items:center!important;}.align-items-xxl-baseline{align-items:baseline!important;}.align-items-xxl-stretch{align-items:stretch!important;}.align-content-xxl-start{align-content:flex-start!important;}.align-content-xxl-end{align-content:flex-end!important;}.align-content-xxl-center{align-content:center!important;}.align-content-xxl-between{align-content:space-between!important;}.align-content-xxl-around{align-content:space-around!important;}.align-content-xxl-stretch{align-content:stretch!important;}.align-self-xxl-auto{align-self:auto!important;}.align-self-xxl-start{align-self:flex-start!important;}.align-self-xxl-end{align-self:flex-end!important;}.align-self-xxl-center{align-self:center!important;}.align-self-xxl-baseline{align-self:baseline!important;}.align-self-xxl-stretch{align-self:stretch!important;}.order-xxl-first{order:-1!important;}.order-xxl-0{order:0!important;}.order-xxl-1{order:1!important;}.order-xxl-2{order:2!important;}.order-xxl-3{order:3!important;}.order-xxl-4{order:4!important;}.order-xxl-5{order:5!important;}.order-xxl-last{order:6!important;}.m-xxl-0{margin:0!important;}.m-xxl-1{margin:.25rem!important;}.m-xxl-2{margin:.5rem!important;}.m-xxl-3{margin:1rem!important;}.m-xxl-4{margin:1.5rem!important;}.m-xxl-5{margin:3rem!important;}.m-xxl-auto{margin:auto!important;}.mx-xxl-0{margin-right:0!important;margin-left:0!important;}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important;}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important;}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important;}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important;}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important;}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important;}.my-xxl-0{margin-top:0!important;margin-bottom:0!important;}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important;}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important;}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important;}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important;}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important;}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important;}.mt-xxl-0{margin-top:0!important;}.mt-xxl-1{margin-top:.25rem!important;}.mt-xxl-2{margin-top:.5rem!important;}.mt-xxl-3{margin-top:1rem!important;}.mt-xxl-4{margin-top:1.5rem!important;}.mt-xxl-5{margin-top:3rem!important;}.mt-xxl-auto{margin-top:auto!important;}.me-xxl-0{margin-right:0!important;}.me-xxl-1{margin-right:.25rem!important;}.me-xxl-2{margin-right:.5rem!important;}.me-xxl-3{margin-right:1rem!important;}.me-xxl-4{margin-right:1.5rem!important;}.me-xxl-5{margin-right:3rem!important;}.me-xxl-auto{margin-right:auto!important;}.mb-xxl-0{margin-bottom:0!important;}.mb-xxl-1{margin-bottom:.25rem!important;}.mb-xxl-2{margin-bottom:.5rem!important;}.mb-xxl-3{margin-bottom:1rem!important;}.mb-xxl-4{margin-bottom:1.5rem!important;}.mb-xxl-5{margin-bottom:3rem!important;}.mb-xxl-auto{margin-bottom:auto!important;}.ms-xxl-0{margin-left:0!important;}.ms-xxl-1{margin-left:.25rem!important;}.ms-xxl-2{margin-left:.5rem!important;}.ms-xxl-3{margin-left:1rem!important;}.ms-xxl-4{margin-left:1.5rem!important;}.ms-xxl-5{margin-left:3rem!important;}.ms-xxl-auto{margin-left:auto!important;}.p-xxl-0{padding:0!important;}.p-xxl-1{padding:.25rem!important;}.p-xxl-2{padding:.5rem!important;}.p-xxl-3{padding:1rem!important;}.p-xxl-4{padding:1.5rem!important;}.p-xxl-5{padding:3rem!important;}.px-xxl-0{padding-right:0!important;padding-left:0!important;}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important;}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important;}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important;}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important;}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important;}.py-xxl-0{padding-top:0!important;padding-bottom:0!important;}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important;}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important;}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important;}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important;}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important;}.pt-xxl-0{padding-top:0!important;}.pt-xxl-1{padding-top:.25rem!important;}.pt-xxl-2{padding-top:.5rem!important;}.pt-xxl-3{padding-top:1rem!important;}.pt-xxl-4{padding-top:1.5rem!important;}.pt-xxl-5{padding-top:3rem!important;}.pe-xxl-0{padding-right:0!important;}.pe-xxl-1{padding-right:.25rem!important;}.pe-xxl-2{padding-right:.5rem!important;}.pe-xxl-3{padding-right:1rem!important;}.pe-xxl-4{padding-right:1.5rem!important;}.pe-xxl-5{padding-right:3rem!important;}.pb-xxl-0{padding-bottom:0!important;}.pb-xxl-1{padding-bottom:.25rem!important;}.pb-xxl-2{padding-bottom:.5rem!important;}.pb-xxl-3{padding-bottom:1rem!important;}.pb-xxl-4{padding-bottom:1.5rem!important;}.pb-xxl-5{padding-bottom:3rem!important;}.ps-xxl-0{padding-left:0!important;}.ps-xxl-1{padding-left:.25rem!important;}.ps-xxl-2{padding-left:.5rem!important;}.ps-xxl-3{padding-left:1rem!important;}.ps-xxl-4{padding-left:1.5rem!important;}.ps-xxl-5{padding-left:3rem!important;}.gap-xxl-0{gap:0!important;}.gap-xxl-1{gap:.25rem!important;}.gap-xxl-2{gap:.5rem!important;}.gap-xxl-3{gap:1rem!important;}.gap-xxl-4{gap:1.5rem!important;}.gap-xxl-5{gap:3rem!important;}.row-gap-xxl-0{row-gap:0!important;}.row-gap-xxl-1{row-gap:.25rem!important;}.row-gap-xxl-2{row-gap:.5rem!important;}.row-gap-xxl-3{row-gap:1rem!important;}.row-gap-xxl-4{row-gap:1.5rem!important;}.row-gap-xxl-5{row-gap:3rem!important;}.column-gap-xxl-0{column-gap:0!important;}.column-gap-xxl-1{column-gap:.25rem!important;}.column-gap-xxl-2{column-gap:.5rem!important;}.column-gap-xxl-3{column-gap:1rem!important;}.column-gap-xxl-4{column-gap:1.5rem!important;}.column-gap-xxl-5{column-gap:3rem!important;}.text-xxl-start{text-align:left!important;}.text-xxl-end{text-align:right!important;}.text-xxl-center{text-align:center!important;}}@media(min-width:1200px){.fs-1{font-size:2.5rem!important;}.fs-2{font-size:2rem!important;}.fs-3{font-size:1.75rem!important;}.fs-4{font-size:1.5rem!important;}}@media print{.d-print-inline{display:inline!important;}.d-print-inline-block{display:inline-block!important;}.d-print-block{display:block!important;}.d-print-grid{display:grid!important;}.d-print-inline-grid{display:inline-grid!important;}.d-print-table{display:table!important;}.d-print-table-row{display:table-row!important;}.d-print-table-cell{display:table-cell!important;}.d-print-flex{display:flex!important;}.d-print-inline-flex{display:inline-flex!important;}.d-print-none{display:none!important;}}:root{--bs-gray-100-rgb:248,249,250;--bs-gray-200-rgb:233,236,239;--bs-gray-300-rgb:222,226,230;--bs-gray-400-rgb:206,212,218;--bs-gray-500-rgb:173,181,189;--bs-gray-600-rgb:108,117,125;--bs-gray-700-rgb:73,80,87;--bs-gray-800-rgb:52,58,64;--bs-gray-900-rgb:33,37,41;--scroll-size:8px;--scroll-radius:0px;--scroll-track:var(--bs-light);--scroll-thumb-color:var(--bs-primary);--bb-offcanvas-horizontal-width-lg:768px!important;}.btn-xsm,.btn-group-xsm>.btn{--bs-btn-padding-y:.2rem;--bs-btn-padding-x:.3rem;--bs-btn-font-size:.7rem;--bs-btn-border-radius:.25rem;}.loading-container{width:100%;height:100%;display:flex;align-content:center;justify-content:center;align-items:center;flex-direction:column;}.loading-container .svg-container{width:8rem;height:8rem;display:flex;align-content:center;justify-content:center;align-items:center;}.loading-container .loading-progress{position:absolute;display:block;width:8rem;height:8rem;}.loading-container .loading-progress circle{fill:none;stroke:#6f42c1;stroke-width:.2rem;transform-origin:50% 50%;transform:rotate(-90deg);}.loading-container .loading-progress circle:last-child{stroke:#8c68cd;stroke-dasharray:calc(3.141*var(--blazor-load-percentage,0%)*.8),500%;transition:stroke-dasharray .05s ease-in-out;}.loading-container .loading-logo{position:absolute;fill:#8c68cd;height:70px;}.loading-container .loading-progress-text{font-weight:bold;}.loading-container .loading-progress-text:after{content:var(--blazor-load-percentage-text,"Loading");}::-webkit-scrollbar{width:4px;height:4px;}::-webkit-scrollbar-button{width:0;height:0;}::-webkit-scrollbar-thumb{background:#7d59bd;border:0 none #fff;border-radius:.375rem;}::-webkit-scrollbar-thumb:hover{background:#8c68cd;}::-webkit-scrollbar-thumb:active{background:#8c68cd;}::-webkit-scrollbar-track{background:#212529;border:0 none #fff;border-radius:.375rem;}::-webkit-scrollbar-track:hover{background:#1a1d20;}::-webkit-scrollbar-track:active{background:#1a1d20;}::-webkit-scrollbar-corner{background:transparent;}.table-container{max-height:100%;overflow-y:scroll;flex-grow:1;position:relative;}.table-container .loading{position:absolute;z-index:2;top:0;right:0;bottom:0;left:0;background:rgba(var(--bs-gray-900-rgb),.3);display:flex;align-items:center;justify-content:center;}.sticky-header{position:sticky;top:0;background-color:var(--bs-body-bg)!important;z-index:1;}td{vertical-align:middle;}.contained{display:-webkit-box;max-height:calc(61px - 1rem);-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;}@-webkit-keyframes moving-gradient{0%{background-position:-250px 0;}100%{background-position:250px 0;}}tr.row-placeholder td::after{content:" ";display:block;height:12px;background:linear-gradient(to right,rgba(var(--bs-body-color-rgb),.3) 20%,rgba(var(--bs-secondary-bg-rgb),.7) 50%,rgba(var(--bs-body-color-rgb),.3) 80%);background-size:500px 100px;animation-name:moving-gradient;animation-duration:1s;animation-iteration-count:infinite;animation-timing-function:linear;animation-fill-mode:forwards;}.monaco-normal{height:500px!important;}.monaco-small{height:300px!important;}.monaco-extra-small{height:100px!important;}.graph-canvas{cursor:grab;}.graph-canvas.grabbing{cursor:grabbing;}.graph-container,.graph-canvas{--stroke-color:#6c757d;--fill-color:#1a1d20;display:flex;flex-direction:column;}.graph-container .graph-controls,.graph-canvas .graph-controls{display:flex;justify-content:flex-end;gap:1rem;padding-right:1rem;}.graph-container .graph-controls .btn,.graph-canvas .graph-controls .btn{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0,0,0,.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none;color:var(--bs-btn-color);fill:var(--bs-btn-color);border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);}.graph-container .graph-controls .btn:hover,.graph-canvas .graph-controls .btn:hover{color:var(--bs-btn-hover-color);fill:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);}#default-node-gradient stop[offset="0"]{stop-color:#6c757d;}#default-node-gradient stop[offset="1"]{stop-color:#4c5258;}#catch-node-gradient stop[offset="0"]{stop-color:#ff821d;}#catch-node-gradient stop[offset="1"]{stop-color:#b35b14;}#raise-node-gradient stop[offset="0"]{stop-color:#dc3545;}#raise-node-gradient stop[offset="1"]{stop-color:#9a2530;}#call-node-gradient stop[offset="0"]{stop-color:#440154;}#call-node-gradient stop[offset="1"]{stop-color:#30013b;}#do-node-gradient stop[offset="0"]{stop-color:#481f70;}#do-node-gradient stop[offset="1"]{stop-color:#32164e;}#fork-node-gradient stop[offset="0"]{stop-color:#443983;}#fork-node-gradient stop[offset="1"]{stop-color:#30285c;}#for-node-gradient stop[offset="0"]{stop-color:#3b528b;}#for-node-gradient stop[offset="1"]{stop-color:#293961;}#listen-node-gradient stop[offset="0"]{stop-color:#31688e;}#listen-node-gradient stop[offset="1"]{stop-color:#224963;}#run-node-gradient stop[offset="0"]{stop-color:#287c8e;}#run-node-gradient stop[offset="1"]{stop-color:#1c5763;}#set-node-gradient stop[offset="0"]{stop-color:#21918c;}#set-node-gradient stop[offset="1"]{stop-color:#176662;}#switch-node-gradient stop[offset="0"]{stop-color:#20a486;}#switch-node-gradient stop[offset="1"]{stop-color:#16735e;}#try-catch-node-gradient stop[offset="0"]{stop-color:#35b779;}#try-catch-node-gradient stop[offset="1"]{stop-color:#258055;}#try-node-gradient stop[offset="0"]{stop-color:#5ec962;}#try-node-gradient stop[offset="1"]{stop-color:#428d45;}#emit-node-gradient stop[offset="0"]{stop-color:#90d743;}#emit-node-gradient stop[offset="1"]{stop-color:#65972f;}#wait-node-gradient stop[offset="0"]{stop-color:#c8e020;}#wait-node-gradient stop[offset="1"]{stop-color:#8c9d16;}.node,.cluster{cursor:pointer;--gradient-url:url(#default-node-gradient);}.node.default-task-node,.cluster.default-task-node{--gradient-url:url(#default-node-gradient);}.node.catch-task-node,.cluster.catch-task-node{--gradient-url:url(#catch-node-gradient);}.node.raise-task-node,.cluster.raise-task-node{--gradient-url:url(#raise-node-gradient);}.node.call-task-node,.cluster.call-task-node{--gradient-url:url(#call-node-gradient);}.node.do-task-node,.cluster.do-task-node{--gradient-url:url(#do-node-gradient);}.node.fork-task-node,.cluster.fork-task-node{--gradient-url:url(#fork-node-gradient);}.node.for-task-node,.cluster.for-task-node{--gradient-url:url(#for-node-gradient);}.node.listen-task-node,.cluster.listen-task-node{--gradient-url:url(#listen-node-gradient);}.node.run-task-node,.cluster.run-task-node{--gradient-url:url(#run-node-gradient);}.node.set-task-node,.cluster.set-task-node{--gradient-url:url(#set-node-gradient);}.node.switch-task-node,.cluster.switch-task-node{--gradient-url:url(#switch-node-gradient);}.node.try-catch-task-node,.cluster.try-catch-task-node{--gradient-url:url(#try-catch-node-gradient);}.node.try-task-node,.cluster.try-task-node{--gradient-url:url(#try-node-gradient);}.node.emit-task-node,.cluster.emit-task-node{--gradient-url:url(#emit-node-gradient);}.node.wait-task-node,.cluster.wait-task-node{--gradient-url:url(#wait-node-gradient);}.node .node-rectangle,.cluster .node-rectangle{stroke:var(--gradient-url);stroke-width:3px;}.node .node-cartouche,.cluster .node-cartouche{fill:var(--gradient-url);}.node .label-content,.cluster .label-content{width:100%;height:100%;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.node .label-content h3,.node .label-content .h3,.cluster .label-content h3,.cluster .label-content .h3{margin-bottom:.25rem;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.node .label-content p,.cluster .label-content p{margin:0;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;}.node .label-content pre,.cluster .label-content pre{margin:0;height:35px;text-overflow:ellipsis;overflow-x:hidden;overflow-y:auto;}.node.shape-cartouche .label-content,.cluster.shape-cartouche .label-content{text-align:left;padding:5px 5px 5px 65px;}.node.active rect,.cluster.active rect{fill:rgba(var(--bs-info-rgb),.2);}.node .badge circle,.cluster .badge circle{stroke-width:0;fill:var(--bs-primary);}.node .badge foreignObject,.cluster .badge foreignObject{text-align:center;font-size:8px;color:#fff;}.node .badge foreignObject div,.cluster .badge foreignObject div{display:flex;align-content:center;align-items:center;justify-content:center;}.node .activity-badge.activity-badge--active circle,.cluster .activity-badge.activity-badge--active circle{fill:var(--bs-success);}.node .activity-badge.activity-badge--faulted circle,.cluster .activity-badge.activity-badge--faulted circle{fill:var(--bs-danger);}.node.legend .node-rectangle,.cluster.legend .node-rectangle{fill:var(--gradient-url);}.node.legend div,.cluster.legend div{width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:#fff;}.symbol{fill:var(--bs-body-color);}.start-node circle{stroke-width:2px;}.end-node circle{stroke-width:5px;}.edge-label .label-content{text-align:center;background-color:#1a1d20;}.horizontal-collapse{border-right:1px solid var(--bs-dark-bg-subtle);overflow:scroll;}.horizontal-collapse:nth-last-child(1){border:0;}.horizontal-collapse .title{display:flex;gap:.5rem;}.horizontal-collapse .title i{margin-left:auto;}.horizontal-collapse:nth-child(1) .title{padding-left:0!important;}.horizontal-collapse.collapsed{overflow:hidden;}.horizontal-collapse.collapsed .title{width:22px;height:100%;flex-direction:column;}.horizontal-collapse.collapsed .title .label{display:block;transform:rotate(90deg) translate(3px);transform-origin:center;}.horizontal-collapse.collapsed .title i{margin-top:auto;}.workflow-instance-details h6,.workflow-instance-details .h6{font-weight:bold;}.workflow-instance-details .label{font-weight:lighter;}html,body{font-family:"Inconsolata",monospace;width:100%;height:100%;}#app{width:100%;height:100%;}h1:focus,.h1:focus{outline:0;}#blazor-error-ui{background:#2b3035;bottom:0;box-shadow:0 -1px 2px rgba(0,0,0,.2);display:none;left:0;padding:.6rem 1.25rem .7rem 1.25rem;position:fixed;width:100%;z-index:1000;}#blazor-error-ui .dismiss{cursor:pointer;position:absolute;right:.75rem;top:.5rem;}.blazor-error-boundary{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem,#b32121;padding:1rem 1rem 1rem 3.7rem;color:#fff;}.blazor-error-boundary::after{content:"An error has occurred.";}.flex-grow{flex-grow:1;}.svg-definitions,#svg-definitions{width:0!important;height:0!important;margin:0!important;padding:0!important;position:absolute;top:-100px;left:-100px;}.logo{fill:#8c68cd;height:75px;}.logo-large{height:300px;}.logo-typing{font-family:Rajdhani;color:#8c68cd;}.logo-typing-large{font-size:8rem;margin-right:0;}.vh-30{height:30vh!important;}.offcanvas{margin-top:1px;}.cursor-pointer{cursor:pointer;}.modal-confirmation .modal-footer{border-top:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color)!important;}.modal .modal-header{background-color:inherit!important;color:inherit!important;justify-content:space-between;}.h-100-px{height:100px;}.h-200-px{height:200px;}.h-300-px{height:300px;}.h-400-px{height:400px;}.h-500-px{height:500px;}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;height:16px;width:16px;margin-left:.4em;mask-image:url("data:image/svg+xml;utf8,");cursor:pointer;background-color:#c6b4e6;}.breadcrumb{margin-bottom:0;}.collapsible-instances .content{max-height:calc(100% - 80px)!important;}.bb-offcanvas-lg{--bs-offcanvas-width:80vw;}.monaco-editor.logs-container{height:300px;overflow:scroll;background-color:var(--vscode-editor-background);color:var(--vscode-editor-foreground);} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/app.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/app.scss index cd75019f7..e380ba3b2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/css/app.scss +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/app.scss @@ -1,306 +1,172 @@ -$bootstrap-icons-font-dir: '/lib/bootstrap-icons/font/fonts/'; - -@import "../lib/bootstrap-icons/font/bootstrap-icons"; -@import "./theme"; -@import "./splash"; -@import "./scroll.scss"; - +$bootstrap-icons-font-dir: '../lib/bootstrap-icons/font/fonts/'; + +@import "../lib/bootstrap-icons/font/bootstrap-icons.scss"; +@import "./theme/theme.scss"; +@import "./blazor-loading.scss"; +@import "./scrollbar.scss"; +@import "./table.scss"; +@import "./text-editor.scss"; +@import "./graph.scss"; +@import "./horizontal-collapse.scss"; +@import "./workflow-instance-details.scss"; + +html, body { + font-family: 'Inconsolata', monospace; + width: 100%; + height: 100%; +} -* { - padding: 0; - margin: 0; +#app { + width: 100%; + height: 100%; } -body { - @include scroll-style( - $size: var(--scroll-size, 10px), - $thumb: var(--scroll-thumb, none), - $thumb-color: var(--scroll-thumb-color, grey), - $thumb-radius: var(--scroll-thumb-radius, var(--scroll-radius)), - $track-color: var(--scroll-track, transparent), - $track-radius: var(--scroll-track-radius, var(--scroll-radius)) - ); +h1:focus { + outline: none; } #blazor-error-ui { - background: var(--bs-danger); - color: var(--bs-light); + background: $body-tertiary-bg-dark; bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); display: none; left: 0; padding: 0.6rem 1.25rem 0.7rem 1.25rem; position: fixed; width: 100%; z-index: 1000; - - a { - color: var(--bs-light); - text-decoration: none; - } - - .reload{ - font-weight: 600; - } - .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; - } } -.form-control.invalid { - border-color: red; +#blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; } -input[type=time]::-webkit-datetime-edit-ampm-field { - display: none; +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; } -input[type=time]::-webkit-clear-button { - -webkit-appearance: none; - -moz-appearance: none; - -o-appearance: none; - -ms-appearance: none; - appearance: none; - margin: -10px; -} - -.cursor-pointer { - cursor: pointer; +.blazor-error-boundary::after { + content: "An error has occurred." } .flex-grow { flex-grow: 1; } -.default.group .port { - width: 0px !important; - height: 0px !important; - margin: 0px !important; -} - -.monaco-normal { - height: 500px !important; -} - -.monaco-small { - height: 300px !important; -} - -.monaco-extra-small { - height: 100px !important; -} - -.bi { - width: 16px; - height: 16px; - margin-right: 8px; - display: inline-flex; - justify-content: flex-end; - align-items: center; -} - -.toast-container { - z-index: 1 !important; - position: fixed !important; - width: 25rem !important; - top: 0px !important; - right: 0px !important; +.svg-definitions, #svg-definitions { + width: 0 !important; + height: 0 !important; + margin: 0 !important; + padding: 0 !important; + position: absolute; + top: -100px; + left: -100px; } -.end-node circle { - stroke-width: 3px; +.logo { + fill: $primary-dark; + height: 75px; } -.btn-outline-dark:hover { - fill: white; +.logo-large { + height: 300px; } -.mh-50 { - max-height: 50% !important; +.logo-typing { + font-family: Rajdhani; + color: $primary-dark; + /* + background-image: -webkit-linear-gradient(0deg, $primary-dark 0%, $primary-bg-subtle-dark 100%); + background-clip: text; + -webkit-background-clip: text; + text-fill-color: transparent; + -webkit-text-fill-color: transparent; + */ } -.overflow-y-scroll { - overflow-y: scroll; +.logo-typing-large { + font-size: 8rem; + margin-right: 0px; } -.tab-content { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; +.vh-30{ + height: 30vh!important; } -.node, .cluster { - &.active { - rect { - fill: rgba(var(--bs-info-rgb), .2); - } - } - - .badge { - circle { - stroke-width: 0; - fill: var(--bs-primary); - } - - foreignObject { - text-align: center; - font-size: 8px; - color: white; - - div { - display: flex; - align-content: center; - align-items: center; - justify-content: center; - } - } - } - - .activity-badge { - &.activity-badge--active circle { - fill: var(--bs-success); - } - - &.activity-badge--faulted circle { - fill: var(--bs-danger); - } - } - - .add-state { - .add-state__label { - font-size: 12px; - font-weight: bold; - } - } +.offcanvas { + margin-top: 1px; } -.toolbar { - .btn { - display: flex; - align-items: center; - justify-content: flex-start; - } +.cursor-pointer { + cursor: pointer; } -.edges { - pointer-events: none; - - .used-for-compensation { - path { - stroke-dasharray: 4; - } +.modal-confirmation { + .modal-footer { + border-top: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color) !important; } } -.ghost { - opacity: 0.3; - pointer-events: none; -} - -.drop-destination { - rect, - circle, - ellipse { - stroke: red; - stroke-width: 2px; - stroke-dasharray: 2; +.modal { + .modal-header { + background-color: inherit !important; + color: inherit !important; + justify-content: space-between; } } -.card-metric { - box-shadow: 2px 2px 10px var(--bs-gray-200); - margin: 5px; - padding: 10px; - background-color: var(--bs-white); - border-radius: 5px; - transition: 0.3s linear all; -} - -.card-metric:hover { - box-shadow: 4px 4px 20px var(--bs-gray-200); - transition: 0.3s linear all; -} - -.card-metric.info { - background-color: var(--bs-info); - color: var(--bs-white); -} - -.card-metric.danger { - background-color: var(--bs-danger); - color: var(--bs-white); -} - -.card-metric.success { - background-color: var(--bs-success); - color: var(--bs-white); -} -.card-metric.secondary { - background-color: var(--bs-secondary); - color: var(--bs-white); +.h-100-px{ + height: 100px; } -.card-metric.warning { - background-color: var(--bs-warning); - color: var(--bs-white); +.h-200-px { + height: 200px; } -.card-metric i { - font-size: 5em; - opacity: 0.2; +.h-300-px { + height: 300px; } -.card-metric .card-metric-value { - font-size: 32px; - display: block; +.h-400-px { + height: 400px; } -.card-metric .card-metric-name { - font-style: italic; - text-transform: capitalize; - opacity: 0.5; - display: block; - font-size: 18px; +.h-500-px { + height: 500px; } -table .details { - text-align: right; +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; + height: 16px; + width: 16px; + margin-left: .4em; + mask-image: url("data:image/svg+xml;utf8,"); + cursor: pointer; + background-color: $input-focus-border-color; } -.upload-page { - gap: var(--bs-gutter-x); +.breadcrumb { + margin-bottom: 0; } -/*Breadcrumb*/ -.breadcrumb{ - a{ - text-decoration: none; - font-size: 0.9em; - display: flex; - align-items: center; - justify-content: flex-start; - .bi{ - margin-right: 4px; - } +.collapsible-instances { + .content { + max-height: calc(100% - 80px) !important; } } - -.vh-90 { - height: 90vh; -} - -.vh-85 { - height: 85vh; -} -.pxh-150{ - height: 150px; +.bb-offcanvas-lg { + --bs-offcanvas-width: 80vw; } -.form-select.bg-secondary { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23FFFFFF' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e") !important; +.monaco-editor.logs-container { + height: 300px; + overflow: scroll; + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); } \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/blazor-loading.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/blazor-loading.scss new file mode 100644 index 000000000..834ed3602 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/blazor-loading.scss @@ -0,0 +1,55 @@ +@import "./theme/_variables.scss"; + +.loading-container { + width: 100%; + height: 100%; + display: flex; + align-content: center; + justify-content: center; + align-items: center; + flex-direction: column; + + .svg-container { + width: 8rem; + height: 8rem; + display: flex; + align-content: center; + justify-content: center; + align-items: center; + } + + .loading-progress { + position: absolute; + display: block; + width: 8rem; + height: 8rem; + } + + .loading-progress circle { + fill: none; + stroke: $purple-500; + stroke-width: 0.2rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: $primary-dark; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + + .loading-logo { + position: absolute; + fill: $primary-dark; + height: 70px; + } + + .loading-progress-text { + font-weight: bold; + } + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/graph.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/graph.scss new file mode 100644 index 000000000..3b505d588 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/graph.scss @@ -0,0 +1,219 @@ +@import "./theme/_variables.scss"; +@import "../lib/bootstrap/scss/mixins"; + +//https://observablehq.com/@d3/color-schemes +//* !!! DON'T FORGET TO ADJUST INDEX.HTML s WHEN ADDING/REMOVING GRADIENTS !!! +$nodeColors: ( + "default": $mute, + "catch": #ff821d, // keep catch orange + "raise": $danger // keep raise red +); + +$nodeTypes: + "call", + "do", + "fork", + "for", + "listen", + "run", + "set", + "switch", + "try-catch", + "try", + "emit", + "wait"; + +$viridisScale: // generated with 13 steps, using 12 to exclude the last yellow + #440154, + #481f70, + #443983, + #3b528b, + #31688e, + #287c8e, + #21918c, + #20a486, + #35b779, + #5ec962, + #90d743, + #c8e020; + +@for $index from 1 through length($nodeTypes) { + $name: nth($nodeTypes, $index); + $color: nth($viridisScale, $index); + $nodeColors: map-merge($nodeColors, ($name: $color)); +} + +.graph-canvas { + cursor: grab; + &.grabbing { + cursor: grabbing; + } +} + +.graph-container, .graph-canvas { + --stroke-color: #{$mute}; + --fill-color: #{$dark-bg-subtle-dark}; + display: flex; + flex-direction: column; + + .graph-controls { + display: flex; + justify-content: flex-end; + gap: $spacer; + padding-right: 1rem; + + .btn { + @include button-outline-variant($mute); + color: var(--bs-btn-color); + fill: var(--bs-btn-color); + border: var(--bs-btn-border-width) solid var(--bs-btn-border-color); + + &:hover { + color: var(--bs-btn-hover-color); + fill: var(--bs-btn-hover-color); + background-color: var(--bs-btn-hover-bg); + border-color: var(--bs-btn-hover-border-color); + } + } + } +} + +@each $name, $color in $nodeColors { + ##{$name}-node-gradient { + + stop[offset="0"] { + stop-color: $color; + } + + stop[offset="1"] { + stop-color: shade-color($color, 30%); + } + } +} + +.node, .cluster { + cursor: pointer; + --gradient-url: url(#default-node-gradient); + + @each $name, $color in $nodeColors { + &.#{$name}-task-node { + --gradient-url: url(##{$name}-node-gradient); + } + } + + .node-rectangle { + stroke: var(--gradient-url); + stroke-width: 3px; + } + + .node-cartouche { + fill: var(--gradient-url); + } + + .label-content { + width: 100%; + height: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + + h3 { + margin-bottom: calc($spacer / 4); + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + p { + margin: 0; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + pre { + margin: 0; + height: 35px; + text-overflow: ellipsis; + overflow-x: hidden; + overflow-y: auto; + } + } + + &.shape-cartouche { + .label-content { + text-align: left; + padding: 5px 5px 5px 65px; + } + } + + &.active { + rect { + fill: rgba(var(--bs-info-rgb), .2); + } + } + + .badge { + circle { + stroke-width: 0; + fill: var(--bs-primary); + } + + foreignObject { + text-align: center; + font-size: 8px; + color: white; + + div { + display: flex; + align-content: center; + align-items: center; + justify-content: center; + } + } + } + + .activity-badge { + &.activity-badge--active circle { + fill: var(--bs-success); + } + + &.activity-badge--faulted circle { + fill: var(--bs-danger); + } + } + + &.legend { + .node-rectangle { + fill: var(--gradient-url); + } + + div { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: white; + } + } +} + +.symbol { + fill: var(--bs-body-color); +} + +.start-node circle { + stroke-width: 2px; +} + +.end-node circle { + stroke-width: 5px; +} + +.edge-label { + .label-content { + text-align: center; + background-color: #{$dark-bg-subtle-dark}; + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/horizontal-collapse.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/horizontal-collapse.scss new file mode 100644 index 000000000..365ca4d40 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/horizontal-collapse.scss @@ -0,0 +1,43 @@ +@import "./theme/_variables.scss"; + +.horizontal-collapse { + border-right: 1px solid var(--bs-dark-bg-subtle); + overflow: scroll; + + &:nth-last-child(1) { + border: none; + } + + .title { + display: flex; + gap: calc($spacer / 2); + + i { + margin-left: auto; + } + } + + &:nth-child(1) .title { + padding-left: 0 !important; + } + + &.collapsed { + overflow: hidden; + + .title { + width: 22px; + height: 100%; + flex-direction: column; + + .label { + display: block; + transform: rotate(90deg) translate(3px); + transform-origin: center; + } + + i { + margin-top: auto; + } + } + } +} diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/scroll.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/scroll.scss deleted file mode 100644 index 8f982886c..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/css/scroll.scss +++ /dev/null @@ -1,22 +0,0 @@ -@mixin scroll-style($size: 10px, $thumb: none, $thumb-color: grey, $thumb-radius: 10px, $track-color: transparent, $track-radius: 10px) { - // Respaldo para Firefox - scrollbar-color: $thumb-color $track-color; - scrollbar-width: thin; - // Navegadores basados en webkit - &::-webkit-scrollbar { - width: $size; - height: $size; - - &-track { - background-color: $track-color; - border-radius: $track-radius; - } - - &-thumb { - background-color: $thumb-color; - background-image: $thumb; - border-radius: $thumb-radius; - } - } -} - diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/scrollbar.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/scrollbar.scss new file mode 100644 index 000000000..a2f04bd69 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/scrollbar.scss @@ -0,0 +1,43 @@ +@import "./theme/_variables.scss"; + +::-webkit-scrollbar { + width: 4px; + height: 4px; +} + +::-webkit-scrollbar-button { + width: 0px; + height: 0px; +} + +::-webkit-scrollbar-thumb { + background: $primary-bg-subtle-dark; + border: 0px none #ffffff; + border-radius: $border-radius; +} + +::-webkit-scrollbar-thumb:hover { + background: $primary-dark; +} + +::-webkit-scrollbar-thumb:active { + background: $primary-dark; +} + +::-webkit-scrollbar-track { + background: $body-bg-dark; + border: 0px none #ffffff; + border-radius: $border-radius; +} + +::-webkit-scrollbar-track:hover { + background: $dark-bg-subtle-dark; +} + +::-webkit-scrollbar-track:active { + background: $dark-bg-subtle-dark; +} + +::-webkit-scrollbar-corner { + background: transparent; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/splash.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/splash.scss deleted file mode 100644 index 9303072c6..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/css/splash.scss +++ /dev/null @@ -1,105 +0,0 @@ -html, -body { - width: 100%; - height: 100%; - -} -#logo-container { -} - -.spinner-border { - margin-top: 3rem; - color: white; -} - -#logo-container, #animation-container { - width: 100%; - height: 100%; - background: var(--bs-primary) !important; - fill: white; - stroke: white; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - - svg { - max-width: 200px; - } -} - -#svg-definitions { - display: none; -} - -.logo-viewbox { - width: 25%; - display: block; - fill: var(--bs-black); - stroke: var(--bs-black); -} - -#splash-viewbox { - stroke-miterlimit: 5; - stroke-dashArray: 2000; - stroke-dashoffset: 2000; - fill-opacity: 0; -} - -.fade-out { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: fade-out-animation; - animation-duration: 1s; - animation-delay: 3s; -} - -@for $i from 1 through 8 { - $duration1: 1.5; - $duration2: 0.75; - $duration3: 0.75; - $delay1: $i/5; - $delay2: $delay1+2.0; - $delay3: $delay1+1.0; - .animation-phase-#{$i} { - animation-timing-function: ease-in-out; - animation-fill-mode: forwards; - animation-iteration: 1; - animation-name: - stroke-draw-animation, - stroke-fade-out-animation, - fade-in-animation; - animation-duration: #{$duration1}s, #{$duration2}s, #{$duration3}s; - animation-delay: #{$delay1}s, #{$delay2}s, #{$delay3}s; - } -} - -@keyframes stroke-draw-animation { - to { - stroke-dashOffset: 0; - } -} - -@keyframes stroke-fade-out-animation { - to { - stroke-opacity: 0; - } -} - -@keyframes fade-in-animation { - to { - fill-opacity: 1; - } -} - -@keyframes fade-out-animation { - to { - opacity: 0; - } -} diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/table.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/table.scss new file mode 100644 index 000000000..eacca3da1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/table.scss @@ -0,0 +1,67 @@ +.table-container { + max-height: 100%; + overflow-y: scroll; + flex-grow: 1; + position: relative; + + .loading { + position: absolute; + z-index: 2; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(var(--bs-gray-900-rgb), 0.3); + display: flex; + align-items: center; + justify-content: center; + } +} + +.sticky-header { + position: sticky; + top: 0; + background-color: var(--bs-body-bg) !important; + z-index: 1; +} + +/*tr { + height: 61px; + max-height: 61px; +}*/ + +td { + vertical-align: middle; +} + +.contained { + display: -webkit-box; + max-height: calc(61px - 1rem); + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} + +@-webkit-keyframes moving-gradient { + 0% { + background-position: -250px 0; + } + + 100% { + background-position: 250px 0; + } +} + +tr.row-placeholder td::after { + content: ' '; + display: block; + height: 12px; + background: linear-gradient(to right, rgba(var(--bs-body-color-rgb), 0.3) 20%, rgba(var(--bs-secondary-bg-rgb), 0.7) 50%, rgba(var(--bs-body-color-rgb), 0.3) 80%); + background-size: 500px 100px; + animation-name: moving-gradient; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-timing-function: linear; + animation-fill-mode: forwards; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/text-editor.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/text-editor.scss new file mode 100644 index 000000000..80116bad2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/text-editor.scss @@ -0,0 +1,11 @@ +.monaco-normal { + height: 500px !important; +} + +.monaco-small { + height: 300px !important; +} + +.monaco-extra-small { + height: 100px !important; +} diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/theme.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/theme.scss deleted file mode 100644 index c666f6add..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/css/theme.scss +++ /dev/null @@ -1,85 +0,0 @@ -/*! - * Bootstrap v5.1.3 (https://getbootstrap.com/) - * Copyright 2011-2021 The Bootstrap Authors - * Copyright 2011-2021 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - */ -// scss-docs-start import-stack -// Configuration - -@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,700;1,400&display=swap'); -@import "../lib/bootstrap/scss/functions"; - -$primary: #4276A7; -$success: #6EAB63; -$info: #6DA3B7; -$warning: #D5804C; -$danger: #C54C45; -$dark: #30475D; -$light: #F5FAFF; - -$font-family-base: 'Source Sans Pro', sans-serif; -$font-size-root: 15px; - -$body-color: #00305E; - -@import "../lib/bootstrap/scss/variables"; -@import "../lib/bootstrap/scss/mixins"; -@import "../lib/bootstrap/scss/utilities"; - -// Layout & components -@import "../lib/bootstrap/scss/root"; -@import "../lib/bootstrap/scss/reboot"; -@import "../lib/bootstrap/scss/type"; -@import "../lib/bootstrap/scss/images"; -@import "../lib/bootstrap/scss/containers"; -@import "../lib/bootstrap/scss/grid"; -@import "../lib/bootstrap/scss/tables"; -@import "../lib/bootstrap/scss/forms"; -@import "../lib/bootstrap/scss/buttons"; -@import "../lib/bootstrap/scss/transitions"; -@import "../lib/bootstrap/scss/dropdown"; -@import "../lib/bootstrap/scss/button-group"; -@import "../lib/bootstrap/scss/nav"; -@import "../lib/bootstrap/scss/navbar"; -@import "../lib/bootstrap/scss/card"; -@import "../lib/bootstrap/scss/accordion"; -@import "../lib/bootstrap/scss/breadcrumb"; -@import "../lib/bootstrap/scss/pagination"; -@import "../lib/bootstrap/scss/badge"; -@import "../lib/bootstrap/scss/alert"; -@import "../lib/bootstrap/scss/progress"; -@import "../lib/bootstrap/scss/list-group"; -@import "../lib/bootstrap/scss/close"; -// @import "../lib/bootstrap/scss/toasts"; -@import "../lib/bootstrap/scss/modal"; -@import "../lib/bootstrap/scss/tooltip"; -@import "../lib/bootstrap/scss/popover"; -@import "../lib/bootstrap/scss/carousel"; -@import "../lib/bootstrap/scss/spinners"; -@import "../lib/bootstrap/scss/offcanvas"; -@import "../lib/bootstrap/scss/placeholders"; - -// Helpers -@import "../lib/bootstrap/scss/helpers"; - -// Utilities -@import "../lib/bootstrap/scss/utilities/api"; -// scss-docs-end import-stack - -:root { - --bs-gray-100-rgb: #{to-rgb($gray-100)}; - --bs-gray-200-rgb: #{to-rgb($gray-200)}; - --bs-gray-300-rgb: #{to-rgb($gray-300)}; - --bs-gray-400-rgb: #{to-rgb($gray-400)}; - --bs-gray-500-rgb: #{to-rgb($gray-500)}; - --bs-gray-600-rgb: #{to-rgb($gray-600)}; - --bs-gray-700-rgb: #{to-rgb($gray-700)}; - --bs-gray-800-rgb: #{to-rgb($gray-800)}; - --bs-gray-900-rgb: #{to-rgb($gray-900)}; - - --scroll-size: 8px; - --scroll-radius: 0px; - --scroll-track: var(--bs-light); - --scroll-thumb-color: var(--bs-primary); -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/theme/_variables.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/theme/_variables.scss new file mode 100644 index 000000000..9f870ff94 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/theme/_variables.scss @@ -0,0 +1,91 @@ +@import "../../lib/bootstrap/scss/functions"; + + +$variable-prefix: bs- !default; // Deprecated in v5.2.0 for the shorter `$prefix` +$prefix: $variable-prefix !default; + +$primary: #8c68cd !default; +$primary-text: $primary; +$primary-bg-subtle: #7d59bd !default; +$primary-border-subtle: #714db1 !default; +$primary-dark: $primary; +$primary-text-dark: $primary; +$primary-bg-subtle-dark: $primary-bg-subtle; +$primary-border-subtle-dark: $primary-border-subtle; + +$secondary: #3c8cd6 !default; +$secondary-dark: $secondary; +$secondary-text-dark: $secondary; + +$accent: #9CEC5B !default; //lime +$accent-dark: $accent; +$accent-text-dark: $accent; +$accent-bg-subtle: $accent; +$accent-bg-subtle-dark: $accent; +$accent-border-subtle-dark: $accent; + +$cinereous: #837569 !default; //light brown/gray +$cinereous: $cinereous; +$cinereous-text-dark: $cinereous; +$cinereous-bg-subtle: $cinereous; +$cinereous-bg-subtle-dark: $cinereous; +$cinereous-border-subtle-dark: $cinereous; + +$verdigris: #50C5B7 !default; //blue-green +$verdigris-dark: $verdigris; +$verdigris-text-dark: $verdigris; +$verdigris-bg-subtle: $verdigris; +$verdigris-bg-subtle-dark: $verdigris; +$verdigris-border-subtle-dark: $verdigris; + +$icterine: #F0F465 !default; //pale yellow +$icterine-dark: $verdigris; +$icterine-text-dark: $verdigris; +$icterine-bg-subtle: $icterine; +$icterine-bg-subtle-dark: $icterine; +$icterine-border-subtle-dark: $icterine; + +@import "../../lib/bootstrap/scss/variables"; + + +$link-color-dark: $purple-400; +$link-hover-color-dark: $purple-200; + + +$mute: $gray-600 !default; +$mute-dark: $mute; +$mute-text-dark: $mute; + +@import "../../lib/bootstrap/scss/variables-dark"; + +$table-color: $body-color-dark; + +@import "../../lib/bootstrap/scss/functions"; +@import "../../lib/bootstrap/scss/maps"; +@import "../../lib/bootstrap/scss/mixins"; + +$extra-colors: ( + "accent": $accent, + "cinereous": $cinereous, + "verdigris": $verdigris, + "icterine": $icterine, + "mute": $mute +); + +$colors: map-merge( $colors, $extra-colors ); +$theme-colors: map-merge( $theme-colors, $extra-colors ); +$theme-colors-bg-subtle: map-merge( $theme-colors-bg-subtle, $extra-colors ); +$theme-colors-border-subtle: map-merge( $theme-colors-border-subtle, $extra-colors ); +$theme-colors-text-dark: map-merge( $theme-colors-text-dark, $extra-colors ); +$theme-colors-bg-subtle-dark: map-merge( $theme-colors-bg-subtle-dark, $extra-colors ); +$theme-colors-border-subtle-dark: map-merge( $theme-colors-border-subtle-dark, $extra-colors ); +$theme-colors-rgb: map-loop( $theme-colors, to-rgb, "$value" ); + +$utilities-colors: map-merge( $utilities-colors, $theme-colors-rgb ); +$utilities-text: map-merge( $utilities-text, $theme-colors-rgb ); +$utilities-text-emphasis-colors: map-merge( $utilities-text-emphasis-colors, $extra-colors ); +$utilities-bg-subtle: map-merge( $utilities-bg-subtle, $extra-colors ); +$utilities-border-subtle: map-merge( $utilities-border-subtle, $extra-colors ); + +$utilities-text-colors: map-loop( $utilities-text, rgba-css-var, "$key", "text" ); +$utilities-bg-colors: map-loop( $utilities-colors, rgba-css-var, "$key", "bg" ); \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/theme/theme.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/theme/theme.scss new file mode 100644 index 000000000..2a48c30f9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/theme/theme.scss @@ -0,0 +1,87 @@ +@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200..900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;400;500;600;700&display=swap'); + +$font-family-base: 'Inconsolata', monospace; +$font-size-root: 15px; + +@import "../../lib/bootstrap/scss/mixins/banner"; +@include bsBanner(""); + +// scss-docs-start import-stack +// Configuration +@import "./_variables.scss"; +//@import "../../lib/bootstrap/scss/maps"; +//@import "../../lib/bootstrap/scss/mixins"; +@import "../../lib/bootstrap/scss/utilities"; + +// Layout & components +@import "../../lib/bootstrap/scss/root"; +@import "../../lib/bootstrap/scss/reboot"; +@import "../../lib/bootstrap/scss/type"; +@import "../../lib/bootstrap/scss/images"; +@import "../../lib/bootstrap/scss/containers"; +@import "../../lib/bootstrap/scss/grid"; +@import "../../lib/bootstrap/scss/tables"; +@import "../../lib/bootstrap/scss/forms"; +@import "../../lib/bootstrap/scss/buttons"; + +@import "../../lib/bootstrap/scss/transitions"; +@import "../../lib/bootstrap/scss/dropdown"; +@import "../../lib/bootstrap/scss/button-group"; +@import "../../lib/bootstrap/scss/nav"; +@import "../../lib/bootstrap/scss/navbar"; +@import "../../lib/bootstrap/scss/card"; +@import "../../lib/bootstrap/scss/accordion"; +@import "../../lib/bootstrap/scss/breadcrumb"; +@import "../../lib/bootstrap/scss/pagination"; +@import "../../lib/bootstrap/scss/badge"; +@import "../../lib/bootstrap/scss/alert"; +@import "../../lib/bootstrap/scss/progress"; +@import "../../lib/bootstrap/scss/list-group"; +@import "../../lib/bootstrap/scss/close"; +@import "../../lib/bootstrap/scss/toasts"; +@import "../../lib/bootstrap/scss/modal"; +@import "../../lib/bootstrap/scss/tooltip"; +@import "../../lib/bootstrap/scss/popover"; +@import "../../lib/bootstrap/scss/carousel"; +@import "../../lib/bootstrap/scss/spinners"; +@import "../../lib/bootstrap/scss/offcanvas"; +@import "../../lib/bootstrap/scss/placeholders"; + +// Helpers +@import "../../lib/bootstrap/scss/helpers"; + +// Utilities +@import "../../lib/bootstrap/scss/utilities/api"; +// scss-docs-end import-stack + + +:root { + --bs-gray-100-rgb: #{to-rgb($gray-100)}; + --bs-gray-200-rgb: #{to-rgb($gray-200)}; + --bs-gray-300-rgb: #{to-rgb($gray-300)}; + --bs-gray-400-rgb: #{to-rgb($gray-400)}; + --bs-gray-500-rgb: #{to-rgb($gray-500)}; + --bs-gray-600-rgb: #{to-rgb($gray-600)}; + --bs-gray-700-rgb: #{to-rgb($gray-700)}; + --bs-gray-800-rgb: #{to-rgb($gray-800)}; + --bs-gray-900-rgb: #{to-rgb($gray-900)}; + --scroll-size: 8px; + --scroll-radius: 0px; + --scroll-track: var(--bs-light); + --scroll-thumb-color: var(--bs-primary); + --bb-offcanvas-horizontal-width-lg: 768px !important; +} + +$btn-padding-y-xsm: .20rem !default; +$btn-padding-x-xsm: .30rem !default; +$btn-font-size-xsm: $font-size-base * .7 !default; +$btn-border-radius-xsm: .25rem !default; + +.btn-xsm { + @include button-size($btn-padding-y-xsm, $btn-padding-x-xsm, $btn-font-size-xsm, $btn-border-radius-xsm); +} + +.btn-group-xsm > .btn { + @extend .btn-xsm; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/timeline.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/timeline.scss new file mode 100644 index 000000000..07e7640de --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/timeline.scss @@ -0,0 +1,27 @@ +.y-tick { + stroke: var(--bs-gray-700); + fill: none; + stroke-width: 1px; +} + +.axis g.tick line { + stroke: var(--bs-gray-700); + fill: none; + stroke-width: 1px; +} + +.line-separator, +.x-axis { + stroke: var(--bs-gray-700); + fill: none; + stroke-width: 1px; +} + +.drop-line:last-child .line-separator { + display: none; +} + +text { + stroke: none; + fill: var(--bs-body-color); +} diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/css/workflow-instance-details.scss b/src/dashboard/Synapse.Dashboard/wwwroot/css/workflow-instance-details.scss new file mode 100644 index 000000000..99130d70d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/css/workflow-instance-details.scss @@ -0,0 +1,9 @@ +.workflow-instance-details { + h6 { + font-weight: bold; + } + + .label { + font-weight: lighter; + } +} diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/favicon.ico b/src/dashboard/Synapse.Dashboard/wwwroot/favicon.ico deleted file mode 100644 index 836a0e277..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/favicon.ico and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.otf b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.otf new file mode 100644 index 000000000..62083c8cc Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.otf differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.ttf b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.ttf new file mode 100644 index 000000000..3ebcfed4d Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.ttf differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.woff b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.woff new file mode 100644 index 000000000..832f418ab Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.woff differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.woff2 b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.woff2 new file mode 100644 index 000000000..a392a37e0 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/Aquatico-Regular.woff2 differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.eot b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.eot deleted file mode 100644 index f98177dbf..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.eot and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.otf b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.otf deleted file mode 100644 index f6bd6846a..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.otf and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.svg b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.svg deleted file mode 100644 index 32b2c4e99..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.svg +++ /dev/null @@ -1,543 +0,0 @@ - - - - - -Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014 - By P.J. Onori -Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.ttf b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.ttf deleted file mode 100644 index fab604866..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.ttf and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.woff b/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.woff deleted file mode 100644 index f9309988a..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/fonts/open-iconic.woff and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/icon-192.png b/src/dashboard/Synapse.Dashboard/wwwroot/icon-192.png deleted file mode 100644 index 166f56da7..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/icon-192.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/icon-512.png b/src/dashboard/Synapse.Dashboard/wwwroot/icon-512.png deleted file mode 100644 index c2dd4842d..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/icon-512.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/android-chrome-192x192.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/android-chrome-192x192.png new file mode 100644 index 000000000..5313d7df1 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/android-chrome-192x192.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/android-chrome-512x512.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/android-chrome-512x512.png new file mode 100644 index 000000000..d4bc0e942 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/android-chrome-512x512.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/apple-touch-icon.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/apple-touch-icon.png new file mode 100644 index 000000000..d38409bad Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/apple-touch-icon.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/cloudevents-icon-color.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/cloudevents-icon-color.png deleted file mode 100644 index 67b50a84c..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/img/cloudevents-icon-color.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/cncf-stacked-color.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/cncf-stacked-color.png deleted file mode 100644 index 96bd009f0..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/img/cncf-stacked-color.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon-16x16.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon-16x16.png new file mode 100644 index 000000000..03eeb5ce3 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon-16x16.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon-32x32.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon-32x32.png new file mode 100644 index 000000000..873afea25 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon-32x32.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon.ico b/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon.ico new file mode 100644 index 000000000..d4074eb7e Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/favicon.ico differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo.eps new file mode 100644 index 000000000..c982d91d9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo.eps @@ -0,0 +1,386 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:22 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Aquatico-Regular +11 dict begin +/FontType 42 def +/FontName /Aquatico-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674200d1406cb00000720000000346670676d9e3611ca0000 +075400000e15676c7966749368660000009c000006846865616411b678940000156c00000036 +68686561084f03d7000015a400000024686d7478128e0148000015c80000001c6c6f6361070a +08ca000015e4000000106d61787001ea0f15000015f400000020707265706846c89c00001614 +000000a7000a005dff4c019a03340003000f001500190023002900350039003d004800ec40e9 +400121014b001618151516720000000702000767060102050103040203670004000a0b040a67 +000b250c0209080b0967000800110d08116700140e0d145710010d000e0f0d0e67000f001213 +0f126700131a01181613186700150017191517680019001c1d191c67001d26011e1b1d1e6700 +1b00231f1b236722011f0021201f2167002001012057002020015f24010120014f3a3a161600 +0048474645444342413f3e3a3d3a3d3c3b393837363534333231302f2e2d2c2b2a2928272625 +24232221201f1e1d1c1b1a1619161918171514131211100f0e0d0c0b0a090807060504000300 +03112706172b1711211103331523153335233533352307333523352317353315073315231533 +3533352317231533352307333533152335231533352303333523173533150733071533352337 +3335235d013df24142a64242a501a6426421214242426442a68585a6214322216421a66442a6 +a62164854646a6664620a6b403e8fc1803842521212521e922464624245e252146217e2264b2 +172f507171fee271502f2f592f21212f210000010028fff002630323004d00f9b51d01010001 +4c4bb0095058402500010004000104800004030004037e0000000261000202174d0003030561 +0601050515054e1b4bb00b5058402500010004000104800004030004037e0000000261000202 +174d0003030561060105050f054e1b4bb00d5058402500010004000104800004030004037e00 +00000261000202174d00030305610601050515054e1b4bb00f50584025000100040001048000 +04030004037e0000000261000202174d0003030561060105050f054e1b402500010004000104 +800004030004037e0000000261000202174d00030305610601050515054e5959595940100000 +004d004c43413b392a282d0707192b04363637363534272e0235343633321617161716161716 +3332363736353427262627262322070615141716171617161617161514070606070623222726 +27262726232206151417161716171633017a73630e05e35e613163441f2e1536210307040a08 +0a12060c030a3b2a4253b44410172358365745600f05080d3a2725201b23652d0d080809121c +021342334d2728102050441c17a437172333293f3a050814250207030509080e1208071c3310 +1a8820353c263b2317120e302e110e1415202808070511371204042013090535231d0d060000 +00010028000002a50312001f001d401a190d05030001014c020101010e4d00000012004e262a +280307192b001615140701111406232226351101263534363736333216171313363633321702 +990c08fefb1d15141efefc080c0a0c100c1607e3e407160c100c0303170c100cfe7afef4141e +1e14010c01860c100c1707080c0afeab01550a0c080000010028000002c80313001b00244021 +180a020200014c010100000e4d040302020212024e0000001b001a2525250507192b24363511 +34262322061511012623220615111416333236351101163302ae1a1e14151dfe1c0e18151d1d +15151d01e30d19011b1502b0151d1d15fddb0244121d15fd52151d1d150224fdbb1000000002 +0023ffff02fd03120022002b002a40272a0104002b29020204024c0004000201040269000000 +0e4d0301010112014e222426282705071b2b2436353427012626232206070106151417163332 +37363736373633321717161633323702232207060607131302ed1004fec9051a0e0f1807fec4 +08110b0d16133f4d474f2930353345071a0e0c08d01c37362a6128c0970b190e0c0902ae0d10 +0f0efd521010160b07133d302c0f0a0d9a0e0f04011b0c0825170199feb40001002800000277 +0312002b002940260001000204010269000000035f0003030e4d0501040412044e0000002b00 +2a3b242a2306071a2b3236351133321617161716151406070623220615141633323736363534 +2726272627262321220615111416336f1df70a2b171e10162021396c141e1e148750383b1412 +222f421f1cfed7141e1e141d15027c090d121c273b2c3c13211d15151d2e206b4740342e222e +10081d15fd52151d000100280000026a03120020002f402c0003000405030467000202015f00 +01010e4d06010505005f00000012004e00000020001f242124353407071b2b24161514062321 +2226351134363321321615140623211521321615140623211521024d1d1d15fe22141e1e1401 +d4151d1d15fe5e0172151d1d15fe8e01ac641d15151d1d1502ae151d1d15151df11e14151df5 +000000000000000000000000000000000000000000640064006400640312fff5031203120000 +fff50312fff40312031e0000fff5b0002c20b0005558455920204bb8000e514bb006535a58b0 +341bb028596066208a5558b0022561b908000800636323621b2121b00059b000432344b20001 +004360422db0012cb02060662db0022c232123212db0032c2064b3031415004243b013432060 +6042b102144342b1250343b00243547820b00c23b00243436164b0045078b2020202436042b0 +21651c21b0024343b20e1501421c20b002432342b213011343604223b00050586559b2160102 +4360422db0042cb0032bb015435823212321b016434323b000505865591b206420b0c050b004 +265ab228010d43456345b006455821b0032559525b582123211b8a5820b050505821b040591b +20b038505821b038595920b1010d434563456164b028505821b1010d4345634520b030505821 +b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036505821 +b036601b605959591bb00225b00c4363b0005258b0004bb00a505821b00c431b4bb01e505821 +b01e4b61b8100063b00c4363b80500625959646159b0012b595923b00050586559592064b016 +432342592db0052c204520b00425616420b007435058b0072342b00823421b212159b001602d +b0062c23212321b0032b2064b107624220b0082342b00645581bb1010d434563b1010d43b001 +604563b0052a2120b00843208a208ab0012bb1300525b00426515860501b6152595823592159 +20b0405358b0012b1b21b0405923b000505865592db0072cb009432bb20002004360422db008 +2cb00923422320b000234261b0026266b00163b00160b0072a2db0092c20204520b00e4363b8 +04006220b0005058b040605966b001636044b001602db00a2cb2090e004345422a21b2000100 +4360422db00b2cb000432344b20001004360422db00c2c20204520b0012b23b00043b0042560 +20458a2361206420b020505821b0001bb0305058b0201bb040595923b00050586559b0032523 +614444b001602db00d2c20204520b0012b23b00043b004256020458a23612064b0245058b000 +1bb0405923b00050586559b0032523614444b001602db00e2c20b0002342b30d0c0003455058 +211b2321592a212db00f2cb1020245b06461442db0102cb001602020b00f434ab000505820b0 +0f234259b010434ab000525820b0102342592db0112c20b0106266b0016320b80400638a2361 +b0114360208a6020b0112342232db0122c4b5458b10464445924b00d6523782db0132c4b5158 +4b5358b1046444591b215924b0136523782db0142cb10012435558b1121243b0016142b0112b +59b00043b0022542b10f022542b110022542b001162320b003255058b101004360b00425428a +8a208a2361b0102a2123b00161208a2361b0102a211bb101004360b0022542b0022561b0102a +2159b00f4347b010434760b0026220b0005058b040605966b0016320b00e4363b804006220b0 +005058b040605966b0016360b10000132344b00143b0003eb20101014360422db0152c00b100 +02455458b01223422045b00e2342b00d23b001604220b01423422060b00161b7181801001100 +13004242428a6020b0144360b0142342b114082bb08b2b1b22592db0162cb100152b2db0172c +b101152b2db0182cb102152b2db0192cb103152b2db01a2cb104152b2db01b2cb105152b2db0 +1c2cb106152b2db01d2cb107152b2db01e2cb108152b2db01f2cb109152b2db02b2c2320b010 +6266b00163b006604b545823202eb0015d1b2121592db02c2c2320b0106266b00163b016604b +545823202eb001711b2121592db02d2c2320b0106266b00163b026604b545823202eb001721b +2121592db0202c00b00f2bb10002455458b01223422045b00e2342b00d23b00160422060b001 +61b518180100110042428a60b114082bb08b2b1b22592db0212cb100202b2db0222cb101202b +2db0232cb102202b2db0242cb103202b2db0252cb104202b2db0262cb105202b2db0272cb106 +202b2db0282cb107202b2db0292cb108202b2db02a2cb109202b2db02e2c203cb001602db02f +2c2060b01860204323b0016043b0022561b00160b02e2a212db0302cb02f2bb02f2a2db0312c +2020472020b00e4363b804006220b0005058b040605966b001636023613823208a5558204720 +20b00e4363b804006220b0005058b040605966b00163602361381b21592db0322c00b1000245 +5458b10e064542b00116b0312ab1050115455830591b22592db0332c00b00f2bb10002455458 +b10e064542b00116b0312ab1050115455830591b22592db0342c2035b001602db0352c00b10e +064542b0014563b804006220b0005058b040605966b00163b0012bb00e4363b804006220b000 +5058b040605966b00163b0012bb00016b40000000000443e2338b13401152a212db0362c203c +204720b00e4363b804006220b0005058b040605966b0016360b0004361382db0372c2e173c2d +b0382c203c204720b00e4363b804006220b0005058b040605966b0016360b0004361b0014363 +382db0392cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b00123 +42b238010115142a2db03a2cb00016b0172342b00425b004254723472361b10c0042b00b432b +658a2e2320203c8a382db03b2cb00016b0172342b00425b00425202e472347236120b0062342 +b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a5942422320b00a4320 +8a234723472361234660b00643b0026220b0005058b040605966b001636020b0012b208a8a61 +20b00443606423b0054361645058b00443611bb005436059b00325b0026220b0005058b04060 +5966b0016361232020b00426234661381b23b00a4346b00225b00a4347234723616020b00643 +b0026220b0005058b040605966b00163602320b0012b23b0064360b0012bb0052561b00525b0 +026220b0005058b040605966b00163b004266120b00425606423b0032560645058211b232159 +232020b0042623466138592db03c2cb00016b0172342202020b00526202e4723472361233c38 +2db03d2cb00016b017234220b00a2342202020462347b0012b2361382db03e2cb00016b01723 +42b00325b002254723472361b00054582e203c23211bb00225b00225472347236120b00525b0 +04254723472361b00625b0052549b0022561b9080008006363232058621b215963b804006220 +b0005058b040605966b0016360232e2320203c8a382321592db03f2cb00016b017234220b00a +43202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a382db0 +402c23202e46b0022546b0174358501b525958203c592eb13001142b2db0412c23202e46b002 +2546b0174358521b505958203c592eb13001142b2db0422c23202e46b0022546b0174358501b +525958203c5923202e46b0022546b0174358521b505958203c592eb13001142b2db0432cb03a +2b23202e46b0022546b0174358501b525958203c592eb13001142b2db0442cb03b2b8a20203c +b00623428a3823202e46b0022546b0174358501b525958203c592eb13001142bb006432eb030 +2b2db0452cb00016b00425b0042620202046234761b00c23422e4723472361b00b432b23203c +202e2338b13001142b2db0462cb10a042542b00016b00425b00425202e472347236120b00623 +42b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a594242232047b006 +43b0026220b0005058b040605966b001636020b0012b208a8a6120b00443606423b005436164 +5058b00443611bb005436059b00325b0026220b0005058b040605966b0016361b00225466138 +23203c23381b212020462347b0012b2361382159b13001142b2db0472cb1003a2b2eb1300114 +2b2db0482cb1003b2b212320203cb00623422338b13001142bb006432eb0302b2db0492cb000 +152047b0002342b20001011514132eb0362a2db04a2cb000152047b0002342b2000101151413 +2eb0362a2db04b2cb100011413b0372a2db04c2cb0392a2db04d2cb000164523202e20468a23 +6138b13001142b2db04e2cb00a2342b04d2b2db04f2cb20000462b2db0502cb20001462b2db0 +512cb20100462b2db0522cb20101462b2db0532cb20000472b2db0542cb20001472b2db0552c +b20100472b2db0562cb20101472b2db0572cb3000000432b2db0582cb3000100432b2db0592c +b3010000432b2db05a2cb3010100432b2db05b2cb3000001432b2db05c2cb3000101432b2db0 +5d2cb3010001432b2db05e2cb3010101432b2db05f2cb20000452b2db0602cb20001452b2db0 +612cb20100452b2db0622cb20101452b2db0632cb20000482b2db0642cb20001482b2db0652c +b20100482b2db0662cb20101482b2db0672cb3000000442b2db0682cb3000100442b2db0692c +b3010000442b2db06a2cb3010100442b2db06b2cb3000001442b2db06c2cb3000101442b2db0 +6d2cb3010001442b2db06e2cb3010101442b2db06f2cb1003c2b2eb13001142b2db0702cb100 +3c2bb0402b2db0712cb1003c2bb0412b2db0722cb00016b1003c2bb0422b2db0732cb1013c2b +b0402b2db0742cb1013c2bb0412b2db0752cb00016b1013c2bb0422b2db0762cb1003d2b2eb1 +3001142b2db0772cb1003d2bb0402b2db0782cb1003d2bb0412b2db0792cb1003d2bb0422b2d +b07a2cb1013d2bb0402b2db07b2cb1013d2bb0412b2db07c2cb1013d2bb0422b2db07d2cb100 +3e2b2eb13001142b2db07e2cb1003e2bb0402b2db07f2cb1003e2bb0412b2db0802cb1003e2b +b0422b2db0812cb1013e2bb0402b2db0822cb1013e2bb0412b2db0832cb1013e2bb0422b2db0 +842cb1003f2b2eb13001142b2db0852cb1003f2bb0402b2db0862cb1003f2bb0412b2db0872c +b1003f2bb0422b2db0882cb1013f2bb0402b2db0892cb1013f2bb0412b2db08a2cb1013f2bb0 +422b2db08b2cb20b0003455058b0061bb2040203455823211b215959422bb00865b003245078 +b1050115455830592d0000000001000000010000817977795f0f3cf5000703e800000000d572 +f60700000000d8cd3ee2000aff4c0451037b0000000700020000000000000001000003fcff4c +000004790000000a045100010000000000000000000000000000000701f4005d028c002802cd +002802f0002803200023029f002802920028000000d801c6020a024a02a602fa034200010000 +00070071000a000000000002004e008d008d000000fd0e1500000000004bb800c85258b10101 +8e59b001b9080008006370b1000742b21701002ab1000742b30c08010a2ab1000742b3140601 +0a2ab1000842ba03400001000b2ab1000942ba00400001000b2ab90003000044b12401885158 +b0408858b90003006444b12801885158b808008858b90003000044591bb12701885158ba0880 +0001044088635458b900030000445959595959b30e06010e2ab801ff85b0048db1020044b305 +64060044440000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0.54902 0.407843 0.803922 rg +BT +168.077333 0 0 168.077333 100 228.679932 Tm +/f-0-0 1 Tf +(SYNAPSE)Tj +ET +707.109 625.969 m 707.109 645.617 695.551 661.797 678.215 668.734 c 678.215 + 669.891 678.215 671.043 678.215 672.199 c 678.215 709.188 648.16 739.238 + 611.176 739.238 c 607.707 739.238 605.395 739.238 601.926 738.082 c 591.523 + 753.109 574.188 762.355 555.695 762.355 c 542.98 762.355 531.422 758.891 + 522.176 751.953 c 511.773 758.891 499.059 762.355 485.188 762.355 c 463.227 + 762.355 444.734 751.953 432.02 736.93 c 430.863 736.93 429.707 736.93 428.551 + 736.93 c 392.719 736.93 363.824 710.344 359.203 675.668 c 329.148 667.578 + 307.188 640.992 307.188 607.473 c 307.188 569.332 338.395 538.125 376.539 + 538.125 c 388.098 538.125 398.5 540.434 408.902 546.215 c 426.238 527.723 + 450.512 517.316 478.254 517.316 c 484.031 517.316 489.812 517.316 495.59 + 518.473 c 503.68 489.578 530.266 467.617 562.629 467.617 c 584.59 467.617 + 604.238 478.02 616.953 493.047 c 622.734 487.266 634.293 473.395 629.668 + 449.125 c 629.668 449.125 652.785 471.086 642.383 503.449 c 675.902 508.07 + 702.484 536.969 702.484 572.797 c 702.484 580.891 701.328 590.137 697.863 + 597.07 c 703.641 605.16 707.109 615.566 707.109 625.969 c h +686.305 593.605 m 688.617 586.668 689.77 580.891 689.77 573.953 c 689.77 + 542.746 664.344 516.164 631.98 516.164 c 629.668 516.164 627.355 516.164 + 625.043 516.164 c 616.953 517.316 l 612.332 510.383 l 601.926 491.891 583.434 + 480.332 562.629 480.332 c 537.199 480.332 514.082 497.668 507.148 521.941 + c 503.68 532.344 l 492.121 530.031 l 487.5 528.875 481.719 528.875 477.098 + 528.875 c 453.98 528.875 433.176 538.125 416.992 554.305 c 411.215 561.238 + l 403.125 557.773 l 395.031 553.148 385.785 551.992 376.539 551.992 c 345.332 + 551.992 318.746 577.422 318.746 609.785 c 318.746 636.371 336.086 659.488 + 361.512 665.266 c 369.605 667.578 l 370.758 675.668 l 374.227 704.562 398.5 + 725.371 427.395 725.371 c 428.551 725.371 429.707 725.371 430.863 725.371 + c 436.641 725.371 l 440.109 729.992 l 450.512 743.863 466.695 750.797 484.031 + 750.797 c 494.434 750.797 504.836 747.332 514.082 741.551 c 521.02 736.93 + l 530.266 742.707 l 538.355 748.484 546.449 750.797 556.852 750.797 c 571.875 + 750.797 584.59 743.863 593.836 732.305 c 598.461 726.523 l 605.395 727.68 + l 607.707 727.68 610.02 727.68 612.332 727.68 c 642.383 727.68 666.656 +703.41 666.656 673.355 c 666.656 672.199 666.656 671.043 666.656 669.891 + c 666.656 660.641 l 674.746 657.176 l 687.461 651.395 695.551 639.836 695.551 + 625.969 c 695.551 619.031 693.238 610.941 688.617 605.16 c 683.992 599.383 + l h +686.305 593.605 m f +616.953 695.316 m 616.953 684.465 608.156 675.668 597.305 675.668 c 586.453 + 675.668 577.656 684.465 577.656 695.316 c 577.656 706.168 586.453 714.969 + 597.305 714.969 c 608.156 714.969 616.953 706.168 616.953 695.316 c h +616.953 695.316 m f +568.41 726.523 m 568.41 721.418 564.27 717.277 559.16 717.277 c 554.055 + 717.277 549.914 721.418 549.914 726.523 c 549.914 731.633 554.055 735.773 + 559.16 735.773 c 564.27 735.773 568.41 731.633 568.41 726.523 c h +568.41 726.523 m f +562.629 686.07 m 562.629 684.156 561.078 682.602 559.16 682.602 c 557.246 + 682.602 555.695 684.156 555.695 686.07 c 555.695 687.984 557.246 689.539 + 559.16 689.539 c 561.078 689.539 562.629 687.984 562.629 686.07 c h +562.629 686.07 m f +495.59 698.785 m 495.59 696.871 494.039 695.316 492.121 695.316 c 490.207 + 695.316 488.656 696.871 488.656 698.785 c 488.656 700.699 490.207 702.254 + 492.121 702.254 c 494.039 702.254 495.59 700.699 495.59 698.785 c h +495.59 698.785 m f +666.656 607.473 m 666.656 625.969 651.629 639.836 634.293 639.836 c 625.043 + 639.836 615.797 635.215 610.02 629.434 c 604.238 634.059 597.305 636.371 + 590.371 636.371 c 589.215 636.371 589.215 636.371 588.059 636.371 c 578.812 + 664.109 551.07 667.578 537.199 664.109 c 532.578 662.953 527.953 660.641 + 524.488 658.332 c 518.707 664.109 510.617 668.734 501.371 668.734 c 497.902 + 668.734 495.59 668.734 492.121 667.578 c 487.5 679.137 475.941 687.227 +462.07 687.227 c 451.668 687.227 442.422 682.602 435.488 674.512 c 429.707 + 679.137 422.773 681.449 415.836 681.449 c 398.5 681.449 384.629 668.734 + 383.473 652.551 c 381.164 652.551 380.008 652.551 377.695 652.551 c 353.422 + 652.551 333.773 632.902 333.773 608.629 c 333.773 584.355 353.422 564.707 + 377.695 564.707 c 392.719 564.707 405.434 571.645 413.527 583.199 c 423.93 + 558.93 449.355 542.746 478.254 542.746 c 492.121 542.746 504.836 546.215 + 516.395 553.148 c 516.395 550.836 515.238 547.371 515.238 545.059 c 515.238 + 518.473 537.199 497.668 562.629 497.668 c 586.902 497.668 607.707 516.164 + 610.02 540.434 c 616.953 534.656 625.043 531.188 635.445 531.188 c 657.406 + 531.188 675.902 548.527 675.902 571.645 c 675.902 582.047 671.277 591.293 + 665.5 599.383 c 665.5 599.383 666.656 602.852 666.656 607.473 c h +378.852 591.293 m 373.07 591.293 368.449 595.914 368.449 601.695 c 368.449 + 607.473 373.07 612.098 378.852 612.098 c 384.629 612.098 389.254 607.473 + 389.254 601.695 c 388.098 595.914 384.629 591.293 378.852 591.293 c h +457.449 625.969 m 449.355 625.969 442.422 632.902 442.422 640.992 c 442.422 + 649.082 449.355 656.02 457.449 656.02 c 465.539 656.02 472.473 649.082 +472.473 640.992 c 472.473 632.902 465.539 625.969 457.449 625.969 c h +511.773 591.293 m 508.305 591.293 504.836 594.758 504.836 598.227 c 504.836 + 601.695 508.305 605.16 511.773 605.16 c 515.238 605.16 518.707 601.695 +518.707 598.227 c 518.707 594.758 515.238 591.293 511.773 591.293 c h +542.98 573.953 m 541.824 573.953 540.668 575.109 540.668 576.266 c 540.668 + 577.422 541.824 578.578 542.98 578.578 c 544.137 578.578 545.293 577.422 + 545.293 576.266 c 545.293 575.109 544.137 573.953 542.98 573.953 c h +548.758 602.852 m 544.137 602.852 539.512 607.473 539.512 612.098 c 539.512 + 616.719 544.137 621.344 548.758 621.344 c 553.383 621.344 558.004 616.719 + 558.004 612.098 c 558.004 607.473 554.539 602.852 548.758 602.852 c h +601.926 564.707 m 597.305 564.707 594.992 568.176 594.992 571.645 c 594.992 + 575.109 598.461 578.578 601.926 578.578 c 605.395 578.578 608.863 575.109 + 608.863 571.645 c 608.863 568.176 606.551 564.707 601.926 564.707 c h +601.926 564.707 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo.svg new file mode 100644 index 000000000..5611f494a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo.svg @@ -0,0 +1,18 @@ + + + +Created with Fabric.js 2.3.6 + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_background.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_background.eps new file mode 100644 index 000000000..28ba8efc3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_background.eps @@ -0,0 +1,384 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:23 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 106 225 918 763 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Aquatico-Regular +11 dict begin +/FontType 42 def +/FontName /Aquatico-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674200d1406cb00000720000000346670676d9e3611ca0000 +075400000e15676c7966749368660000009c000006846865616411b678940000156c00000036 +68686561084f03d7000015a400000024686d7478128e0148000015c80000001c6c6f6361070a +08ca000015e4000000106d61787001ea0f15000015f400000020707265706846c89c00001614 +000000a7000a005dff4c019a03340003000f001500190023002900350039003d004800ec40e9 +400121014b001618151516720000000702000767060102050103040203670004000a0b040a67 +000b250c0209080b0967000800110d08116700140e0d145710010d000e0f0d0e67000f001213 +0f126700131a01181613186700150017191517680019001c1d191c67001d26011e1b1d1e6700 +1b00231f1b236722011f0021201f2167002001012057002020015f24010120014f3a3a161600 +0048474645444342413f3e3a3d3a3d3c3b393837363534333231302f2e2d2c2b2a2928272625 +24232221201f1e1d1c1b1a1619161918171514131211100f0e0d0c0b0a090807060504000300 +03112706172b1711211103331523153335233533352307333523352317353315073315231533 +3533352317231533352307333533152335231533352303333523173533150733071533352337 +3335235d013df24142a64242a501a6426421214242426442a68585a6214322216421a66442a6 +a62164854646a6664620a6b403e8fc1803842521212521e922464624245e252146217e2264b2 +172f507171fee271502f2f592f21212f210000010028fff002630323004d00f9b51d01010001 +4c4bb0095058402500010004000104800004030004037e0000000261000202174d0003030561 +0601050515054e1b4bb00b5058402500010004000104800004030004037e0000000261000202 +174d0003030561060105050f054e1b4bb00d5058402500010004000104800004030004037e00 +00000261000202174d00030305610601050515054e1b4bb00f50584025000100040001048000 +04030004037e0000000261000202174d0003030561060105050f054e1b402500010004000104 +800004030004037e0000000261000202174d00030305610601050515054e5959595940100000 +004d004c43413b392a282d0707192b04363637363534272e0235343633321617161716161716 +3332363736353427262627262322070615141716171617161617161514070606070623222726 +27262726232206151417161716171633017a73630e05e35e613163441f2e1536210307040a08 +0a12060c030a3b2a4253b44410172358365745600f05080d3a2725201b23652d0d080809121c +021342334d2728102050441c17a437172333293f3a050814250207030509080e1208071c3310 +1a8820353c263b2317120e302e110e1415202808070511371204042013090535231d0d060000 +00010028000002a50312001f001d401a190d05030001014c020101010e4d00000012004e262a +280307192b001615140701111406232226351101263534363736333216171313363633321702 +990c08fefb1d15141efefc080c0a0c100c1607e3e407160c100c0303170c100cfe7afef4141e +1e14010c01860c100c1707080c0afeab01550a0c080000010028000002c80313001b00244021 +180a020200014c010100000e4d040302020212024e0000001b001a2525250507192b24363511 +34262322061511012623220615111416333236351101163302ae1a1e14151dfe1c0e18151d1d +15151d01e30d19011b1502b0151d1d15fddb0244121d15fd52151d1d150224fdbb1000000002 +0023ffff02fd03120022002b002a40272a0104002b29020204024c0004000201040269000000 +0e4d0301010112014e222426282705071b2b2436353427012626232206070106151417163332 +37363736373633321717161633323702232207060607131302ed1004fec9051a0e0f1807fec4 +08110b0d16133f4d474f2930353345071a0e0c08d01c37362a6128c0970b190e0c0902ae0d10 +0f0efd521010160b07133d302c0f0a0d9a0e0f04011b0c0825170199feb40001002800000277 +0312002b002940260001000204010269000000035f0003030e4d0501040412044e0000002b00 +2a3b242a2306071a2b3236351133321617161716151406070623220615141633323736363534 +2726272627262321220615111416336f1df70a2b171e10162021396c141e1e148750383b1412 +222f421f1cfed7141e1e141d15027c090d121c273b2c3c13211d15151d2e206b4740342e222e +10081d15fd52151d000100280000026a03120020002f402c0003000405030467000202015f00 +01010e4d06010505005f00000012004e00000020001f242124353407071b2b24161514062321 +2226351134363321321615140623211521321615140623211521024d1d1d15fe22141e1e1401 +d4151d1d15fe5e0172151d1d15fe8e01ac641d15151d1d1502ae151d1d15151df11e14151df5 +000000000000000000000000000000000000000000640064006400640312fff5031203120000 +fff50312fff40312031e0000fff5b0002c20b0005558455920204bb8000e514bb006535a58b0 +341bb028596066208a5558b0022561b908000800636323621b2121b00059b000432344b20001 +004360422db0012cb02060662db0022c232123212db0032c2064b3031415004243b013432060 +6042b102144342b1250343b00243547820b00c23b00243436164b0045078b2020202436042b0 +21651c21b0024343b20e1501421c20b002432342b213011343604223b00050586559b2160102 +4360422db0042cb0032bb015435823212321b016434323b000505865591b206420b0c050b004 +265ab228010d43456345b006455821b0032559525b582123211b8a5820b050505821b040591b +20b038505821b038595920b1010d434563456164b028505821b1010d4345634520b030505821 +b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036505821 +b036601b605959591bb00225b00c4363b0005258b0004bb00a505821b00c431b4bb01e505821 +b01e4b61b8100063b00c4363b80500625959646159b0012b595923b00050586559592064b016 +432342592db0052c204520b00425616420b007435058b0072342b00823421b212159b001602d +b0062c23212321b0032b2064b107624220b0082342b00645581bb1010d434563b1010d43b001 +604563b0052a2120b00843208a208ab0012bb1300525b00426515860501b6152595823592159 +20b0405358b0012b1b21b0405923b000505865592db0072cb009432bb20002004360422db008 +2cb00923422320b000234261b0026266b00163b00160b0072a2db0092c20204520b00e4363b8 +04006220b0005058b040605966b001636044b001602db00a2cb2090e004345422a21b2000100 +4360422db00b2cb000432344b20001004360422db00c2c20204520b0012b23b00043b0042560 +20458a2361206420b020505821b0001bb0305058b0201bb040595923b00050586559b0032523 +614444b001602db00d2c20204520b0012b23b00043b004256020458a23612064b0245058b000 +1bb0405923b00050586559b0032523614444b001602db00e2c20b0002342b30d0c0003455058 +211b2321592a212db00f2cb1020245b06461442db0102cb001602020b00f434ab000505820b0 +0f234259b010434ab000525820b0102342592db0112c20b0106266b0016320b80400638a2361 +b0114360208a6020b0112342232db0122c4b5458b10464445924b00d6523782db0132c4b5158 +4b5358b1046444591b215924b0136523782db0142cb10012435558b1121243b0016142b0112b +59b00043b0022542b10f022542b110022542b001162320b003255058b101004360b00425428a +8a208a2361b0102a2123b00161208a2361b0102a211bb101004360b0022542b0022561b0102a +2159b00f4347b010434760b0026220b0005058b040605966b0016320b00e4363b804006220b0 +005058b040605966b0016360b10000132344b00143b0003eb20101014360422db0152c00b100 +02455458b01223422045b00e2342b00d23b001604220b01423422060b00161b7181801001100 +13004242428a6020b0144360b0142342b114082bb08b2b1b22592db0162cb100152b2db0172c +b101152b2db0182cb102152b2db0192cb103152b2db01a2cb104152b2db01b2cb105152b2db0 +1c2cb106152b2db01d2cb107152b2db01e2cb108152b2db01f2cb109152b2db02b2c2320b010 +6266b00163b006604b545823202eb0015d1b2121592db02c2c2320b0106266b00163b016604b +545823202eb001711b2121592db02d2c2320b0106266b00163b026604b545823202eb001721b +2121592db0202c00b00f2bb10002455458b01223422045b00e2342b00d23b00160422060b001 +61b518180100110042428a60b114082bb08b2b1b22592db0212cb100202b2db0222cb101202b +2db0232cb102202b2db0242cb103202b2db0252cb104202b2db0262cb105202b2db0272cb106 +202b2db0282cb107202b2db0292cb108202b2db02a2cb109202b2db02e2c203cb001602db02f +2c2060b01860204323b0016043b0022561b00160b02e2a212db0302cb02f2bb02f2a2db0312c +2020472020b00e4363b804006220b0005058b040605966b001636023613823208a5558204720 +20b00e4363b804006220b0005058b040605966b00163602361381b21592db0322c00b1000245 +5458b10e064542b00116b0312ab1050115455830591b22592db0332c00b00f2bb10002455458 +b10e064542b00116b0312ab1050115455830591b22592db0342c2035b001602db0352c00b10e +064542b0014563b804006220b0005058b040605966b00163b0012bb00e4363b804006220b000 +5058b040605966b00163b0012bb00016b40000000000443e2338b13401152a212db0362c203c +204720b00e4363b804006220b0005058b040605966b0016360b0004361382db0372c2e173c2d +b0382c203c204720b00e4363b804006220b0005058b040605966b0016360b0004361b0014363 +382db0392cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b00123 +42b238010115142a2db03a2cb00016b0172342b00425b004254723472361b10c0042b00b432b +658a2e2320203c8a382db03b2cb00016b0172342b00425b00425202e472347236120b0062342 +b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a5942422320b00a4320 +8a234723472361234660b00643b0026220b0005058b040605966b001636020b0012b208a8a61 +20b00443606423b0054361645058b00443611bb005436059b00325b0026220b0005058b04060 +5966b0016361232020b00426234661381b23b00a4346b00225b00a4347234723616020b00643 +b0026220b0005058b040605966b00163602320b0012b23b0064360b0012bb0052561b00525b0 +026220b0005058b040605966b00163b004266120b00425606423b0032560645058211b232159 +232020b0042623466138592db03c2cb00016b0172342202020b00526202e4723472361233c38 +2db03d2cb00016b017234220b00a2342202020462347b0012b2361382db03e2cb00016b01723 +42b00325b002254723472361b00054582e203c23211bb00225b00225472347236120b00525b0 +04254723472361b00625b0052549b0022561b9080008006363232058621b215963b804006220 +b0005058b040605966b0016360232e2320203c8a382321592db03f2cb00016b017234220b00a +43202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a382db0 +402c23202e46b0022546b0174358501b525958203c592eb13001142b2db0412c23202e46b002 +2546b0174358521b505958203c592eb13001142b2db0422c23202e46b0022546b0174358501b +525958203c5923202e46b0022546b0174358521b505958203c592eb13001142b2db0432cb03a +2b23202e46b0022546b0174358501b525958203c592eb13001142b2db0442cb03b2b8a20203c +b00623428a3823202e46b0022546b0174358501b525958203c592eb13001142bb006432eb030 +2b2db0452cb00016b00425b0042620202046234761b00c23422e4723472361b00b432b23203c +202e2338b13001142b2db0462cb10a042542b00016b00425b00425202e472347236120b00623 +42b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a594242232047b006 +43b0026220b0005058b040605966b001636020b0012b208a8a6120b00443606423b005436164 +5058b00443611bb005436059b00325b0026220b0005058b040605966b0016361b00225466138 +23203c23381b212020462347b0012b2361382159b13001142b2db0472cb1003a2b2eb1300114 +2b2db0482cb1003b2b212320203cb00623422338b13001142bb006432eb0302b2db0492cb000 +152047b0002342b20001011514132eb0362a2db04a2cb000152047b0002342b2000101151413 +2eb0362a2db04b2cb100011413b0372a2db04c2cb0392a2db04d2cb000164523202e20468a23 +6138b13001142b2db04e2cb00a2342b04d2b2db04f2cb20000462b2db0502cb20001462b2db0 +512cb20100462b2db0522cb20101462b2db0532cb20000472b2db0542cb20001472b2db0552c +b20100472b2db0562cb20101472b2db0572cb3000000432b2db0582cb3000100432b2db0592c +b3010000432b2db05a2cb3010100432b2db05b2cb3000001432b2db05c2cb3000101432b2db0 +5d2cb3010001432b2db05e2cb3010101432b2db05f2cb20000452b2db0602cb20001452b2db0 +612cb20100452b2db0622cb20101452b2db0632cb20000482b2db0642cb20001482b2db0652c +b20100482b2db0662cb20101482b2db0672cb3000000442b2db0682cb3000100442b2db0692c +b3010000442b2db06a2cb3010100442b2db06b2cb3000001442b2db06c2cb3000101442b2db0 +6d2cb3010001442b2db06e2cb3010101442b2db06f2cb1003c2b2eb13001142b2db0702cb100 +3c2bb0402b2db0712cb1003c2bb0412b2db0722cb00016b1003c2bb0422b2db0732cb1013c2b +b0402b2db0742cb1013c2bb0412b2db0752cb00016b1013c2bb0422b2db0762cb1003d2b2eb1 +3001142b2db0772cb1003d2bb0402b2db0782cb1003d2bb0412b2db0792cb1003d2bb0422b2d +b07a2cb1013d2bb0402b2db07b2cb1013d2bb0412b2db07c2cb1013d2bb0422b2db07d2cb100 +3e2b2eb13001142b2db07e2cb1003e2bb0402b2db07f2cb1003e2bb0412b2db0802cb1003e2b +b0422b2db0812cb1013e2bb0402b2db0822cb1013e2bb0412b2db0832cb1013e2bb0422b2db0 +842cb1003f2b2eb13001142b2db0852cb1003f2bb0402b2db0862cb1003f2bb0412b2db0872c +b1003f2bb0422b2db0882cb1013f2bb0402b2db0892cb1013f2bb0412b2db08a2cb1013f2bb0 +422b2db08b2cb20b0003455058b0061bb2040203455823211b215959422bb00865b003245078 +b1050115455830592d0000000001000000010000817977795f0f3cf5000703e800000000d572 +f60700000000d8cd3ee2000aff4c0451037b0000000700020000000000000001000003fcff4c +000004790000000a045100010000000000000000000000000000000701f4005d028c002802cd +002802f0002803200023029f002802920028000000d801c6020a024a02a602fa034200010000 +00070071000a000000000002004e008d008d000000fd0e1500000000004bb800c85258b10101 +8e59b001b9080008006370b1000742b21701002ab1000742b30c08010a2ab1000742b3140601 +0a2ab1000842ba03400001000b2ab1000942ba00400001000b2ab90003000044b12401885158 +b0408858b90003006444b12801885158b808008858b90003000044591bb12701885158ba0880 +0001044088635458b900030000445959595959b30e06010e2ab801ff85b0048db1020044b305 +64060044440000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 106 225 918 763 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 106 225 812 538 rectclip q +0.54902 0.407843 0.803922 rg +BT +168.077333 0 0 168.077333 100 228.679932 Tm +/f-0-0 1 Tf +(SYNAPSE)Tj +ET +707.109 625.969 m 707.109 645.617 695.551 661.797 678.215 668.734 c 678.215 + 669.891 678.215 671.043 678.215 672.199 c 678.215 709.188 648.16 739.238 + 611.176 739.238 c 607.707 739.238 605.395 739.238 601.926 738.082 c 591.523 + 753.109 574.188 762.355 555.695 762.355 c 542.98 762.355 531.422 758.891 + 522.176 751.953 c 511.773 758.891 499.059 762.355 485.188 762.355 c 463.227 + 762.355 444.734 751.953 432.02 736.93 c 430.863 736.93 429.707 736.93 428.551 + 736.93 c 392.719 736.93 363.824 710.344 359.203 675.668 c 329.148 667.578 + 307.188 640.992 307.188 607.473 c 307.188 569.332 338.395 538.125 376.539 + 538.125 c 388.098 538.125 398.5 540.434 408.902 546.215 c 426.238 527.723 + 450.512 517.316 478.254 517.316 c 484.031 517.316 489.812 517.316 495.59 + 518.473 c 503.68 489.578 530.266 467.617 562.629 467.617 c 584.59 467.617 + 604.238 478.02 616.953 493.047 c 622.734 487.266 634.293 473.395 629.668 + 449.125 c 629.668 449.125 652.785 471.086 642.383 503.449 c 675.902 508.07 + 702.484 536.969 702.484 572.797 c 702.484 580.891 701.328 590.137 697.863 + 597.07 c 703.641 605.16 707.109 615.566 707.109 625.969 c h +686.305 593.605 m 688.617 586.668 689.77 580.891 689.77 573.953 c 689.77 + 542.746 664.344 516.164 631.98 516.164 c 629.668 516.164 627.355 516.164 + 625.043 516.164 c 616.953 517.316 l 612.332 510.383 l 601.926 491.891 583.434 + 480.332 562.629 480.332 c 537.199 480.332 514.082 497.668 507.148 521.941 + c 503.68 532.344 l 492.121 530.031 l 487.5 528.875 481.719 528.875 477.098 + 528.875 c 453.98 528.875 433.176 538.125 416.992 554.305 c 411.215 561.238 + l 403.125 557.773 l 395.031 553.148 385.785 551.992 376.539 551.992 c 345.332 + 551.992 318.746 577.422 318.746 609.785 c 318.746 636.371 336.086 659.488 + 361.512 665.266 c 369.605 667.578 l 370.758 675.668 l 374.227 704.562 398.5 + 725.371 427.395 725.371 c 428.551 725.371 429.707 725.371 430.863 725.371 + c 436.641 725.371 l 440.109 729.992 l 450.512 743.863 466.695 750.797 484.031 + 750.797 c 494.434 750.797 504.836 747.332 514.082 741.551 c 521.02 736.93 + l 530.266 742.707 l 538.355 748.484 546.449 750.797 556.852 750.797 c 571.875 + 750.797 584.59 743.863 593.836 732.305 c 598.461 726.523 l 605.395 727.68 + l 607.707 727.68 610.02 727.68 612.332 727.68 c 642.383 727.68 666.656 +703.41 666.656 673.355 c 666.656 672.199 666.656 671.043 666.656 669.891 + c 666.656 660.641 l 674.746 657.176 l 687.461 651.395 695.551 639.836 695.551 + 625.969 c 695.551 619.031 693.238 610.941 688.617 605.16 c 683.992 599.383 + l h +686.305 593.605 m f +616.953 695.316 m 616.953 684.465 608.156 675.668 597.305 675.668 c 586.453 + 675.668 577.656 684.465 577.656 695.316 c 577.656 706.168 586.453 714.969 + 597.305 714.969 c 608.156 714.969 616.953 706.168 616.953 695.316 c h +616.953 695.316 m f +568.41 726.523 m 568.41 721.418 564.27 717.277 559.16 717.277 c 554.055 + 717.277 549.914 721.418 549.914 726.523 c 549.914 731.633 554.055 735.773 + 559.16 735.773 c 564.27 735.773 568.41 731.633 568.41 726.523 c h +568.41 726.523 m f +562.629 686.07 m 562.629 684.156 561.078 682.602 559.16 682.602 c 557.246 + 682.602 555.695 684.156 555.695 686.07 c 555.695 687.984 557.246 689.539 + 559.16 689.539 c 561.078 689.539 562.629 687.984 562.629 686.07 c h +562.629 686.07 m f +495.59 698.785 m 495.59 696.871 494.039 695.316 492.121 695.316 c 490.207 + 695.316 488.656 696.871 488.656 698.785 c 488.656 700.699 490.207 702.254 + 492.121 702.254 c 494.039 702.254 495.59 700.699 495.59 698.785 c h +495.59 698.785 m f +666.656 607.473 m 666.656 625.969 651.629 639.836 634.293 639.836 c 625.043 + 639.836 615.797 635.215 610.02 629.434 c 604.238 634.059 597.305 636.371 + 590.371 636.371 c 589.215 636.371 589.215 636.371 588.059 636.371 c 578.812 + 664.109 551.07 667.578 537.199 664.109 c 532.578 662.953 527.953 660.641 + 524.488 658.332 c 518.707 664.109 510.617 668.734 501.371 668.734 c 497.902 + 668.734 495.59 668.734 492.121 667.578 c 487.5 679.137 475.941 687.227 +462.07 687.227 c 451.668 687.227 442.422 682.602 435.488 674.512 c 429.707 + 679.137 422.773 681.449 415.836 681.449 c 398.5 681.449 384.629 668.734 + 383.473 652.551 c 381.164 652.551 380.008 652.551 377.695 652.551 c 353.422 + 652.551 333.773 632.902 333.773 608.629 c 333.773 584.355 353.422 564.707 + 377.695 564.707 c 392.719 564.707 405.434 571.645 413.527 583.199 c 423.93 + 558.93 449.355 542.746 478.254 542.746 c 492.121 542.746 504.836 546.215 + 516.395 553.148 c 516.395 550.836 515.238 547.371 515.238 545.059 c 515.238 + 518.473 537.199 497.668 562.629 497.668 c 586.902 497.668 607.707 516.164 + 610.02 540.434 c 616.953 534.656 625.043 531.188 635.445 531.188 c 657.406 + 531.188 675.902 548.527 675.902 571.645 c 675.902 582.047 671.277 591.293 + 665.5 599.383 c 665.5 599.383 666.656 602.852 666.656 607.473 c h +378.852 591.293 m 373.07 591.293 368.449 595.914 368.449 601.695 c 368.449 + 607.473 373.07 612.098 378.852 612.098 c 384.629 612.098 389.254 607.473 + 389.254 601.695 c 388.098 595.914 384.629 591.293 378.852 591.293 c h +457.449 625.969 m 449.355 625.969 442.422 632.902 442.422 640.992 c 442.422 + 649.082 449.355 656.02 457.449 656.02 c 465.539 656.02 472.473 649.082 +472.473 640.992 c 472.473 632.902 465.539 625.969 457.449 625.969 c h +511.773 591.293 m 508.305 591.293 504.836 594.758 504.836 598.227 c 504.836 + 601.695 508.305 605.16 511.773 605.16 c 515.238 605.16 518.707 601.695 +518.707 598.227 c 518.707 594.758 515.238 591.293 511.773 591.293 c h +542.98 573.953 m 541.824 573.953 540.668 575.109 540.668 576.266 c 540.668 + 577.422 541.824 578.578 542.98 578.578 c 544.137 578.578 545.293 577.422 + 545.293 576.266 c 545.293 575.109 544.137 573.953 542.98 573.953 c h +548.758 602.852 m 544.137 602.852 539.512 607.473 539.512 612.098 c 539.512 + 616.719 544.137 621.344 548.758 621.344 c 553.383 621.344 558.004 616.719 + 558.004 612.098 c 558.004 607.473 554.539 602.852 548.758 602.852 c h +601.926 564.707 m 597.305 564.707 594.992 568.176 594.992 571.645 c 594.992 + 575.109 598.461 578.578 601.926 578.578 c 605.395 578.578 608.863 575.109 + 608.863 571.645 c 608.863 568.176 606.551 564.707 601.926 564.707 c h +601.926 564.707 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_background.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_background.svg new file mode 100644 index 000000000..e1fcaa226 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_background.svg @@ -0,0 +1,19 @@ + + + +Created with Fabric.js 2.3.6 + + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_black.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_black.eps new file mode 100644 index 000000000..5da23d1a5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_black.eps @@ -0,0 +1,386 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:23 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Aquatico-Regular +11 dict begin +/FontType 42 def +/FontName /Aquatico-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674200d1406cb00000720000000346670676d9e3611ca0000 +075400000e15676c7966749368660000009c000006846865616411b678940000156c00000036 +68686561084f03d7000015a400000024686d7478128e0148000015c80000001c6c6f6361070a +08ca000015e4000000106d61787001ea0f15000015f400000020707265706846c89c00001614 +000000a7000a005dff4c019a03340003000f001500190023002900350039003d004800ec40e9 +400121014b001618151516720000000702000767060102050103040203670004000a0b040a67 +000b250c0209080b0967000800110d08116700140e0d145710010d000e0f0d0e67000f001213 +0f126700131a01181613186700150017191517680019001c1d191c67001d26011e1b1d1e6700 +1b00231f1b236722011f0021201f2167002001012057002020015f24010120014f3a3a161600 +0048474645444342413f3e3a3d3a3d3c3b393837363534333231302f2e2d2c2b2a2928272625 +24232221201f1e1d1c1b1a1619161918171514131211100f0e0d0c0b0a090807060504000300 +03112706172b1711211103331523153335233533352307333523352317353315073315231533 +3533352317231533352307333533152335231533352303333523173533150733071533352337 +3335235d013df24142a64242a501a6426421214242426442a68585a6214322216421a66442a6 +a62164854646a6664620a6b403e8fc1803842521212521e922464624245e252146217e2264b2 +172f507171fee271502f2f592f21212f210000010028fff002630323004d00f9b51d01010001 +4c4bb0095058402500010004000104800004030004037e0000000261000202174d0003030561 +0601050515054e1b4bb00b5058402500010004000104800004030004037e0000000261000202 +174d0003030561060105050f054e1b4bb00d5058402500010004000104800004030004037e00 +00000261000202174d00030305610601050515054e1b4bb00f50584025000100040001048000 +04030004037e0000000261000202174d0003030561060105050f054e1b402500010004000104 +800004030004037e0000000261000202174d00030305610601050515054e5959595940100000 +004d004c43413b392a282d0707192b04363637363534272e0235343633321617161716161716 +3332363736353427262627262322070615141716171617161617161514070606070623222726 +27262726232206151417161716171633017a73630e05e35e613163441f2e1536210307040a08 +0a12060c030a3b2a4253b44410172358365745600f05080d3a2725201b23652d0d080809121c +021342334d2728102050441c17a437172333293f3a050814250207030509080e1208071c3310 +1a8820353c263b2317120e302e110e1415202808070511371204042013090535231d0d060000 +00010028000002a50312001f001d401a190d05030001014c020101010e4d00000012004e262a +280307192b001615140701111406232226351101263534363736333216171313363633321702 +990c08fefb1d15141efefc080c0a0c100c1607e3e407160c100c0303170c100cfe7afef4141e +1e14010c01860c100c1707080c0afeab01550a0c080000010028000002c80313001b00244021 +180a020200014c010100000e4d040302020212024e0000001b001a2525250507192b24363511 +34262322061511012623220615111416333236351101163302ae1a1e14151dfe1c0e18151d1d +15151d01e30d19011b1502b0151d1d15fddb0244121d15fd52151d1d150224fdbb1000000002 +0023ffff02fd03120022002b002a40272a0104002b29020204024c0004000201040269000000 +0e4d0301010112014e222426282705071b2b2436353427012626232206070106151417163332 +37363736373633321717161633323702232207060607131302ed1004fec9051a0e0f1807fec4 +08110b0d16133f4d474f2930353345071a0e0c08d01c37362a6128c0970b190e0c0902ae0d10 +0f0efd521010160b07133d302c0f0a0d9a0e0f04011b0c0825170199feb40001002800000277 +0312002b002940260001000204010269000000035f0003030e4d0501040412044e0000002b00 +2a3b242a2306071a2b3236351133321617161716151406070623220615141633323736363534 +2726272627262321220615111416336f1df70a2b171e10162021396c141e1e148750383b1412 +222f421f1cfed7141e1e141d15027c090d121c273b2c3c13211d15151d2e206b4740342e222e +10081d15fd52151d000100280000026a03120020002f402c0003000405030467000202015f00 +01010e4d06010505005f00000012004e00000020001f242124353407071b2b24161514062321 +2226351134363321321615140623211521321615140623211521024d1d1d15fe22141e1e1401 +d4151d1d15fe5e0172151d1d15fe8e01ac641d15151d1d1502ae151d1d15151df11e14151df5 +000000000000000000000000000000000000000000640064006400640312fff5031203120000 +fff50312fff40312031e0000fff5b0002c20b0005558455920204bb8000e514bb006535a58b0 +341bb028596066208a5558b0022561b908000800636323621b2121b00059b000432344b20001 +004360422db0012cb02060662db0022c232123212db0032c2064b3031415004243b013432060 +6042b102144342b1250343b00243547820b00c23b00243436164b0045078b2020202436042b0 +21651c21b0024343b20e1501421c20b002432342b213011343604223b00050586559b2160102 +4360422db0042cb0032bb015435823212321b016434323b000505865591b206420b0c050b004 +265ab228010d43456345b006455821b0032559525b582123211b8a5820b050505821b040591b +20b038505821b038595920b1010d434563456164b028505821b1010d4345634520b030505821 +b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036505821 +b036601b605959591bb00225b00c4363b0005258b0004bb00a505821b00c431b4bb01e505821 +b01e4b61b8100063b00c4363b80500625959646159b0012b595923b00050586559592064b016 +432342592db0052c204520b00425616420b007435058b0072342b00823421b212159b001602d +b0062c23212321b0032b2064b107624220b0082342b00645581bb1010d434563b1010d43b001 +604563b0052a2120b00843208a208ab0012bb1300525b00426515860501b6152595823592159 +20b0405358b0012b1b21b0405923b000505865592db0072cb009432bb20002004360422db008 +2cb00923422320b000234261b0026266b00163b00160b0072a2db0092c20204520b00e4363b8 +04006220b0005058b040605966b001636044b001602db00a2cb2090e004345422a21b2000100 +4360422db00b2cb000432344b20001004360422db00c2c20204520b0012b23b00043b0042560 +20458a2361206420b020505821b0001bb0305058b0201bb040595923b00050586559b0032523 +614444b001602db00d2c20204520b0012b23b00043b004256020458a23612064b0245058b000 +1bb0405923b00050586559b0032523614444b001602db00e2c20b0002342b30d0c0003455058 +211b2321592a212db00f2cb1020245b06461442db0102cb001602020b00f434ab000505820b0 +0f234259b010434ab000525820b0102342592db0112c20b0106266b0016320b80400638a2361 +b0114360208a6020b0112342232db0122c4b5458b10464445924b00d6523782db0132c4b5158 +4b5358b1046444591b215924b0136523782db0142cb10012435558b1121243b0016142b0112b +59b00043b0022542b10f022542b110022542b001162320b003255058b101004360b00425428a +8a208a2361b0102a2123b00161208a2361b0102a211bb101004360b0022542b0022561b0102a +2159b00f4347b010434760b0026220b0005058b040605966b0016320b00e4363b804006220b0 +005058b040605966b0016360b10000132344b00143b0003eb20101014360422db0152c00b100 +02455458b01223422045b00e2342b00d23b001604220b01423422060b00161b7181801001100 +13004242428a6020b0144360b0142342b114082bb08b2b1b22592db0162cb100152b2db0172c +b101152b2db0182cb102152b2db0192cb103152b2db01a2cb104152b2db01b2cb105152b2db0 +1c2cb106152b2db01d2cb107152b2db01e2cb108152b2db01f2cb109152b2db02b2c2320b010 +6266b00163b006604b545823202eb0015d1b2121592db02c2c2320b0106266b00163b016604b +545823202eb001711b2121592db02d2c2320b0106266b00163b026604b545823202eb001721b +2121592db0202c00b00f2bb10002455458b01223422045b00e2342b00d23b00160422060b001 +61b518180100110042428a60b114082bb08b2b1b22592db0212cb100202b2db0222cb101202b +2db0232cb102202b2db0242cb103202b2db0252cb104202b2db0262cb105202b2db0272cb106 +202b2db0282cb107202b2db0292cb108202b2db02a2cb109202b2db02e2c203cb001602db02f +2c2060b01860204323b0016043b0022561b00160b02e2a212db0302cb02f2bb02f2a2db0312c +2020472020b00e4363b804006220b0005058b040605966b001636023613823208a5558204720 +20b00e4363b804006220b0005058b040605966b00163602361381b21592db0322c00b1000245 +5458b10e064542b00116b0312ab1050115455830591b22592db0332c00b00f2bb10002455458 +b10e064542b00116b0312ab1050115455830591b22592db0342c2035b001602db0352c00b10e +064542b0014563b804006220b0005058b040605966b00163b0012bb00e4363b804006220b000 +5058b040605966b00163b0012bb00016b40000000000443e2338b13401152a212db0362c203c +204720b00e4363b804006220b0005058b040605966b0016360b0004361382db0372c2e173c2d +b0382c203c204720b00e4363b804006220b0005058b040605966b0016360b0004361b0014363 +382db0392cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b00123 +42b238010115142a2db03a2cb00016b0172342b00425b004254723472361b10c0042b00b432b +658a2e2320203c8a382db03b2cb00016b0172342b00425b00425202e472347236120b0062342 +b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a5942422320b00a4320 +8a234723472361234660b00643b0026220b0005058b040605966b001636020b0012b208a8a61 +20b00443606423b0054361645058b00443611bb005436059b00325b0026220b0005058b04060 +5966b0016361232020b00426234661381b23b00a4346b00225b00a4347234723616020b00643 +b0026220b0005058b040605966b00163602320b0012b23b0064360b0012bb0052561b00525b0 +026220b0005058b040605966b00163b004266120b00425606423b0032560645058211b232159 +232020b0042623466138592db03c2cb00016b0172342202020b00526202e4723472361233c38 +2db03d2cb00016b017234220b00a2342202020462347b0012b2361382db03e2cb00016b01723 +42b00325b002254723472361b00054582e203c23211bb00225b00225472347236120b00525b0 +04254723472361b00625b0052549b0022561b9080008006363232058621b215963b804006220 +b0005058b040605966b0016360232e2320203c8a382321592db03f2cb00016b017234220b00a +43202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a382db0 +402c23202e46b0022546b0174358501b525958203c592eb13001142b2db0412c23202e46b002 +2546b0174358521b505958203c592eb13001142b2db0422c23202e46b0022546b0174358501b +525958203c5923202e46b0022546b0174358521b505958203c592eb13001142b2db0432cb03a +2b23202e46b0022546b0174358501b525958203c592eb13001142b2db0442cb03b2b8a20203c +b00623428a3823202e46b0022546b0174358501b525958203c592eb13001142bb006432eb030 +2b2db0452cb00016b00425b0042620202046234761b00c23422e4723472361b00b432b23203c +202e2338b13001142b2db0462cb10a042542b00016b00425b00425202e472347236120b00623 +42b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a594242232047b006 +43b0026220b0005058b040605966b001636020b0012b208a8a6120b00443606423b005436164 +5058b00443611bb005436059b00325b0026220b0005058b040605966b0016361b00225466138 +23203c23381b212020462347b0012b2361382159b13001142b2db0472cb1003a2b2eb1300114 +2b2db0482cb1003b2b212320203cb00623422338b13001142bb006432eb0302b2db0492cb000 +152047b0002342b20001011514132eb0362a2db04a2cb000152047b0002342b2000101151413 +2eb0362a2db04b2cb100011413b0372a2db04c2cb0392a2db04d2cb000164523202e20468a23 +6138b13001142b2db04e2cb00a2342b04d2b2db04f2cb20000462b2db0502cb20001462b2db0 +512cb20100462b2db0522cb20101462b2db0532cb20000472b2db0542cb20001472b2db0552c +b20100472b2db0562cb20101472b2db0572cb3000000432b2db0582cb3000100432b2db0592c +b3010000432b2db05a2cb3010100432b2db05b2cb3000001432b2db05c2cb3000101432b2db0 +5d2cb3010001432b2db05e2cb3010101432b2db05f2cb20000452b2db0602cb20001452b2db0 +612cb20100452b2db0622cb20101452b2db0632cb20000482b2db0642cb20001482b2db0652c +b20100482b2db0662cb20101482b2db0672cb3000000442b2db0682cb3000100442b2db0692c +b3010000442b2db06a2cb3010100442b2db06b2cb3000001442b2db06c2cb3000101442b2db0 +6d2cb3010001442b2db06e2cb3010101442b2db06f2cb1003c2b2eb13001142b2db0702cb100 +3c2bb0402b2db0712cb1003c2bb0412b2db0722cb00016b1003c2bb0422b2db0732cb1013c2b +b0402b2db0742cb1013c2bb0412b2db0752cb00016b1013c2bb0422b2db0762cb1003d2b2eb1 +3001142b2db0772cb1003d2bb0402b2db0782cb1003d2bb0412b2db0792cb1003d2bb0422b2d +b07a2cb1013d2bb0402b2db07b2cb1013d2bb0412b2db07c2cb1013d2bb0422b2db07d2cb100 +3e2b2eb13001142b2db07e2cb1003e2bb0402b2db07f2cb1003e2bb0412b2db0802cb1003e2b +b0422b2db0812cb1013e2bb0402b2db0822cb1013e2bb0412b2db0832cb1013e2bb0422b2db0 +842cb1003f2b2eb13001142b2db0852cb1003f2bb0402b2db0862cb1003f2bb0412b2db0872c +b1003f2bb0422b2db0882cb1013f2bb0402b2db0892cb1013f2bb0412b2db08a2cb1013f2bb0 +422b2db08b2cb20b0003455058b0061bb2040203455823211b215959422bb00865b003245078 +b1050115455830592d0000000001000000010000817977795f0f3cf5000703e800000000d572 +f60700000000d8cd3ee2000aff4c0451037b0000000700020000000000000001000003fcff4c +000004790000000a045100010000000000000000000000000000000701f4005d028c002802cd +002802f0002803200023029f002802920028000000d801c6020a024a02a602fa034200010000 +00070071000a000000000002004e008d008d000000fd0e1500000000004bb800c85258b10101 +8e59b001b9080008006370b1000742b21701002ab1000742b30c08010a2ab1000742b3140601 +0a2ab1000842ba03400001000b2ab1000942ba00400001000b2ab90003000044b12401885158 +b0408858b90003006444b12801885158b808008858b90003000044591bb12701885158ba0880 +0001044088635458b900030000445959595959b30e06010e2ab801ff85b0048db1020044b305 +64060044440000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0 g +BT +168.077333 0 0 168.077333 100 228.679932 Tm +/f-0-0 1 Tf +(SYNAPSE)Tj +ET +707.109 625.969 m 707.109 645.617 695.551 661.797 678.215 668.734 c 678.215 + 669.891 678.215 671.043 678.215 672.199 c 678.215 709.188 648.16 739.238 + 611.176 739.238 c 607.707 739.238 605.395 739.238 601.926 738.082 c 591.523 + 753.109 574.188 762.355 555.695 762.355 c 542.98 762.355 531.422 758.891 + 522.176 751.953 c 511.773 758.891 499.059 762.355 485.188 762.355 c 463.227 + 762.355 444.734 751.953 432.02 736.93 c 430.863 736.93 429.707 736.93 428.551 + 736.93 c 392.719 736.93 363.824 710.344 359.203 675.668 c 329.148 667.578 + 307.188 640.992 307.188 607.473 c 307.188 569.332 338.395 538.125 376.539 + 538.125 c 388.098 538.125 398.5 540.434 408.902 546.215 c 426.238 527.723 + 450.512 517.316 478.254 517.316 c 484.031 517.316 489.812 517.316 495.59 + 518.473 c 503.68 489.578 530.266 467.617 562.629 467.617 c 584.59 467.617 + 604.238 478.02 616.953 493.047 c 622.734 487.266 634.293 473.395 629.668 + 449.125 c 629.668 449.125 652.785 471.086 642.383 503.449 c 675.902 508.07 + 702.484 536.969 702.484 572.797 c 702.484 580.891 701.328 590.137 697.863 + 597.07 c 703.641 605.16 707.109 615.566 707.109 625.969 c h +686.305 593.605 m 688.617 586.668 689.77 580.891 689.77 573.953 c 689.77 + 542.746 664.344 516.164 631.98 516.164 c 629.668 516.164 627.355 516.164 + 625.043 516.164 c 616.953 517.316 l 612.332 510.383 l 601.926 491.891 583.434 + 480.332 562.629 480.332 c 537.199 480.332 514.082 497.668 507.148 521.941 + c 503.68 532.344 l 492.121 530.031 l 487.5 528.875 481.719 528.875 477.098 + 528.875 c 453.98 528.875 433.176 538.125 416.992 554.305 c 411.215 561.238 + l 403.125 557.773 l 395.031 553.148 385.785 551.992 376.539 551.992 c 345.332 + 551.992 318.746 577.422 318.746 609.785 c 318.746 636.371 336.086 659.488 + 361.512 665.266 c 369.605 667.578 l 370.758 675.668 l 374.227 704.562 398.5 + 725.371 427.395 725.371 c 428.551 725.371 429.707 725.371 430.863 725.371 + c 436.641 725.371 l 440.109 729.992 l 450.512 743.863 466.695 750.797 484.031 + 750.797 c 494.434 750.797 504.836 747.332 514.082 741.551 c 521.02 736.93 + l 530.266 742.707 l 538.355 748.484 546.449 750.797 556.852 750.797 c 571.875 + 750.797 584.59 743.863 593.836 732.305 c 598.461 726.523 l 605.395 727.68 + l 607.707 727.68 610.02 727.68 612.332 727.68 c 642.383 727.68 666.656 +703.41 666.656 673.355 c 666.656 672.199 666.656 671.043 666.656 669.891 + c 666.656 660.641 l 674.746 657.176 l 687.461 651.395 695.551 639.836 695.551 + 625.969 c 695.551 619.031 693.238 610.941 688.617 605.16 c 683.992 599.383 + l h +686.305 593.605 m f +616.953 695.316 m 616.953 684.465 608.156 675.668 597.305 675.668 c 586.453 + 675.668 577.656 684.465 577.656 695.316 c 577.656 706.168 586.453 714.969 + 597.305 714.969 c 608.156 714.969 616.953 706.168 616.953 695.316 c h +616.953 695.316 m f +568.41 726.523 m 568.41 721.418 564.27 717.277 559.16 717.277 c 554.055 + 717.277 549.914 721.418 549.914 726.523 c 549.914 731.633 554.055 735.773 + 559.16 735.773 c 564.27 735.773 568.41 731.633 568.41 726.523 c h +568.41 726.523 m f +562.629 686.07 m 562.629 684.156 561.078 682.602 559.16 682.602 c 557.246 + 682.602 555.695 684.156 555.695 686.07 c 555.695 687.984 557.246 689.539 + 559.16 689.539 c 561.078 689.539 562.629 687.984 562.629 686.07 c h +562.629 686.07 m f +495.59 698.785 m 495.59 696.871 494.039 695.316 492.121 695.316 c 490.207 + 695.316 488.656 696.871 488.656 698.785 c 488.656 700.699 490.207 702.254 + 492.121 702.254 c 494.039 702.254 495.59 700.699 495.59 698.785 c h +495.59 698.785 m f +666.656 607.473 m 666.656 625.969 651.629 639.836 634.293 639.836 c 625.043 + 639.836 615.797 635.215 610.02 629.434 c 604.238 634.059 597.305 636.371 + 590.371 636.371 c 589.215 636.371 589.215 636.371 588.059 636.371 c 578.812 + 664.109 551.07 667.578 537.199 664.109 c 532.578 662.953 527.953 660.641 + 524.488 658.332 c 518.707 664.109 510.617 668.734 501.371 668.734 c 497.902 + 668.734 495.59 668.734 492.121 667.578 c 487.5 679.137 475.941 687.227 +462.07 687.227 c 451.668 687.227 442.422 682.602 435.488 674.512 c 429.707 + 679.137 422.773 681.449 415.836 681.449 c 398.5 681.449 384.629 668.734 + 383.473 652.551 c 381.164 652.551 380.008 652.551 377.695 652.551 c 353.422 + 652.551 333.773 632.902 333.773 608.629 c 333.773 584.355 353.422 564.707 + 377.695 564.707 c 392.719 564.707 405.434 571.645 413.527 583.199 c 423.93 + 558.93 449.355 542.746 478.254 542.746 c 492.121 542.746 504.836 546.215 + 516.395 553.148 c 516.395 550.836 515.238 547.371 515.238 545.059 c 515.238 + 518.473 537.199 497.668 562.629 497.668 c 586.902 497.668 607.707 516.164 + 610.02 540.434 c 616.953 534.656 625.043 531.188 635.445 531.188 c 657.406 + 531.188 675.902 548.527 675.902 571.645 c 675.902 582.047 671.277 591.293 + 665.5 599.383 c 665.5 599.383 666.656 602.852 666.656 607.473 c h +378.852 591.293 m 373.07 591.293 368.449 595.914 368.449 601.695 c 368.449 + 607.473 373.07 612.098 378.852 612.098 c 384.629 612.098 389.254 607.473 + 389.254 601.695 c 388.098 595.914 384.629 591.293 378.852 591.293 c h +457.449 625.969 m 449.355 625.969 442.422 632.902 442.422 640.992 c 442.422 + 649.082 449.355 656.02 457.449 656.02 c 465.539 656.02 472.473 649.082 +472.473 640.992 c 472.473 632.902 465.539 625.969 457.449 625.969 c h +511.773 591.293 m 508.305 591.293 504.836 594.758 504.836 598.227 c 504.836 + 601.695 508.305 605.16 511.773 605.16 c 515.238 605.16 518.707 601.695 +518.707 598.227 c 518.707 594.758 515.238 591.293 511.773 591.293 c h +542.98 573.953 m 541.824 573.953 540.668 575.109 540.668 576.266 c 540.668 + 577.422 541.824 578.578 542.98 578.578 c 544.137 578.578 545.293 577.422 + 545.293 576.266 c 545.293 575.109 544.137 573.953 542.98 573.953 c h +548.758 602.852 m 544.137 602.852 539.512 607.473 539.512 612.098 c 539.512 + 616.719 544.137 621.344 548.758 621.344 c 553.383 621.344 558.004 616.719 + 558.004 612.098 c 558.004 607.473 554.539 602.852 548.758 602.852 c h +601.926 564.707 m 597.305 564.707 594.992 568.176 594.992 571.645 c 594.992 + 575.109 598.461 578.578 601.926 578.578 c 605.395 578.578 608.863 575.109 + 608.863 571.645 c 608.863 568.176 606.551 564.707 601.926 564.707 c h +601.926 564.707 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_black.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_black.svg new file mode 100644 index 000000000..903cbb1ac --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_black.svg @@ -0,0 +1,18 @@ + + + +Created with Fabric.js 2.3.6 + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_white.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_white.eps new file mode 100644 index 000000000..fd5f1fcc9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_white.eps @@ -0,0 +1,386 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:24 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%BeginResource: font Aquatico-Regular +11 dict begin +/FontType 42 def +/FontName /Aquatico-Regular def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 65 /A put +Encoding 69 /E put +Encoding 78 /N put +Encoding 80 /P put +Encoding 83 /S put +Encoding 89 /Y put +/CharStrings 7 dict dup begin +/.notdef 0 def +/S 1 def +/Y 2 def +/N 3 def +/A 4 def +/P 5 def +/E 6 def +end readonly def +/sfnts [ +<000100000009008000030010637674200d1406cb00000720000000346670676d9e3611ca0000 +075400000e15676c7966749368660000009c000006846865616411b678940000156c00000036 +68686561084f03d7000015a400000024686d7478128e0148000015c80000001c6c6f6361070a +08ca000015e4000000106d61787001ea0f15000015f400000020707265706846c89c00001614 +000000a7000a005dff4c019a03340003000f001500190023002900350039003d004800ec40e9 +400121014b001618151516720000000702000767060102050103040203670004000a0b040a67 +000b250c0209080b0967000800110d08116700140e0d145710010d000e0f0d0e67000f001213 +0f126700131a01181613186700150017191517680019001c1d191c67001d26011e1b1d1e6700 +1b00231f1b236722011f0021201f2167002001012057002020015f24010120014f3a3a161600 +0048474645444342413f3e3a3d3a3d3c3b393837363534333231302f2e2d2c2b2a2928272625 +24232221201f1e1d1c1b1a1619161918171514131211100f0e0d0c0b0a090807060504000300 +03112706172b1711211103331523153335233533352307333523352317353315073315231533 +3533352317231533352307333533152335231533352303333523173533150733071533352337 +3335235d013df24142a64242a501a6426421214242426442a68585a6214322216421a66442a6 +a62164854646a6664620a6b403e8fc1803842521212521e922464624245e252146217e2264b2 +172f507171fee271502f2f592f21212f210000010028fff002630323004d00f9b51d01010001 +4c4bb0095058402500010004000104800004030004037e0000000261000202174d0003030561 +0601050515054e1b4bb00b5058402500010004000104800004030004037e0000000261000202 +174d0003030561060105050f054e1b4bb00d5058402500010004000104800004030004037e00 +00000261000202174d00030305610601050515054e1b4bb00f50584025000100040001048000 +04030004037e0000000261000202174d0003030561060105050f054e1b402500010004000104 +800004030004037e0000000261000202174d00030305610601050515054e5959595940100000 +004d004c43413b392a282d0707192b04363637363534272e0235343633321617161716161716 +3332363736353427262627262322070615141716171617161617161514070606070623222726 +27262726232206151417161716171633017a73630e05e35e613163441f2e1536210307040a08 +0a12060c030a3b2a4253b44410172358365745600f05080d3a2725201b23652d0d080809121c +021342334d2728102050441c17a437172333293f3a050814250207030509080e1208071c3310 +1a8820353c263b2317120e302e110e1415202808070511371204042013090535231d0d060000 +00010028000002a50312001f001d401a190d05030001014c020101010e4d00000012004e262a +280307192b001615140701111406232226351101263534363736333216171313363633321702 +990c08fefb1d15141efefc080c0a0c100c1607e3e407160c100c0303170c100cfe7afef4141e +1e14010c01860c100c1707080c0afeab01550a0c080000010028000002c80313001b00244021 +180a020200014c010100000e4d040302020212024e0000001b001a2525250507192b24363511 +34262322061511012623220615111416333236351101163302ae1a1e14151dfe1c0e18151d1d +15151d01e30d19011b1502b0151d1d15fddb0244121d15fd52151d1d150224fdbb1000000002 +0023ffff02fd03120022002b002a40272a0104002b29020204024c0004000201040269000000 +0e4d0301010112014e222426282705071b2b2436353427012626232206070106151417163332 +37363736373633321717161633323702232207060607131302ed1004fec9051a0e0f1807fec4 +08110b0d16133f4d474f2930353345071a0e0c08d01c37362a6128c0970b190e0c0902ae0d10 +0f0efd521010160b07133d302c0f0a0d9a0e0f04011b0c0825170199feb40001002800000277 +0312002b002940260001000204010269000000035f0003030e4d0501040412044e0000002b00 +2a3b242a2306071a2b3236351133321617161716151406070623220615141633323736363534 +2726272627262321220615111416336f1df70a2b171e10162021396c141e1e148750383b1412 +222f421f1cfed7141e1e141d15027c090d121c273b2c3c13211d15151d2e206b4740342e222e +10081d15fd52151d000100280000026a03120020002f402c0003000405030467000202015f00 +01010e4d06010505005f00000012004e00000020001f242124353407071b2b24161514062321 +2226351134363321321615140623211521321615140623211521024d1d1d15fe22141e1e1401 +d4151d1d15fe5e0172151d1d15fe8e01ac641d15151d1d1502ae151d1d15151df11e14151df5 +000000000000000000000000000000000000000000640064006400640312fff5031203120000 +fff50312fff40312031e0000fff5b0002c20b0005558455920204bb8000e514bb006535a58b0 +341bb028596066208a5558b0022561b908000800636323621b2121b00059b000432344b20001 +004360422db0012cb02060662db0022c232123212db0032c2064b3031415004243b013432060 +6042b102144342b1250343b00243547820b00c23b00243436164b0045078b2020202436042b0 +21651c21b0024343b20e1501421c20b002432342b213011343604223b00050586559b2160102 +4360422db0042cb0032bb015435823212321b016434323b000505865591b206420b0c050b004 +265ab228010d43456345b006455821b0032559525b582123211b8a5820b050505821b040591b +20b038505821b038595920b1010d434563456164b028505821b1010d4345634520b030505821 +b030591b20b0c050582066208a8a6120b00a5058601b20b020505821b00a601b20b036505821 +b036601b605959591bb00225b00c4363b0005258b0004bb00a505821b00c431b4bb01e505821 +b01e4b61b8100063b00c4363b80500625959646159b0012b595923b00050586559592064b016 +432342592db0052c204520b00425616420b007435058b0072342b00823421b212159b001602d +b0062c23212321b0032b2064b107624220b0082342b00645581bb1010d434563b1010d43b001 +604563b0052a2120b00843208a208ab0012bb1300525b00426515860501b6152595823592159 +20b0405358b0012b1b21b0405923b000505865592db0072cb009432bb20002004360422db008 +2cb00923422320b000234261b0026266b00163b00160b0072a2db0092c20204520b00e4363b8 +04006220b0005058b040605966b001636044b001602db00a2cb2090e004345422a21b2000100 +4360422db00b2cb000432344b20001004360422db00c2c20204520b0012b23b00043b0042560 +20458a2361206420b020505821b0001bb0305058b0201bb040595923b00050586559b0032523 +614444b001602db00d2c20204520b0012b23b00043b004256020458a23612064b0245058b000 +1bb0405923b00050586559b0032523614444b001602db00e2c20b0002342b30d0c0003455058 +211b2321592a212db00f2cb1020245b06461442db0102cb001602020b00f434ab000505820b0 +0f234259b010434ab000525820b0102342592db0112c20b0106266b0016320b80400638a2361 +b0114360208a6020b0112342232db0122c4b5458b10464445924b00d6523782db0132c4b5158 +4b5358b1046444591b215924b0136523782db0142cb10012435558b1121243b0016142b0112b +59b00043b0022542b10f022542b110022542b001162320b003255058b101004360b00425428a +8a208a2361b0102a2123b00161208a2361b0102a211bb101004360b0022542b0022561b0102a +2159b00f4347b010434760b0026220b0005058b040605966b0016320b00e4363b804006220b0 +005058b040605966b0016360b10000132344b00143b0003eb20101014360422db0152c00b100 +02455458b01223422045b00e2342b00d23b001604220b01423422060b00161b7181801001100 +13004242428a6020b0144360b0142342b114082bb08b2b1b22592db0162cb100152b2db0172c +b101152b2db0182cb102152b2db0192cb103152b2db01a2cb104152b2db01b2cb105152b2db0 +1c2cb106152b2db01d2cb107152b2db01e2cb108152b2db01f2cb109152b2db02b2c2320b010 +6266b00163b006604b545823202eb0015d1b2121592db02c2c2320b0106266b00163b016604b +545823202eb001711b2121592db02d2c2320b0106266b00163b026604b545823202eb001721b +2121592db0202c00b00f2bb10002455458b01223422045b00e2342b00d23b00160422060b001 +61b518180100110042428a60b114082bb08b2b1b22592db0212cb100202b2db0222cb101202b +2db0232cb102202b2db0242cb103202b2db0252cb104202b2db0262cb105202b2db0272cb106 +202b2db0282cb107202b2db0292cb108202b2db02a2cb109202b2db02e2c203cb001602db02f +2c2060b01860204323b0016043b0022561b00160b02e2a212db0302cb02f2bb02f2a2db0312c +2020472020b00e4363b804006220b0005058b040605966b001636023613823208a5558204720 +20b00e4363b804006220b0005058b040605966b00163602361381b21592db0322c00b1000245 +5458b10e064542b00116b0312ab1050115455830591b22592db0332c00b00f2bb10002455458 +b10e064542b00116b0312ab1050115455830591b22592db0342c2035b001602db0352c00b10e +064542b0014563b804006220b0005058b040605966b00163b0012bb00e4363b804006220b000 +5058b040605966b00163b0012bb00016b40000000000443e2338b13401152a212db0362c203c +204720b00e4363b804006220b0005058b040605966b0016360b0004361382db0372c2e173c2d +b0382c203c204720b00e4363b804006220b0005058b040605966b0016360b0004361b0014363 +382db0392cb102001625202e2047b0002342b00225498a8a47234723612058621b2159b00123 +42b238010115142a2db03a2cb00016b0172342b00425b004254723472361b10c0042b00b432b +658a2e2320203c8a382db03b2cb00016b0172342b00425b00425202e472347236120b0062342 +b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a5942422320b00a4320 +8a234723472361234660b00643b0026220b0005058b040605966b001636020b0012b208a8a61 +20b00443606423b0054361645058b00443611bb005436059b00325b0026220b0005058b04060 +5966b0016361232020b00426234661381b23b00a4346b00225b00a4347234723616020b00643 +b0026220b0005058b040605966b00163602320b0012b23b0064360b0012bb0052561b00525b0 +026220b0005058b040605966b00163b004266120b00425606423b0032560645058211b232159 +232020b0042623466138592db03c2cb00016b0172342202020b00526202e4723472361233c38 +2db03d2cb00016b017234220b00a2342202020462347b0012b2361382db03e2cb00016b01723 +42b00325b002254723472361b00054582e203c23211bb00225b00225472347236120b00525b0 +04254723472361b00625b0052549b0022561b9080008006363232058621b215963b804006220 +b0005058b040605966b0016360232e2320203c8a382321592db03f2cb00016b017234220b00a +43202e47234723612060b0206066b0026220b0005058b040605966b001632320203c8a382db0 +402c23202e46b0022546b0174358501b525958203c592eb13001142b2db0412c23202e46b002 +2546b0174358521b505958203c592eb13001142b2db0422c23202e46b0022546b0174358501b +525958203c5923202e46b0022546b0174358521b505958203c592eb13001142b2db0432cb03a +2b23202e46b0022546b0174358501b525958203c592eb13001142b2db0442cb03b2b8a20203c +b00623428a3823202e46b0022546b0174358501b525958203c592eb13001142bb006432eb030 +2b2db0452cb00016b00425b0042620202046234761b00c23422e4723472361b00b432b23203c +202e2338b13001142b2db0462cb10a042542b00016b00425b00425202e472347236120b00623 +42b10c0042b00b432b20b060505820b0405158b3042005201bb30426051a594242232047b006 +43b0026220b0005058b040605966b001636020b0012b208a8a6120b00443606423b005436164 +5058b00443611bb005436059b00325b0026220b0005058b040605966b0016361b00225466138 +23203c23381b212020462347b0012b2361382159b13001142b2db0472cb1003a2b2eb1300114 +2b2db0482cb1003b2b212320203cb00623422338b13001142bb006432eb0302b2db0492cb000 +152047b0002342b20001011514132eb0362a2db04a2cb000152047b0002342b2000101151413 +2eb0362a2db04b2cb100011413b0372a2db04c2cb0392a2db04d2cb000164523202e20468a23 +6138b13001142b2db04e2cb00a2342b04d2b2db04f2cb20000462b2db0502cb20001462b2db0 +512cb20100462b2db0522cb20101462b2db0532cb20000472b2db0542cb20001472b2db0552c +b20100472b2db0562cb20101472b2db0572cb3000000432b2db0582cb3000100432b2db0592c +b3010000432b2db05a2cb3010100432b2db05b2cb3000001432b2db05c2cb3000101432b2db0 +5d2cb3010001432b2db05e2cb3010101432b2db05f2cb20000452b2db0602cb20001452b2db0 +612cb20100452b2db0622cb20101452b2db0632cb20000482b2db0642cb20001482b2db0652c +b20100482b2db0662cb20101482b2db0672cb3000000442b2db0682cb3000100442b2db0692c +b3010000442b2db06a2cb3010100442b2db06b2cb3000001442b2db06c2cb3000101442b2db0 +6d2cb3010001442b2db06e2cb3010101442b2db06f2cb1003c2b2eb13001142b2db0702cb100 +3c2bb0402b2db0712cb1003c2bb0412b2db0722cb00016b1003c2bb0422b2db0732cb1013c2b +b0402b2db0742cb1013c2bb0412b2db0752cb00016b1013c2bb0422b2db0762cb1003d2b2eb1 +3001142b2db0772cb1003d2bb0402b2db0782cb1003d2bb0412b2db0792cb1003d2bb0422b2d +b07a2cb1013d2bb0402b2db07b2cb1013d2bb0412b2db07c2cb1013d2bb0422b2db07d2cb100 +3e2b2eb13001142b2db07e2cb1003e2bb0402b2db07f2cb1003e2bb0412b2db0802cb1003e2b +b0422b2db0812cb1013e2bb0402b2db0822cb1013e2bb0412b2db0832cb1013e2bb0422b2db0 +842cb1003f2b2eb13001142b2db0852cb1003f2bb0402b2db0862cb1003f2bb0412b2db0872c +b1003f2bb0422b2db0882cb1013f2bb0402b2db0892cb1013f2bb0412b2db08a2cb1013f2bb0 +422b2db08b2cb20b0003455058b0061bb2040203455823211b215959422bb00865b003245078 +b1050115455830592d0000000001000000010000817977795f0f3cf5000703e800000000d572 +f60700000000d8cd3ee2000aff4c0451037b0000000700020000000000000001000003fcff4c +000004790000000a045100010000000000000000000000000000000701f4005d028c002802cd +002802f0002803200023029f002802920028000000d801c6020a024a02a602fa034200010000 +00070071000a000000000002004e008d008d000000fd0e1500000000004bb800c85258b10101 +8e59b001b9080008006370b1000742b21701002ab1000742b30c08010a2ab1000742b3140601 +0a2ab1000842ba03400001000b2ab1000942ba00400001000b2ab90003000044b12401885158 +b0408858b90003006444b12801885158b808008858b90003000044591bb12701885158ba0880 +0001044088635458b900030000445959595959b30e06010e2ab801ff85b0048db1020044b305 +64060044440000> +] def +/f-0-0 currentdict end definefont pop +%%EndResource +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +0 g +0 0 1024 1024 rectfill +1 g +BT +168.077333 0 0 168.077333 100 228.679932 Tm +/f-0-0 1 Tf +(SYNAPSE)Tj +ET +707.109 625.969 m 707.109 645.617 695.551 661.797 678.215 668.734 c 678.215 + 669.891 678.215 671.043 678.215 672.199 c 678.215 709.188 648.16 739.238 + 611.176 739.238 c 607.707 739.238 605.395 739.238 601.926 738.082 c 591.523 + 753.109 574.188 762.355 555.695 762.355 c 542.98 762.355 531.422 758.891 + 522.176 751.953 c 511.773 758.891 499.059 762.355 485.188 762.355 c 463.227 + 762.355 444.734 751.953 432.02 736.93 c 430.863 736.93 429.707 736.93 428.551 + 736.93 c 392.719 736.93 363.824 710.344 359.203 675.668 c 329.148 667.578 + 307.188 640.992 307.188 607.473 c 307.188 569.332 338.395 538.125 376.539 + 538.125 c 388.098 538.125 398.5 540.434 408.902 546.215 c 426.238 527.723 + 450.512 517.316 478.254 517.316 c 484.031 517.316 489.812 517.316 495.59 + 518.473 c 503.68 489.578 530.266 467.617 562.629 467.617 c 584.59 467.617 + 604.238 478.02 616.953 493.047 c 622.734 487.266 634.293 473.395 629.668 + 449.125 c 629.668 449.125 652.785 471.086 642.383 503.449 c 675.902 508.07 + 702.484 536.969 702.484 572.797 c 702.484 580.891 701.328 590.137 697.863 + 597.07 c 703.641 605.16 707.109 615.566 707.109 625.969 c h +686.305 593.605 m 688.617 586.668 689.77 580.891 689.77 573.953 c 689.77 + 542.746 664.344 516.164 631.98 516.164 c 629.668 516.164 627.355 516.164 + 625.043 516.164 c 616.953 517.316 l 612.332 510.383 l 601.926 491.891 583.434 + 480.332 562.629 480.332 c 537.199 480.332 514.082 497.668 507.148 521.941 + c 503.68 532.344 l 492.121 530.031 l 487.5 528.875 481.719 528.875 477.098 + 528.875 c 453.98 528.875 433.176 538.125 416.992 554.305 c 411.215 561.238 + l 403.125 557.773 l 395.031 553.148 385.785 551.992 376.539 551.992 c 345.332 + 551.992 318.746 577.422 318.746 609.785 c 318.746 636.371 336.086 659.488 + 361.512 665.266 c 369.605 667.578 l 370.758 675.668 l 374.227 704.562 398.5 + 725.371 427.395 725.371 c 428.551 725.371 429.707 725.371 430.863 725.371 + c 436.641 725.371 l 440.109 729.992 l 450.512 743.863 466.695 750.797 484.031 + 750.797 c 494.434 750.797 504.836 747.332 514.082 741.551 c 521.02 736.93 + l 530.266 742.707 l 538.355 748.484 546.449 750.797 556.852 750.797 c 571.875 + 750.797 584.59 743.863 593.836 732.305 c 598.461 726.523 l 605.395 727.68 + l 607.707 727.68 610.02 727.68 612.332 727.68 c 642.383 727.68 666.656 +703.41 666.656 673.355 c 666.656 672.199 666.656 671.043 666.656 669.891 + c 666.656 660.641 l 674.746 657.176 l 687.461 651.395 695.551 639.836 695.551 + 625.969 c 695.551 619.031 693.238 610.941 688.617 605.16 c 683.992 599.383 + l h +686.305 593.605 m f +616.953 695.316 m 616.953 684.465 608.156 675.668 597.305 675.668 c 586.453 + 675.668 577.656 684.465 577.656 695.316 c 577.656 706.168 586.453 714.969 + 597.305 714.969 c 608.156 714.969 616.953 706.168 616.953 695.316 c h +616.953 695.316 m f +568.41 726.523 m 568.41 721.418 564.27 717.277 559.16 717.277 c 554.055 + 717.277 549.914 721.418 549.914 726.523 c 549.914 731.633 554.055 735.773 + 559.16 735.773 c 564.27 735.773 568.41 731.633 568.41 726.523 c h +568.41 726.523 m f +562.629 686.07 m 562.629 684.156 561.078 682.602 559.16 682.602 c 557.246 + 682.602 555.695 684.156 555.695 686.07 c 555.695 687.984 557.246 689.539 + 559.16 689.539 c 561.078 689.539 562.629 687.984 562.629 686.07 c h +562.629 686.07 m f +495.59 698.785 m 495.59 696.871 494.039 695.316 492.121 695.316 c 490.207 + 695.316 488.656 696.871 488.656 698.785 c 488.656 700.699 490.207 702.254 + 492.121 702.254 c 494.039 702.254 495.59 700.699 495.59 698.785 c h +495.59 698.785 m f +666.656 607.473 m 666.656 625.969 651.629 639.836 634.293 639.836 c 625.043 + 639.836 615.797 635.215 610.02 629.434 c 604.238 634.059 597.305 636.371 + 590.371 636.371 c 589.215 636.371 589.215 636.371 588.059 636.371 c 578.812 + 664.109 551.07 667.578 537.199 664.109 c 532.578 662.953 527.953 660.641 + 524.488 658.332 c 518.707 664.109 510.617 668.734 501.371 668.734 c 497.902 + 668.734 495.59 668.734 492.121 667.578 c 487.5 679.137 475.941 687.227 +462.07 687.227 c 451.668 687.227 442.422 682.602 435.488 674.512 c 429.707 + 679.137 422.773 681.449 415.836 681.449 c 398.5 681.449 384.629 668.734 + 383.473 652.551 c 381.164 652.551 380.008 652.551 377.695 652.551 c 353.422 + 652.551 333.773 632.902 333.773 608.629 c 333.773 584.355 353.422 564.707 + 377.695 564.707 c 392.719 564.707 405.434 571.645 413.527 583.199 c 423.93 + 558.93 449.355 542.746 478.254 542.746 c 492.121 542.746 504.836 546.215 + 516.395 553.148 c 516.395 550.836 515.238 547.371 515.238 545.059 c 515.238 + 518.473 537.199 497.668 562.629 497.668 c 586.902 497.668 607.707 516.164 + 610.02 540.434 c 616.953 534.656 625.043 531.188 635.445 531.188 c 657.406 + 531.188 675.902 548.527 675.902 571.645 c 675.902 582.047 671.277 591.293 + 665.5 599.383 c 665.5 599.383 666.656 602.852 666.656 607.473 c h +378.852 591.293 m 373.07 591.293 368.449 595.914 368.449 601.695 c 368.449 + 607.473 373.07 612.098 378.852 612.098 c 384.629 612.098 389.254 607.473 + 389.254 601.695 c 388.098 595.914 384.629 591.293 378.852 591.293 c h +457.449 625.969 m 449.355 625.969 442.422 632.902 442.422 640.992 c 442.422 + 649.082 449.355 656.02 457.449 656.02 c 465.539 656.02 472.473 649.082 +472.473 640.992 c 472.473 632.902 465.539 625.969 457.449 625.969 c h +511.773 591.293 m 508.305 591.293 504.836 594.758 504.836 598.227 c 504.836 + 601.695 508.305 605.16 511.773 605.16 c 515.238 605.16 518.707 601.695 +518.707 598.227 c 518.707 594.758 515.238 591.293 511.773 591.293 c h +542.98 573.953 m 541.824 573.953 540.668 575.109 540.668 576.266 c 540.668 + 577.422 541.824 578.578 542.98 578.578 c 544.137 578.578 545.293 577.422 + 545.293 576.266 c 545.293 575.109 544.137 573.953 542.98 573.953 c h +548.758 602.852 m 544.137 602.852 539.512 607.473 539.512 612.098 c 539.512 + 616.719 544.137 621.344 548.758 621.344 c 553.383 621.344 558.004 616.719 + 558.004 612.098 c 558.004 607.473 554.539 602.852 548.758 602.852 c h +601.926 564.707 m 597.305 564.707 594.992 568.176 594.992 571.645 c 594.992 + 575.109 598.461 578.578 601.926 578.578 c 605.395 578.578 608.863 575.109 + 608.863 571.645 c 608.863 568.176 606.551 564.707 601.926 564.707 c h +601.926 564.707 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_white.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_white.svg new file mode 100644 index 000000000..c6927f018 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logo_white.svg @@ -0,0 +1,18 @@ + + + +Created with Fabric.js 2.3.6 + + + + SYNAPSE + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark.eps new file mode 100644 index 000000000..6c81b8701 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark.eps @@ -0,0 +1,198 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:25 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0.54902 0.407843 0.803922 rg +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark.svg new file mode 100644 index 000000000..b9cfb2256 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark.svg @@ -0,0 +1,15 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_background.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_background.eps new file mode 100644 index 000000000..8193de312 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_background.eps @@ -0,0 +1,196 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:25 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 155 231 869 791 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 155 231 869 791 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 155 231 714 560 rectclip q +0.54902 0.407843 0.803922 rg +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_background.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_background.svg new file mode 100644 index 000000000..9be5b1f16 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_background.svg @@ -0,0 +1,16 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_black.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_black.eps new file mode 100644 index 000000000..327d4f657 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_black.eps @@ -0,0 +1,198 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:26 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +1 g +0 0 1024 1024 rectfill +0 g +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_black.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_black.svg new file mode 100644 index 000000000..95018e132 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_black.svg @@ -0,0 +1,15 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_white.eps b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_white.eps new file mode 100644 index 000000000..71e6913e3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_white.eps @@ -0,0 +1,198 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.14.8 (http://cairographics.org) +%%CreationDate: Tue Jun 4 13:28:27 2024 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 361x361mm 1024 1024 0 () () +%%BoundingBox: 0 0 1024 1024 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 361x361mm +%%PageBoundingBox: 0 0 1024 1024 +1024 1024 cairo_set_page_size +%%EndPageSetup +q 0 0 1024 1024 rectclip q +0 g +0 0 1024 1024 rectfill +1 g +868.379 547.02 m 868.379 582.039 847.781 610.879 816.879 623.238 c 816.879 + 625.301 816.879 627.359 816.879 629.422 c 816.879 695.34 763.32 748.898 + 697.398 748.898 c 691.219 748.898 687.102 748.898 680.922 746.84 c 662.379 + 773.621 631.48 790.102 598.52 790.102 c 575.859 790.102 555.262 783.922 + 538.781 771.559 c 520.238 783.922 497.578 790.102 472.859 790.102 c 433.719 + 790.102 400.762 771.559 378.102 744.781 c 376.039 744.781 373.98 744.781 + 371.922 744.781 c 308.059 744.781 256.559 697.398 248.32 635.602 c 194.762 + 621.18 155.621 573.801 155.621 514.059 c 155.621 446.078 211.238 390.461 + 279.219 390.461 c 299.82 390.461 318.359 394.578 336.898 404.879 c 367.801 + 371.922 411.059 353.379 460.5 353.379 c 470.801 353.379 481.102 353.379 + 491.398 355.441 c 505.82 303.941 553.199 264.801 610.879 264.801 c 650.02 + 264.801 685.039 283.34 707.699 310.121 c 718 299.82 738.602 275.102 730.359 + 231.84 c 730.359 231.84 771.559 270.98 753.02 328.66 c 812.762 336.898 +860.141 388.398 860.141 452.262 c 860.141 466.68 858.078 483.16 851.898 +495.52 c 862.199 509.941 868.379 528.48 868.379 547.02 c h +831.301 489.34 m 835.422 476.98 837.48 466.68 837.48 454.32 c 837.48 398.699 + 792.16 351.32 734.48 351.32 c 730.359 351.32 726.238 351.32 722.121 351.32 + c 707.699 353.379 l 699.461 341.02 l 680.922 308.059 647.961 287.461 610.879 + 287.461 c 565.559 287.461 524.359 318.359 512 361.621 c 505.82 380.16 l + 485.219 376.039 l 476.98 373.98 466.68 373.98 458.441 373.98 c 417.238 +373.98 380.16 390.461 351.32 419.301 c 341.02 431.66 l 326.602 425.48 l +312.18 417.238 295.699 415.18 279.219 415.18 c 223.602 415.18 176.219 460.5 + 176.219 518.18 c 176.219 565.559 207.121 606.762 252.441 617.059 c 266.859 + 621.18 l 268.922 635.602 l 275.102 687.102 318.359 724.18 369.859 724.18 + c 371.922 724.18 373.98 724.18 376.039 724.18 c 386.34 724.18 l 392.52 +732.422 l 411.059 757.141 439.898 769.5 470.801 769.5 c 489.34 769.5 507.879 + 763.32 524.359 753.02 c 536.719 744.781 l 553.199 755.078 l 567.621 765.379 + 582.039 769.5 600.578 769.5 c 627.359 769.5 650.02 757.141 666.5 736.539 + c 674.738 726.238 l 687.102 728.301 l 691.219 728.301 695.34 728.301 699.461 + 728.301 c 753.02 728.301 796.281 685.039 796.281 631.48 c 796.281 629.422 + 796.281 627.359 796.281 625.301 c 796.281 608.82 l 810.699 602.641 l 833.359 + 592.34 847.781 571.738 847.781 547.02 c 847.781 534.66 843.66 520.238 835.422 + 509.941 c 827.18 499.641 l h +831.301 489.34 m f +707.699 670.621 m 707.699 651.277 692.02 635.602 672.68 635.602 c 653.34 + 635.602 637.66 651.277 637.66 670.621 c 637.66 689.961 653.34 705.641 672.68 + 705.641 c 692.02 705.641 707.699 689.961 707.699 670.621 c h +707.699 670.621 m f +621.18 726.238 m 621.18 717.137 613.801 709.762 604.699 709.762 c 595.598 + 709.762 588.219 717.137 588.219 726.238 c 588.219 735.34 595.598 742.719 + 604.699 742.719 c 613.801 742.719 621.18 735.34 621.18 726.238 c h +621.18 726.238 m f +610.879 654.141 m 610.879 650.727 608.113 647.961 604.699 647.961 c 601.285 + 647.961 598.52 650.727 598.52 654.141 c 598.52 657.555 601.285 660.32 604.699 + 660.32 c 608.113 660.32 610.879 657.555 610.879 654.141 c h +610.879 654.141 m f +491.398 676.801 m 491.398 673.387 488.633 670.621 485.219 670.621 c 481.809 + 670.621 479.039 673.387 479.039 676.801 c 479.039 680.215 481.809 682.98 + 485.219 682.98 c 488.633 682.98 491.398 680.215 491.398 676.801 c h +491.398 676.801 m f +796.281 514.059 m 796.281 547.02 769.5 571.738 738.602 571.738 c 722.121 + 571.738 705.641 563.5 695.34 553.199 c 685.039 561.441 672.68 565.559 660.32 + 565.559 c 658.262 565.559 658.262 565.559 656.199 565.559 c 639.719 615 + 590.281 621.18 565.559 615 c 557.32 612.941 549.078 608.82 542.898 604.699 + c 532.602 615 518.18 623.238 501.699 623.238 c 495.52 623.238 491.398 623.238 + 485.219 621.18 c 476.98 641.781 456.379 656.199 431.66 656.199 c 413.121 + 656.199 396.641 647.961 384.281 633.539 c 373.98 641.781 361.621 645.898 + 349.262 645.898 c 318.359 645.898 293.641 623.238 291.578 594.398 c 287.461 + 594.398 285.398 594.398 281.281 594.398 c 238.02 594.398 203 559.379 203 + 516.121 c 203 472.859 238.02 437.84 281.281 437.84 c 308.059 437.84 330.719 + 450.199 345.141 470.801 c 363.68 427.539 409 398.699 460.5 398.699 c 485.219 + 398.699 507.879 404.879 528.48 417.238 c 528.48 413.121 526.422 406.941 + 526.422 402.82 c 526.422 355.441 565.559 318.359 610.879 318.359 c 654.141 + 318.359 691.219 351.32 695.34 394.578 c 707.699 384.281 722.121 378.102 + 740.66 378.102 c 779.801 378.102 812.762 409 812.762 450.199 c 812.762 +468.738 804.52 485.219 794.219 499.641 c 794.219 499.641 796.281 505.82 +796.281 514.059 c h +283.34 485.219 m 273.039 485.219 264.801 493.461 264.801 503.762 c 264.801 + 514.059 273.039 522.301 283.34 522.301 c 293.641 522.301 301.879 514.059 + 301.879 503.762 c 299.82 493.461 293.641 485.219 283.34 485.219 c h +423.422 547.02 m 409 547.02 396.641 559.379 396.641 573.801 c 396.641 588.219 + 409 600.578 423.422 600.578 c 437.84 600.578 450.199 588.219 450.199 573.801 + c 450.199 559.379 437.84 547.02 423.422 547.02 c h +520.238 485.219 m 514.059 485.219 507.879 491.398 507.879 497.578 c 507.879 + 503.762 514.059 509.941 520.238 509.941 c 526.422 509.941 532.602 503.762 + 532.602 497.578 c 532.602 491.398 526.422 485.219 520.238 485.219 c h +575.859 454.32 m 573.801 454.32 571.738 456.379 571.738 458.441 c 571.738 + 460.5 573.801 462.559 575.859 462.559 c 577.922 462.559 579.98 460.5 579.98 + 458.441 c 579.98 456.379 577.922 454.32 575.859 454.32 c h +586.16 505.82 m 577.922 505.82 569.68 514.059 569.68 522.301 c 569.68 530.539 + 577.922 538.781 586.16 538.781 c 594.398 538.781 602.641 530.539 602.641 + 522.301 c 602.641 514.059 596.461 505.82 586.16 505.82 c h +680.922 437.84 m 672.68 437.84 668.559 444.02 668.559 450.199 c 668.559 + 456.379 674.738 462.559 680.922 462.559 c 687.102 462.559 693.281 456.379 + 693.281 450.199 c 693.281 444.02 689.16 437.84 680.922 437.84 c h +680.922 437.84 m f +Q Q +showpage +%%Trailer +%%EOF diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_white.svg b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_white.svg new file mode 100644 index 000000000..314461f52 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/img/logomark_white.svg @@ -0,0 +1,15 @@ + + + +Created with Fabric.js 2.3.6 + + + + + + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/serverlessworkflow-icon-color.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/serverlessworkflow-icon-color.png deleted file mode 100644 index 3a8c828d9..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/img/serverlessworkflow-icon-color.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-black.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-black.png deleted file mode 100644 index 8223a90c2..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-black.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-color.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-color.png deleted file mode 100644 index 037fd3236..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-color.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-white.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-white.png deleted file mode 100644 index 45d680312..000000000 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/img/synapse-logo-white.png and /dev/null differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo.png new file mode 100644 index 000000000..7af7cfaaf Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo_black.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo_black.png new file mode 100644 index 000000000..16f9dde80 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo_black.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo_white.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo_white.png new file mode 100644 index 000000000..f974fac9e Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logo_white.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark.png new file mode 100644 index 000000000..b526f8e61 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark_black.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark_black.png new file mode 100644 index 000000000..14de36832 Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark_black.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark_white.png b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark_white.png new file mode 100644 index 000000000..ff56e4f8d Binary files /dev/null and b/src/dashboard/Synapse.Dashboard/wwwroot/img/transparent_logomark_white.png differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/index.html b/src/dashboard/Synapse.Dashboard/wwwroot/index.html index 99e4b4028..32ab178f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/index.html +++ b/src/dashboard/Synapse.Dashboard/wwwroot/index.html @@ -1,4 +1,5 @@ + - + Synapse + + + - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - + + + + + + + + - - + + + - - + + + - - - + + + - - + + + + + + + - - + + + + + + - - - - + + + + + + + - - - + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
-
- - - - - - - - - - -
- Loading... +
+
+
+ + + + +
+
-
- - - - - - - - - - -
-
An unhandled error has occurred. Reload 🗙
- - - - - - - - - + + + + + - - - - + + diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/chart.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/chart.js deleted file mode 100644 index a8e8d7aca..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/js/chart.js +++ /dev/null @@ -1 +0,0 @@ -export const createChart = (el, config) => new Chart(el, config); \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/file-download.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/file-download.js deleted file mode 100644 index e7babd776..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/js/file-download.js +++ /dev/null @@ -1,17 +0,0 @@ -async function downloadFileFromStream(fileName, contentStreamReference) { - const arrayBuffer = await contentStreamReference.arrayBuffer(); - const blob = new Blob([arrayBuffer]); - const url = URL.createObjectURL(blob); - - triggerFileDownload(fileName, url); - - URL.revokeObjectURL(url); -} - -function triggerFileDownload(fileName, url) { - const anchorElement = document.createElement('a'); - anchorElement.href = url; - anchorElement.download = fileName ?? ''; - anchorElement.click(); - anchorElement.remove(); -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/js-interop.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/js-interop.js new file mode 100644 index 000000000..e095e9715 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/js/js-interop.js @@ -0,0 +1,33 @@ +/** + * Sets a checkbox tri-state + * @param {any} checkboxEl The validation schema to add + * @param {any} state 0 = unchecked, 1 = checked, -1 = indeterminate + * @returns + */ +export function setCheckboxState(checkboxEl, state) { + if (!checkboxEl) return; + switch (state) { + case 1: + checkboxEl.checked = true; + checkboxEl.indeterminate = false; + break; + case -1: + checkboxEl.checked = false; + checkboxEl.indeterminate = true; + break; + default: + checkboxEl.checked = false; + checkboxEl.indeterminate = false; + break; + } +} + +/** + * Scrolls down the provided element + * @param {any} el The element to scroll + * @param {int} height The height to scroll, or the total scroll if not provided + */ +export function scrollDown(el, height) { + if (!el) return; + el.scrollTop = height || el.scrollHeight; +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/monaco-editor-extensions.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/monaco-editor-extensions.js deleted file mode 100644 index fb3b6c7d8..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/js/monaco-editor-extensions.js +++ /dev/null @@ -1,15 +0,0 @@ -function enableJsonValidation08(modelUri) { - monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ - validate: true, - allowComments: false, - enableSchemaRequest: true, - schemas: [{ - uri: `${window.location.protocol}//${window.location.host}/schemas/0.8/workflow.json`, - fileMatch: [modelUri.toString()] - }] - }); -} - -function getModelMarkers(owner) { - return monaco.editor.getModelMarkers({ owner }) || []; -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/monaco-editor-interop-extension.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/monaco-editor-interop-extension.js new file mode 100644 index 000000000..fda6d8f4c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/js/monaco-editor-interop-extension.js @@ -0,0 +1,112 @@ +import { parse } from "https://esm.sh/json-source-map"; +import { LineCounter, parseDocument } from "https://esm.sh/yaml"; + +/** + * Adds a validation schema to monaco editor's diagnostics options + * @param {any} schema The validation schema to add + * @param {any} schemaUri The schema identifier + * @param {any} schemaType The schema type, used to match the "file"/model URI + * @returns + */ +export function addValidationSchema(schema, schemaUri, schemaType) { + // JSON + if (!monaco.languages.json.jsonDefaults.diagnosticsOptions.schemas.find(s => s.uri === schemaUri)) { + monaco.languages.json.jsonDefaults.setDiagnosticsOptions({ + validate: true, + allowComments: false, + enableSchemaRequest: true, + schemas: [ + ...monaco.languages.json.jsonDefaults.diagnosticsOptions.schemas, + { + schema: JSON.parse(schema), + uri: schemaUri, + fileMatch: [schemaType] + } + ] + }); + } + // YAML + if (!monacoYaml.yamlDefaults.diagnosticsOptions.schemas.find(s => s.uri === schemaUri)) { + monacoYaml.setDiagnosticsOptions({ + validate: true, + enableSchemaRequest: true, + //hover: true, + //completion: true, + //format: true, + schemas: [ + ...monacoYaml.yamlDefaults.diagnosticsOptions.schemas, + { + schema: JSON.parse(schema), + uri: schemaUri, + fileMatch: [schemaType] + } + ] + }); + } +} + +/** + * Finds the range in a JSON text corresponding to a provided JSON Pointer + * @param {string} source The source JSON text + * @param {string} jsonPointer The JSON pointer to find the range for + * @returns {Monaco.Range} The corresponding range + */ +function getJsonPointeRangeForJson(source, jsonPointer) { + const { pointers } = parse(source); + const node = pointers[jsonPointer]; + if (!node) { + throw new Error(`Unable to find JSON pointer '${jsonPointer}'`); + } + return { + startLineNumber: node.key.line + 1, + startColumn: node.key.column + 1, + endLineNumber: node.valueEnd.line + 1, + endColumn: node.valueEnd.column + 1 + }; +} + +/** + * Finds the range in a YAML text corresponding to a provided JSON Pointer + * @param {string} source The source YAML text + * @param {string} jsonPointer The JSON pointer to find the range for + * @returns {Monaco.Range} The corresponding range + */ +function getJsonPointeRangeForYaml(source, jsonPointer) { + const lineCounter = new LineCounter(source); + const document = parseDocument(source, { keepSourceTokens: true, lineCounter }); + const node = jsonPointer.split('/').slice(1).reduce((n, key) => { + const idx = parseInt(key, 10); + const nextNode = n.get(!isNaN(idx) ? idx : key, true); + if (!nextNode) { + throw new Error(`Unable to find JSON pointer '${jsonPointer}'`); + } + return nextNode; + }, document); + const start = lineCounter.linePos(node.range[0]); + const end = lineCounter.linePos(node.range[1]); + return { + startLineNumber: start.line, + startColumn: start.col, + endLineNumber: end.line, + endColumn: end.col + } +} + +/** + * Finds the range in a JSON/YAML text corresponding to a provided JSON Pointer + * @param {string} source The source JSON/YAML text + * @param {string} jsonPointer The JSON pointer to find the range for + * @param {'json'|'yaml'} language The language of the source, JSON or YAML + * @returns {Monaco.Range} The corresponding range + */ +export function getJsonPointerRange(source, jsonPointer, language) { + if (language.toLowerCase() === 'json') { + return getJsonPointeRangeForJson(source, jsonPointer); + } + else if (language.toLowerCase() === 'yaml') { + return getJsonPointeRangeForYaml(source, jsonPointer); + } + else { + throw new Error(`Invalid language '${language}'`); + } +} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/node-ghost.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/node-ghost.js deleted file mode 100644 index 24b903336..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/js/node-ghost.js +++ /dev/null @@ -1,34 +0,0 @@ -const getTranslate = (node, axis) => { - const style = window.getComputedStyle(node); - const matrix = new WebKitCSSMatrix(style.transform); - switch (axis) { - case 'x': - return matrix.m41; - case 'y': - return matrix.m42; - case 'z': - return matrix.m43; - default: - throw `unknown axis '${axis}`; - } -}; - -class NodeGhost { - constructor(node) { - this.node = node; - this.ghost = node.cloneNode(true); - this.ghost.classList.add('ghost'); - this.transformable = this.ghost.querySelector('g[transform]'); - this.node.parentNode.appendChild(this.ghost); - } - dispose() { - this.node.parentNode.removeChild(this.ghost); - } - move(x, y) { - const translateX = getTranslate(this.transformable, 'x') + x; - const translateY = getTranslate(this.transformable, 'y') + y; - this.transformable.setAttribute('transform', `translate(${translateX}, ${translateY})`); - } -} - -export const createNodeGhost = (node) => new NodeGhost(node); \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/splash.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/splash.js deleted file mode 100644 index 076d825ec..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/js/splash.js +++ /dev/null @@ -1,25 +0,0 @@ -document.addEventListener('DOMContentLoaded', () => { - const $animationContainer = document.getElementById('animation-container'); - const now = new Date(); - const localStorageKey = 'next-logo-animation'; - const nextLogoAnimationDateStr = window.localStorage.getItem(localStorageKey); - const nextLogoAnimationDate = new Date(parseInt(nextLogoAnimationDateStr, 10)); - if (nextLogoAnimationDate && now < nextLogoAnimationDate) { - $animationContainer.remove(); - } - else { - const removeContainer = (e) => { - if ($animationContainer.isSameNode(e.target)) { - $animationContainer.removeEventListener('animationend', removeContainer); - $animationContainer.remove(); - const nextAppearance = new Date(); - nextAppearance.setHours(0); - nextAppearance.setMinutes(0); - nextAppearance.setSeconds(0); - nextAppearance.setDate(nextAppearance.getDate() + 1); - window.localStorage.setItem(localStorageKey, nextAppearance.getTime()); - } - }; - $animationContainer.addEventListener('animationend', removeContainer); - } -}); \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/style.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/style.js deleted file mode 100644 index e9b623576..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/js/style.js +++ /dev/null @@ -1,4 +0,0 @@ -window.getComputedStyleProperty = (propertyName) => { - let styles = window.getComputedStyle(document.documentElement); - return styles.getPropertyValue(propertyName); -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/js/yaml-conversion.js b/src/dashboard/Synapse.Dashboard/wwwroot/js/yaml-conversion.js deleted file mode 100644 index 95c24ad14..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/js/yaml-conversion.js +++ /dev/null @@ -1,11 +0,0 @@ -export function yamlToJson(yamlText) { - if (!yamlText) return ""; - var obj = jsyaml.load(yamlText); - return JSON.stringify(obj, null, 2); -} - -export function jsonToYaml(jsonText) { - if (!jsonText) return ""; - var obj = JSON.parse(jsonText); - return jsyaml.dump(obj); -} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/LICENSE b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/LICENSE new file mode 100644 index 000000000..f95243911 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019-2024 The Bootstrap Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/LICENSE.md b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/LICENSE.md deleted file mode 100644 index 47f06b69a..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2019-2021 The Bootstrap Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/README.md b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/README.md index bb558ee72..4e38a7f03 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/README.md +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/README.md @@ -1,13 +1,13 @@

- - Bootstrap logo + + Bootstrap logo

Bootstrap Icons

- Official open source SVG icon library for Bootstrap with over 1,800 icons. + Official open source SVG icon library for Bootstrap with over 2,000 icons.
Explore Bootstrap Icons »
@@ -20,7 +20,7 @@

-[![Bootstrap Icons preview](https://github.com/twbs/icons/blob/main/.github/preview.png)](https://icons.getbootstrap.com) +[![Bootstrap Icons preview](https://github.com/twbs/icons/blob/main/.github/preview.png)](https://icons.getbootstrap.com/) ## Install @@ -36,7 +36,7 @@ For those [using Packagist](https://packagist.org/packages/twbs/bootstrap-icons) composer require twbs/bootstrap-icons ``` -[Also available in Figma.](https://www.figma.com/community/file/1042482994486402696/Bootstrap-Icons) +[Also available in Figma](https://www.figma.com/community/file/1042482994486402696/Bootstrap-Icons). ## Usage @@ -47,11 +47,12 @@ Depending on your setup, you can include Bootstrap Icons in a handful of ways. - Use the SVG sprite - Include via CSS -[See the docs for more information.](https://icons.getbootstrap.com/#usage) +[See the docs for more information](https://icons.getbootstrap.com/#usage). ## Development -[![Build Status](https://github.com/twbs/icons/workflows/Tests/badge.svg)](https://github.com/twbs/icons/actions?workflow=Tests) +[![Build Status](https://img.shields.io/github/actions/workflow/status/twbs/icons/test.yml?branch=main&label=Tests&logo=github)](https://github.com/twbs/icons/actions/workflows/test.yml?query=workflow%3ATests+branch%3Amain) +[![npm version](https://img.shields.io/npm/v/bootstrap-icons?logo=npm&logoColor=fff)](https://www.npmjs.com/package/bootstrap-icons) Clone the repo, install dependencies, and start the Hugo server locally. @@ -66,31 +67,33 @@ Then open `http://localhost:4000` in your browser. ### npm scripts -Here are some key scripts you'll use during development. Be sure to look to our `package.json` for a complete list of scripts. +Here are some key scripts you'll use during development. Be sure to look to our `package.json` or `npm run` output for a complete list of scripts. -| Script | Description | -| --- | --- | -| `start` | Alias for running `docs-serve` | -| `docs-serve` | Starts a local Hugo server | -| `pages` | Generates permalink pages for each icon with template Markdown | -| `icons` | Processes and optimizes SVGs in `icons` directory | +| Script | Description | +|--------------|-------------------------------------------------------------------------------| +| `start` | Alias for running `docs-serve` | +| `docs-serve` | Starts a local Hugo server | +| `pages` | Generates permalink pages for each icon with template Markdown | +| `icons` | Processes and optimizes SVGs in `icons` directory, generates fonts and sprite | ## Adding SVGs Icons are typically only added by @mdo, but exceptions can be made. New glyphs are designed in Figma first on a 16x16px grid, then exported as flattened SVGs with `fill` (no stroke). Once a new SVG icon has been added to the `icons` directory, we use an npm script to: 1. Optimize our SVGs with SVGO. -2. Modify the SVGs source HTML, removing all attributes before setting new attributes and values in our preferred order. +2. Modify the SVGs source code, removing all attributes before setting new attributes and values in our preferred order. Use `npm run icons` to run the script, run `npm run pages` to build permalink pages, complete those pages, and, finally, commit the results in a new branch for updating. +**Warning**: Please exclude any auto-generated files, like `font/**` and `bootstrap-icons.svg` from your branch because they cause conflicts, and we generally update the dist files before a release. + ## Publishing Documentation is published automatically when a new Git tag is published. See our [GitHub Actions](https://github.com/twbs/icons/tree/main/.github/workflows) and [`package.json`](https://github.com/twbs/icons/blob/main/package.json) for more information. ## License -MIT +[MIT](LICENSE) ## Author diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/bootstrap-icons.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/bootstrap-icons.svg index 8104aeadb..b7d55a8e2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/bootstrap-icons.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/bootstrap-icons.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.css b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.css index 7f0bd54b1..bc84a5fe3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.css +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.css @@ -1,8 +1,14 @@ +/*! + * Bootstrap Icons v1.11.3 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + @font-face { font-display: block; font-family: "bootstrap-icons"; - src: url("./fonts/bootstrap-icons.woff2?8d200481aa7f02a2d63a331fc782cfaf") format("woff2"), -url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("woff"); + src: url("./fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format("woff2"), +url("./fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff"); } .bi::before, @@ -441,7 +447,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-cloud-fog2::before { content: "\f2a2"; } .bi-cloud-hail-fill::before { content: "\f2a3"; } .bi-cloud-hail::before { content: "\f2a4"; } -.bi-cloud-haze-1::before { content: "\f2a5"; } .bi-cloud-haze-fill::before { content: "\f2a6"; } .bi-cloud-haze::before { content: "\f2a7"; } .bi-cloud-haze2-fill::before { content: "\f2a8"; } @@ -1437,21 +1442,16 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-dpad::before { content: "\f687"; } .bi-ear-fill::before { content: "\f688"; } .bi-ear::before { content: "\f689"; } -.bi-envelope-check-1::before { content: "\f68a"; } .bi-envelope-check-fill::before { content: "\f68b"; } .bi-envelope-check::before { content: "\f68c"; } -.bi-envelope-dash-1::before { content: "\f68d"; } .bi-envelope-dash-fill::before { content: "\f68e"; } .bi-envelope-dash::before { content: "\f68f"; } -.bi-envelope-exclamation-1::before { content: "\f690"; } .bi-envelope-exclamation-fill::before { content: "\f691"; } .bi-envelope-exclamation::before { content: "\f692"; } .bi-envelope-plus-fill::before { content: "\f693"; } .bi-envelope-plus::before { content: "\f694"; } -.bi-envelope-slash-1::before { content: "\f695"; } .bi-envelope-slash-fill::before { content: "\f696"; } .bi-envelope-slash::before { content: "\f697"; } -.bi-envelope-x-1::before { content: "\f698"; } .bi-envelope-x-fill::before { content: "\f699"; } .bi-envelope-x::before { content: "\f69a"; } .bi-explicit-fill::before { content: "\f69b"; } @@ -1461,8 +1461,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-list-columns-reverse::before { content: "\f69f"; } .bi-list-columns::before { content: "\f6a0"; } .bi-meta::before { content: "\f6a1"; } -.bi-mortorboard-fill::before { content: "\f6a2"; } -.bi-mortorboard::before { content: "\f6a3"; } .bi-nintendo-switch::before { content: "\f6a4"; } .bi-pc-display-horizontal::before { content: "\f6a5"; } .bi-pc-display::before { content: "\f6a6"; } @@ -1481,7 +1479,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-send-check::before { content: "\f6b3"; } .bi-send-dash-fill::before { content: "\f6b4"; } .bi-send-dash::before { content: "\f6b5"; } -.bi-send-exclamation-1::before { content: "\f6b6"; } .bi-send-exclamation-fill::before { content: "\f6b7"; } .bi-send-exclamation::before { content: "\f6b8"; } .bi-send-fill::before { content: "\f6b9"; } @@ -1493,7 +1490,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-send-x::before { content: "\f6bf"; } .bi-send::before { content: "\f6c0"; } .bi-steam::before { content: "\f6c1"; } -.bi-terminal-dash-1::before { content: "\f6c2"; } .bi-terminal-dash::before { content: "\f6c3"; } .bi-terminal-plus::before { content: "\f6c4"; } .bi-terminal-split::before { content: "\f6c5"; } @@ -1523,7 +1519,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-usb-symbol::before { content: "\f6dd"; } .bi-usb::before { content: "\f6de"; } .bi-boombox-fill::before { content: "\f6df"; } -.bi-displayport-1::before { content: "\f6e0"; } .bi-displayport::before { content: "\f6e1"; } .bi-gpu-card::before { content: "\f6e2"; } .bi-memory::before { content: "\f6e3"; } @@ -1536,8 +1531,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-pci-card::before { content: "\f6ea"; } .bi-router-fill::before { content: "\f6eb"; } .bi-router::before { content: "\f6ec"; } -.bi-ssd-fill::before { content: "\f6ed"; } -.bi-ssd::before { content: "\f6ee"; } .bi-thunderbolt-fill::before { content: "\f6ef"; } .bi-thunderbolt::before { content: "\f6f0"; } .bi-usb-drive-fill::before { content: "\f6f1"; } @@ -1644,7 +1637,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-filetype-pdf::before { content: "\f756"; } .bi-filetype-php::before { content: "\f757"; } .bi-filetype-png::before { content: "\f758"; } -.bi-filetype-ppt-1::before { content: "\f759"; } .bi-filetype-ppt::before { content: "\f75a"; } .bi-filetype-psd::before { content: "\f75b"; } .bi-filetype-py::before { content: "\f75c"; } @@ -1660,7 +1652,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-filetype-txt::before { content: "\f766"; } .bi-filetype-wav::before { content: "\f767"; } .bi-filetype-woff::before { content: "\f768"; } -.bi-filetype-xls-1::before { content: "\f769"; } .bi-filetype-xls::before { content: "\f76a"; } .bi-filetype-xml::before { content: "\f76b"; } .bi-filetype-yml::before { content: "\f76c"; } @@ -1703,56 +1694,38 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-filetype-json::before { content: "\f791"; } .bi-filetype-pptx::before { content: "\f792"; } .bi-filetype-xlsx::before { content: "\f793"; } -.bi-1-circle-1::before { content: "\f794"; } -.bi-1-circle-fill-1::before { content: "\f795"; } .bi-1-circle-fill::before { content: "\f796"; } .bi-1-circle::before { content: "\f797"; } .bi-1-square-fill::before { content: "\f798"; } .bi-1-square::before { content: "\f799"; } -.bi-2-circle-1::before { content: "\f79a"; } -.bi-2-circle-fill-1::before { content: "\f79b"; } .bi-2-circle-fill::before { content: "\f79c"; } .bi-2-circle::before { content: "\f79d"; } .bi-2-square-fill::before { content: "\f79e"; } .bi-2-square::before { content: "\f79f"; } -.bi-3-circle-1::before { content: "\f7a0"; } -.bi-3-circle-fill-1::before { content: "\f7a1"; } .bi-3-circle-fill::before { content: "\f7a2"; } .bi-3-circle::before { content: "\f7a3"; } .bi-3-square-fill::before { content: "\f7a4"; } .bi-3-square::before { content: "\f7a5"; } -.bi-4-circle-1::before { content: "\f7a6"; } -.bi-4-circle-fill-1::before { content: "\f7a7"; } .bi-4-circle-fill::before { content: "\f7a8"; } .bi-4-circle::before { content: "\f7a9"; } .bi-4-square-fill::before { content: "\f7aa"; } .bi-4-square::before { content: "\f7ab"; } -.bi-5-circle-1::before { content: "\f7ac"; } -.bi-5-circle-fill-1::before { content: "\f7ad"; } .bi-5-circle-fill::before { content: "\f7ae"; } .bi-5-circle::before { content: "\f7af"; } .bi-5-square-fill::before { content: "\f7b0"; } .bi-5-square::before { content: "\f7b1"; } -.bi-6-circle-1::before { content: "\f7b2"; } -.bi-6-circle-fill-1::before { content: "\f7b3"; } .bi-6-circle-fill::before { content: "\f7b4"; } .bi-6-circle::before { content: "\f7b5"; } .bi-6-square-fill::before { content: "\f7b6"; } .bi-6-square::before { content: "\f7b7"; } -.bi-7-circle-1::before { content: "\f7b8"; } -.bi-7-circle-fill-1::before { content: "\f7b9"; } .bi-7-circle-fill::before { content: "\f7ba"; } .bi-7-circle::before { content: "\f7bb"; } .bi-7-square-fill::before { content: "\f7bc"; } .bi-7-square::before { content: "\f7bd"; } -.bi-8-circle-1::before { content: "\f7be"; } -.bi-8-circle-fill-1::before { content: "\f7bf"; } .bi-8-circle-fill::before { content: "\f7c0"; } .bi-8-circle::before { content: "\f7c1"; } .bi-8-square-fill::before { content: "\f7c2"; } .bi-8-square::before { content: "\f7c3"; } -.bi-9-circle-1::before { content: "\f7c4"; } -.bi-9-circle-fill-1::before { content: "\f7c5"; } .bi-9-circle-fill::before { content: "\f7c6"; } .bi-9-circle::before { content: "\f7c7"; } .bi-9-square-fill::before { content: "\f7c8"; } @@ -1771,8 +1744,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-browser-edge::before { content: "\f7d5"; } .bi-browser-firefox::before { content: "\f7d6"; } .bi-browser-safari::before { content: "\f7d7"; } -.bi-c-circle-1::before { content: "\f7d8"; } -.bi-c-circle-fill-1::before { content: "\f7d9"; } .bi-c-circle-fill::before { content: "\f7da"; } .bi-c-circle::before { content: "\f7db"; } .bi-c-square-fill::before { content: "\f7dc"; } @@ -1783,8 +1754,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-car-front::before { content: "\f7e1"; } .bi-cassette-fill::before { content: "\f7e2"; } .bi-cassette::before { content: "\f7e3"; } -.bi-cc-circle-1::before { content: "\f7e4"; } -.bi-cc-circle-fill-1::before { content: "\f7e5"; } .bi-cc-circle-fill::before { content: "\f7e6"; } .bi-cc-circle::before { content: "\f7e7"; } .bi-cc-square-fill::before { content: "\f7e8"; } @@ -1803,8 +1772,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-filetype-sql::before { content: "\f7f5"; } .bi-fire::before { content: "\f7f6"; } .bi-google-play::before { content: "\f7f7"; } -.bi-h-circle-1::before { content: "\f7f8"; } -.bi-h-circle-fill-1::before { content: "\f7f9"; } .bi-h-circle-fill::before { content: "\f7fa"; } .bi-h-circle::before { content: "\f7fb"; } .bi-h-square-fill::before { content: "\f7fc"; } @@ -1813,8 +1780,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-lungs-fill::before { content: "\f7ff"; } .bi-lungs::before { content: "\f800"; } .bi-microsoft-teams::before { content: "\f801"; } -.bi-p-circle-1::before { content: "\f802"; } -.bi-p-circle-fill-1::before { content: "\f803"; } .bi-p-circle-fill::before { content: "\f804"; } .bi-p-circle::before { content: "\f805"; } .bi-p-square-fill::before { content: "\f806"; } @@ -1823,8 +1788,6 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-pass::before { content: "\f809"; } .bi-prescription::before { content: "\f80a"; } .bi-prescription2::before { content: "\f80b"; } -.bi-r-circle-1::before { content: "\f80c"; } -.bi-r-circle-fill-1::before { content: "\f80d"; } .bi-r-circle-fill::before { content: "\f80e"; } .bi-r-circle::before { content: "\f80f"; } .bi-r-square-fill::before { content: "\f810"; } @@ -1874,3 +1837,242 @@ url("./fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("wof .bi-fuel-pump-diesel::before { content: "\f83c"; } .bi-fuel-pump-fill::before { content: "\f83d"; } .bi-fuel-pump::before { content: "\f83e"; } +.bi-0-circle-fill::before { content: "\f83f"; } +.bi-0-circle::before { content: "\f840"; } +.bi-0-square-fill::before { content: "\f841"; } +.bi-0-square::before { content: "\f842"; } +.bi-rocket-fill::before { content: "\f843"; } +.bi-rocket-takeoff-fill::before { content: "\f844"; } +.bi-rocket-takeoff::before { content: "\f845"; } +.bi-rocket::before { content: "\f846"; } +.bi-stripe::before { content: "\f847"; } +.bi-subscript::before { content: "\f848"; } +.bi-superscript::before { content: "\f849"; } +.bi-trello::before { content: "\f84a"; } +.bi-envelope-at-fill::before { content: "\f84b"; } +.bi-envelope-at::before { content: "\f84c"; } +.bi-regex::before { content: "\f84d"; } +.bi-text-wrap::before { content: "\f84e"; } +.bi-sign-dead-end-fill::before { content: "\f84f"; } +.bi-sign-dead-end::before { content: "\f850"; } +.bi-sign-do-not-enter-fill::before { content: "\f851"; } +.bi-sign-do-not-enter::before { content: "\f852"; } +.bi-sign-intersection-fill::before { content: "\f853"; } +.bi-sign-intersection-side-fill::before { content: "\f854"; } +.bi-sign-intersection-side::before { content: "\f855"; } +.bi-sign-intersection-t-fill::before { content: "\f856"; } +.bi-sign-intersection-t::before { content: "\f857"; } +.bi-sign-intersection-y-fill::before { content: "\f858"; } +.bi-sign-intersection-y::before { content: "\f859"; } +.bi-sign-intersection::before { content: "\f85a"; } +.bi-sign-merge-left-fill::before { content: "\f85b"; } +.bi-sign-merge-left::before { content: "\f85c"; } +.bi-sign-merge-right-fill::before { content: "\f85d"; } +.bi-sign-merge-right::before { content: "\f85e"; } +.bi-sign-no-left-turn-fill::before { content: "\f85f"; } +.bi-sign-no-left-turn::before { content: "\f860"; } +.bi-sign-no-parking-fill::before { content: "\f861"; } +.bi-sign-no-parking::before { content: "\f862"; } +.bi-sign-no-right-turn-fill::before { content: "\f863"; } +.bi-sign-no-right-turn::before { content: "\f864"; } +.bi-sign-railroad-fill::before { content: "\f865"; } +.bi-sign-railroad::before { content: "\f866"; } +.bi-building-add::before { content: "\f867"; } +.bi-building-check::before { content: "\f868"; } +.bi-building-dash::before { content: "\f869"; } +.bi-building-down::before { content: "\f86a"; } +.bi-building-exclamation::before { content: "\f86b"; } +.bi-building-fill-add::before { content: "\f86c"; } +.bi-building-fill-check::before { content: "\f86d"; } +.bi-building-fill-dash::before { content: "\f86e"; } +.bi-building-fill-down::before { content: "\f86f"; } +.bi-building-fill-exclamation::before { content: "\f870"; } +.bi-building-fill-gear::before { content: "\f871"; } +.bi-building-fill-lock::before { content: "\f872"; } +.bi-building-fill-slash::before { content: "\f873"; } +.bi-building-fill-up::before { content: "\f874"; } +.bi-building-fill-x::before { content: "\f875"; } +.bi-building-fill::before { content: "\f876"; } +.bi-building-gear::before { content: "\f877"; } +.bi-building-lock::before { content: "\f878"; } +.bi-building-slash::before { content: "\f879"; } +.bi-building-up::before { content: "\f87a"; } +.bi-building-x::before { content: "\f87b"; } +.bi-buildings-fill::before { content: "\f87c"; } +.bi-buildings::before { content: "\f87d"; } +.bi-bus-front-fill::before { content: "\f87e"; } +.bi-bus-front::before { content: "\f87f"; } +.bi-ev-front-fill::before { content: "\f880"; } +.bi-ev-front::before { content: "\f881"; } +.bi-globe-americas::before { content: "\f882"; } +.bi-globe-asia-australia::before { content: "\f883"; } +.bi-globe-central-south-asia::before { content: "\f884"; } +.bi-globe-europe-africa::before { content: "\f885"; } +.bi-house-add-fill::before { content: "\f886"; } +.bi-house-add::before { content: "\f887"; } +.bi-house-check-fill::before { content: "\f888"; } +.bi-house-check::before { content: "\f889"; } +.bi-house-dash-fill::before { content: "\f88a"; } +.bi-house-dash::before { content: "\f88b"; } +.bi-house-down-fill::before { content: "\f88c"; } +.bi-house-down::before { content: "\f88d"; } +.bi-house-exclamation-fill::before { content: "\f88e"; } +.bi-house-exclamation::before { content: "\f88f"; } +.bi-house-gear-fill::before { content: "\f890"; } +.bi-house-gear::before { content: "\f891"; } +.bi-house-lock-fill::before { content: "\f892"; } +.bi-house-lock::before { content: "\f893"; } +.bi-house-slash-fill::before { content: "\f894"; } +.bi-house-slash::before { content: "\f895"; } +.bi-house-up-fill::before { content: "\f896"; } +.bi-house-up::before { content: "\f897"; } +.bi-house-x-fill::before { content: "\f898"; } +.bi-house-x::before { content: "\f899"; } +.bi-person-add::before { content: "\f89a"; } +.bi-person-down::before { content: "\f89b"; } +.bi-person-exclamation::before { content: "\f89c"; } +.bi-person-fill-add::before { content: "\f89d"; } +.bi-person-fill-check::before { content: "\f89e"; } +.bi-person-fill-dash::before { content: "\f89f"; } +.bi-person-fill-down::before { content: "\f8a0"; } +.bi-person-fill-exclamation::before { content: "\f8a1"; } +.bi-person-fill-gear::before { content: "\f8a2"; } +.bi-person-fill-lock::before { content: "\f8a3"; } +.bi-person-fill-slash::before { content: "\f8a4"; } +.bi-person-fill-up::before { content: "\f8a5"; } +.bi-person-fill-x::before { content: "\f8a6"; } +.bi-person-gear::before { content: "\f8a7"; } +.bi-person-lock::before { content: "\f8a8"; } +.bi-person-slash::before { content: "\f8a9"; } +.bi-person-up::before { content: "\f8aa"; } +.bi-scooter::before { content: "\f8ab"; } +.bi-taxi-front-fill::before { content: "\f8ac"; } +.bi-taxi-front::before { content: "\f8ad"; } +.bi-amd::before { content: "\f8ae"; } +.bi-database-add::before { content: "\f8af"; } +.bi-database-check::before { content: "\f8b0"; } +.bi-database-dash::before { content: "\f8b1"; } +.bi-database-down::before { content: "\f8b2"; } +.bi-database-exclamation::before { content: "\f8b3"; } +.bi-database-fill-add::before { content: "\f8b4"; } +.bi-database-fill-check::before { content: "\f8b5"; } +.bi-database-fill-dash::before { content: "\f8b6"; } +.bi-database-fill-down::before { content: "\f8b7"; } +.bi-database-fill-exclamation::before { content: "\f8b8"; } +.bi-database-fill-gear::before { content: "\f8b9"; } +.bi-database-fill-lock::before { content: "\f8ba"; } +.bi-database-fill-slash::before { content: "\f8bb"; } +.bi-database-fill-up::before { content: "\f8bc"; } +.bi-database-fill-x::before { content: "\f8bd"; } +.bi-database-fill::before { content: "\f8be"; } +.bi-database-gear::before { content: "\f8bf"; } +.bi-database-lock::before { content: "\f8c0"; } +.bi-database-slash::before { content: "\f8c1"; } +.bi-database-up::before { content: "\f8c2"; } +.bi-database-x::before { content: "\f8c3"; } +.bi-database::before { content: "\f8c4"; } +.bi-houses-fill::before { content: "\f8c5"; } +.bi-houses::before { content: "\f8c6"; } +.bi-nvidia::before { content: "\f8c7"; } +.bi-person-vcard-fill::before { content: "\f8c8"; } +.bi-person-vcard::before { content: "\f8c9"; } +.bi-sina-weibo::before { content: "\f8ca"; } +.bi-tencent-qq::before { content: "\f8cb"; } +.bi-wikipedia::before { content: "\f8cc"; } +.bi-alphabet-uppercase::before { content: "\f2a5"; } +.bi-alphabet::before { content: "\f68a"; } +.bi-amazon::before { content: "\f68d"; } +.bi-arrows-collapse-vertical::before { content: "\f690"; } +.bi-arrows-expand-vertical::before { content: "\f695"; } +.bi-arrows-vertical::before { content: "\f698"; } +.bi-arrows::before { content: "\f6a2"; } +.bi-ban-fill::before { content: "\f6a3"; } +.bi-ban::before { content: "\f6b6"; } +.bi-bing::before { content: "\f6c2"; } +.bi-cake::before { content: "\f6e0"; } +.bi-cake2::before { content: "\f6ed"; } +.bi-cookie::before { content: "\f6ee"; } +.bi-copy::before { content: "\f759"; } +.bi-crosshair::before { content: "\f769"; } +.bi-crosshair2::before { content: "\f794"; } +.bi-emoji-astonished-fill::before { content: "\f795"; } +.bi-emoji-astonished::before { content: "\f79a"; } +.bi-emoji-grimace-fill::before { content: "\f79b"; } +.bi-emoji-grimace::before { content: "\f7a0"; } +.bi-emoji-grin-fill::before { content: "\f7a1"; } +.bi-emoji-grin::before { content: "\f7a6"; } +.bi-emoji-surprise-fill::before { content: "\f7a7"; } +.bi-emoji-surprise::before { content: "\f7ac"; } +.bi-emoji-tear-fill::before { content: "\f7ad"; } +.bi-emoji-tear::before { content: "\f7b2"; } +.bi-envelope-arrow-down-fill::before { content: "\f7b3"; } +.bi-envelope-arrow-down::before { content: "\f7b8"; } +.bi-envelope-arrow-up-fill::before { content: "\f7b9"; } +.bi-envelope-arrow-up::before { content: "\f7be"; } +.bi-feather::before { content: "\f7bf"; } +.bi-feather2::before { content: "\f7c4"; } +.bi-floppy-fill::before { content: "\f7c5"; } +.bi-floppy::before { content: "\f7d8"; } +.bi-floppy2-fill::before { content: "\f7d9"; } +.bi-floppy2::before { content: "\f7e4"; } +.bi-gitlab::before { content: "\f7e5"; } +.bi-highlighter::before { content: "\f7f8"; } +.bi-marker-tip::before { content: "\f802"; } +.bi-nvme-fill::before { content: "\f803"; } +.bi-nvme::before { content: "\f80c"; } +.bi-opencollective::before { content: "\f80d"; } +.bi-pci-card-network::before { content: "\f8cd"; } +.bi-pci-card-sound::before { content: "\f8ce"; } +.bi-radar::before { content: "\f8cf"; } +.bi-send-arrow-down-fill::before { content: "\f8d0"; } +.bi-send-arrow-down::before { content: "\f8d1"; } +.bi-send-arrow-up-fill::before { content: "\f8d2"; } +.bi-send-arrow-up::before { content: "\f8d3"; } +.bi-sim-slash-fill::before { content: "\f8d4"; } +.bi-sim-slash::before { content: "\f8d5"; } +.bi-sourceforge::before { content: "\f8d6"; } +.bi-substack::before { content: "\f8d7"; } +.bi-threads-fill::before { content: "\f8d8"; } +.bi-threads::before { content: "\f8d9"; } +.bi-transparency::before { content: "\f8da"; } +.bi-twitter-x::before { content: "\f8db"; } +.bi-type-h4::before { content: "\f8dc"; } +.bi-type-h5::before { content: "\f8dd"; } +.bi-type-h6::before { content: "\f8de"; } +.bi-backpack-fill::before { content: "\f8df"; } +.bi-backpack::before { content: "\f8e0"; } +.bi-backpack2-fill::before { content: "\f8e1"; } +.bi-backpack2::before { content: "\f8e2"; } +.bi-backpack3-fill::before { content: "\f8e3"; } +.bi-backpack3::before { content: "\f8e4"; } +.bi-backpack4-fill::before { content: "\f8e5"; } +.bi-backpack4::before { content: "\f8e6"; } +.bi-brilliance::before { content: "\f8e7"; } +.bi-cake-fill::before { content: "\f8e8"; } +.bi-cake2-fill::before { content: "\f8e9"; } +.bi-duffle-fill::before { content: "\f8ea"; } +.bi-duffle::before { content: "\f8eb"; } +.bi-exposure::before { content: "\f8ec"; } +.bi-gender-neuter::before { content: "\f8ed"; } +.bi-highlights::before { content: "\f8ee"; } +.bi-luggage-fill::before { content: "\f8ef"; } +.bi-luggage::before { content: "\f8f0"; } +.bi-mailbox-flag::before { content: "\f8f1"; } +.bi-mailbox2-flag::before { content: "\f8f2"; } +.bi-noise-reduction::before { content: "\f8f3"; } +.bi-passport-fill::before { content: "\f8f4"; } +.bi-passport::before { content: "\f8f5"; } +.bi-person-arms-up::before { content: "\f8f6"; } +.bi-person-raised-hand::before { content: "\f8f7"; } +.bi-person-standing-dress::before { content: "\f8f8"; } +.bi-person-standing::before { content: "\f8f9"; } +.bi-person-walking::before { content: "\f8fa"; } +.bi-person-wheelchair::before { content: "\f8fb"; } +.bi-shadows::before { content: "\f8fc"; } +.bi-suitcase-fill::before { content: "\f8fd"; } +.bi-suitcase-lg-fill::before { content: "\f8fe"; } +.bi-suitcase-lg::before { content: "\f8ff"; } +.bi-suitcase::before { content: "\f900"; } +.bi-suitcase2-fill::before { content: "\f901"; } +.bi-suitcase2::before { content: "\f902"; } +.bi-vignette::before { content: "\f903"; } diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.json b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.json index faa3b2ce7..56247e50b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.json +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.json @@ -420,7 +420,6 @@ "cloud-fog2": 62114, "cloud-hail-fill": 62115, "cloud-hail": 62116, - "cloud-haze-1": 62117, "cloud-haze-fill": 62118, "cloud-haze": 62119, "cloud-haze2-fill": 62120, @@ -1416,21 +1415,16 @@ "dpad": 63111, "ear-fill": 63112, "ear": 63113, - "envelope-check-1": 63114, "envelope-check-fill": 63115, "envelope-check": 63116, - "envelope-dash-1": 63117, "envelope-dash-fill": 63118, "envelope-dash": 63119, - "envelope-exclamation-1": 63120, "envelope-exclamation-fill": 63121, "envelope-exclamation": 63122, "envelope-plus-fill": 63123, "envelope-plus": 63124, - "envelope-slash-1": 63125, "envelope-slash-fill": 63126, "envelope-slash": 63127, - "envelope-x-1": 63128, "envelope-x-fill": 63129, "envelope-x": 63130, "explicit-fill": 63131, @@ -1440,8 +1434,6 @@ "list-columns-reverse": 63135, "list-columns": 63136, "meta": 63137, - "mortorboard-fill": 63138, - "mortorboard": 63139, "nintendo-switch": 63140, "pc-display-horizontal": 63141, "pc-display": 63142, @@ -1460,7 +1452,6 @@ "send-check": 63155, "send-dash-fill": 63156, "send-dash": 63157, - "send-exclamation-1": 63158, "send-exclamation-fill": 63159, "send-exclamation": 63160, "send-fill": 63161, @@ -1472,7 +1463,6 @@ "send-x": 63167, "send": 63168, "steam": 63169, - "terminal-dash-1": 63170, "terminal-dash": 63171, "terminal-plus": 63172, "terminal-split": 63173, @@ -1502,7 +1492,6 @@ "usb-symbol": 63197, "usb": 63198, "boombox-fill": 63199, - "displayport-1": 63200, "displayport": 63201, "gpu-card": 63202, "memory": 63203, @@ -1515,8 +1504,6 @@ "pci-card": 63210, "router-fill": 63211, "router": 63212, - "ssd-fill": 63213, - "ssd": 63214, "thunderbolt-fill": 63215, "thunderbolt": 63216, "usb-drive-fill": 63217, @@ -1623,7 +1610,6 @@ "filetype-pdf": 63318, "filetype-php": 63319, "filetype-png": 63320, - "filetype-ppt-1": 63321, "filetype-ppt": 63322, "filetype-psd": 63323, "filetype-py": 63324, @@ -1639,7 +1625,6 @@ "filetype-txt": 63334, "filetype-wav": 63335, "filetype-woff": 63336, - "filetype-xls-1": 63337, "filetype-xls": 63338, "filetype-xml": 63339, "filetype-yml": 63340, @@ -1682,56 +1667,38 @@ "filetype-json": 63377, "filetype-pptx": 63378, "filetype-xlsx": 63379, - "1-circle-1": 63380, - "1-circle-fill-1": 63381, "1-circle-fill": 63382, "1-circle": 63383, "1-square-fill": 63384, "1-square": 63385, - "2-circle-1": 63386, - "2-circle-fill-1": 63387, "2-circle-fill": 63388, "2-circle": 63389, "2-square-fill": 63390, "2-square": 63391, - "3-circle-1": 63392, - "3-circle-fill-1": 63393, "3-circle-fill": 63394, "3-circle": 63395, "3-square-fill": 63396, "3-square": 63397, - "4-circle-1": 63398, - "4-circle-fill-1": 63399, "4-circle-fill": 63400, "4-circle": 63401, "4-square-fill": 63402, "4-square": 63403, - "5-circle-1": 63404, - "5-circle-fill-1": 63405, "5-circle-fill": 63406, "5-circle": 63407, "5-square-fill": 63408, "5-square": 63409, - "6-circle-1": 63410, - "6-circle-fill-1": 63411, "6-circle-fill": 63412, "6-circle": 63413, "6-square-fill": 63414, "6-square": 63415, - "7-circle-1": 63416, - "7-circle-fill-1": 63417, "7-circle-fill": 63418, "7-circle": 63419, "7-square-fill": 63420, "7-square": 63421, - "8-circle-1": 63422, - "8-circle-fill-1": 63423, "8-circle-fill": 63424, "8-circle": 63425, "8-square-fill": 63426, "8-square": 63427, - "9-circle-1": 63428, - "9-circle-fill-1": 63429, "9-circle-fill": 63430, "9-circle": 63431, "9-square-fill": 63432, @@ -1750,8 +1717,6 @@ "browser-edge": 63445, "browser-firefox": 63446, "browser-safari": 63447, - "c-circle-1": 63448, - "c-circle-fill-1": 63449, "c-circle-fill": 63450, "c-circle": 63451, "c-square-fill": 63452, @@ -1762,8 +1727,6 @@ "car-front": 63457, "cassette-fill": 63458, "cassette": 63459, - "cc-circle-1": 63460, - "cc-circle-fill-1": 63461, "cc-circle-fill": 63462, "cc-circle": 63463, "cc-square-fill": 63464, @@ -1782,8 +1745,6 @@ "filetype-sql": 63477, "fire": 63478, "google-play": 63479, - "h-circle-1": 63480, - "h-circle-fill-1": 63481, "h-circle-fill": 63482, "h-circle": 63483, "h-square-fill": 63484, @@ -1792,8 +1753,6 @@ "lungs-fill": 63487, "lungs": 63488, "microsoft-teams": 63489, - "p-circle-1": 63490, - "p-circle-fill-1": 63491, "p-circle-fill": 63492, "p-circle": 63493, "p-square-fill": 63494, @@ -1802,8 +1761,6 @@ "pass": 63497, "prescription": 63498, "prescription2": 63499, - "r-circle-1": 63500, - "r-circle-fill-1": 63501, "r-circle-fill": 63502, "r-circle": 63503, "r-square-fill": 63504, @@ -1852,5 +1809,244 @@ "fuel-pump-diesel-fill": 63547, "fuel-pump-diesel": 63548, "fuel-pump-fill": 63549, - "fuel-pump": 63550 + "fuel-pump": 63550, + "0-circle-fill": 63551, + "0-circle": 63552, + "0-square-fill": 63553, + "0-square": 63554, + "rocket-fill": 63555, + "rocket-takeoff-fill": 63556, + "rocket-takeoff": 63557, + "rocket": 63558, + "stripe": 63559, + "subscript": 63560, + "superscript": 63561, + "trello": 63562, + "envelope-at-fill": 63563, + "envelope-at": 63564, + "regex": 63565, + "text-wrap": 63566, + "sign-dead-end-fill": 63567, + "sign-dead-end": 63568, + "sign-do-not-enter-fill": 63569, + "sign-do-not-enter": 63570, + "sign-intersection-fill": 63571, + "sign-intersection-side-fill": 63572, + "sign-intersection-side": 63573, + "sign-intersection-t-fill": 63574, + "sign-intersection-t": 63575, + "sign-intersection-y-fill": 63576, + "sign-intersection-y": 63577, + "sign-intersection": 63578, + "sign-merge-left-fill": 63579, + "sign-merge-left": 63580, + "sign-merge-right-fill": 63581, + "sign-merge-right": 63582, + "sign-no-left-turn-fill": 63583, + "sign-no-left-turn": 63584, + "sign-no-parking-fill": 63585, + "sign-no-parking": 63586, + "sign-no-right-turn-fill": 63587, + "sign-no-right-turn": 63588, + "sign-railroad-fill": 63589, + "sign-railroad": 63590, + "building-add": 63591, + "building-check": 63592, + "building-dash": 63593, + "building-down": 63594, + "building-exclamation": 63595, + "building-fill-add": 63596, + "building-fill-check": 63597, + "building-fill-dash": 63598, + "building-fill-down": 63599, + "building-fill-exclamation": 63600, + "building-fill-gear": 63601, + "building-fill-lock": 63602, + "building-fill-slash": 63603, + "building-fill-up": 63604, + "building-fill-x": 63605, + "building-fill": 63606, + "building-gear": 63607, + "building-lock": 63608, + "building-slash": 63609, + "building-up": 63610, + "building-x": 63611, + "buildings-fill": 63612, + "buildings": 63613, + "bus-front-fill": 63614, + "bus-front": 63615, + "ev-front-fill": 63616, + "ev-front": 63617, + "globe-americas": 63618, + "globe-asia-australia": 63619, + "globe-central-south-asia": 63620, + "globe-europe-africa": 63621, + "house-add-fill": 63622, + "house-add": 63623, + "house-check-fill": 63624, + "house-check": 63625, + "house-dash-fill": 63626, + "house-dash": 63627, + "house-down-fill": 63628, + "house-down": 63629, + "house-exclamation-fill": 63630, + "house-exclamation": 63631, + "house-gear-fill": 63632, + "house-gear": 63633, + "house-lock-fill": 63634, + "house-lock": 63635, + "house-slash-fill": 63636, + "house-slash": 63637, + "house-up-fill": 63638, + "house-up": 63639, + "house-x-fill": 63640, + "house-x": 63641, + "person-add": 63642, + "person-down": 63643, + "person-exclamation": 63644, + "person-fill-add": 63645, + "person-fill-check": 63646, + "person-fill-dash": 63647, + "person-fill-down": 63648, + "person-fill-exclamation": 63649, + "person-fill-gear": 63650, + "person-fill-lock": 63651, + "person-fill-slash": 63652, + "person-fill-up": 63653, + "person-fill-x": 63654, + "person-gear": 63655, + "person-lock": 63656, + "person-slash": 63657, + "person-up": 63658, + "scooter": 63659, + "taxi-front-fill": 63660, + "taxi-front": 63661, + "amd": 63662, + "database-add": 63663, + "database-check": 63664, + "database-dash": 63665, + "database-down": 63666, + "database-exclamation": 63667, + "database-fill-add": 63668, + "database-fill-check": 63669, + "database-fill-dash": 63670, + "database-fill-down": 63671, + "database-fill-exclamation": 63672, + "database-fill-gear": 63673, + "database-fill-lock": 63674, + "database-fill-slash": 63675, + "database-fill-up": 63676, + "database-fill-x": 63677, + "database-fill": 63678, + "database-gear": 63679, + "database-lock": 63680, + "database-slash": 63681, + "database-up": 63682, + "database-x": 63683, + "database": 63684, + "houses-fill": 63685, + "houses": 63686, + "nvidia": 63687, + "person-vcard-fill": 63688, + "person-vcard": 63689, + "sina-weibo": 63690, + "tencent-qq": 63691, + "wikipedia": 63692, + "alphabet-uppercase": 62117, + "alphabet": 63114, + "amazon": 63117, + "arrows-collapse-vertical": 63120, + "arrows-expand-vertical": 63125, + "arrows-vertical": 63128, + "arrows": 63138, + "ban-fill": 63139, + "ban": 63158, + "bing": 63170, + "cake": 63200, + "cake2": 63213, + "cookie": 63214, + "copy": 63321, + "crosshair": 63337, + "crosshair2": 63380, + "emoji-astonished-fill": 63381, + "emoji-astonished": 63386, + "emoji-grimace-fill": 63387, + "emoji-grimace": 63392, + "emoji-grin-fill": 63393, + "emoji-grin": 63398, + "emoji-surprise-fill": 63399, + "emoji-surprise": 63404, + "emoji-tear-fill": 63405, + "emoji-tear": 63410, + "envelope-arrow-down-fill": 63411, + "envelope-arrow-down": 63416, + "envelope-arrow-up-fill": 63417, + "envelope-arrow-up": 63422, + "feather": 63423, + "feather2": 63428, + "floppy-fill": 63429, + "floppy": 63448, + "floppy2-fill": 63449, + "floppy2": 63460, + "gitlab": 63461, + "highlighter": 63480, + "marker-tip": 63490, + "nvme-fill": 63491, + "nvme": 63500, + "opencollective": 63501, + "pci-card-network": 63693, + "pci-card-sound": 63694, + "radar": 63695, + "send-arrow-down-fill": 63696, + "send-arrow-down": 63697, + "send-arrow-up-fill": 63698, + "send-arrow-up": 63699, + "sim-slash-fill": 63700, + "sim-slash": 63701, + "sourceforge": 63702, + "substack": 63703, + "threads-fill": 63704, + "threads": 63705, + "transparency": 63706, + "twitter-x": 63707, + "type-h4": 63708, + "type-h5": 63709, + "type-h6": 63710, + "backpack-fill": 63711, + "backpack": 63712, + "backpack2-fill": 63713, + "backpack2": 63714, + "backpack3-fill": 63715, + "backpack3": 63716, + "backpack4-fill": 63717, + "backpack4": 63718, + "brilliance": 63719, + "cake-fill": 63720, + "cake2-fill": 63721, + "duffle-fill": 63722, + "duffle": 63723, + "exposure": 63724, + "gender-neuter": 63725, + "highlights": 63726, + "luggage-fill": 63727, + "luggage": 63728, + "mailbox-flag": 63729, + "mailbox2-flag": 63730, + "noise-reduction": 63731, + "passport-fill": 63732, + "passport": 63733, + "person-arms-up": 63734, + "person-raised-hand": 63735, + "person-standing-dress": 63736, + "person-standing": 63737, + "person-walking": 63738, + "person-wheelchair": 63739, + "shadows": 63740, + "suitcase-fill": 63741, + "suitcase-lg-fill": 63742, + "suitcase-lg": 63743, + "suitcase": 63744, + "suitcase2-fill": 63745, + "suitcase2": 63746, + "vignette": 63747 } \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.min.css b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.min.css index 617cbc561..dadd6dcac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.min.css +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.min.css @@ -1,8 +1,5 @@ -/** - * Minified by jsDelivr using clean-css v5.3.0. - * Original file: /npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css - * - * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files - */ -@font-face{font-display:block;font-family:bootstrap-icons;src:url("fonts/bootstrap-icons.woff2?8d200481aa7f02a2d63a331fc782cfaf") format("woff2"),url("fonts/bootstrap-icons.woff?8d200481aa7f02a2d63a331fc782cfaf") format("woff")}.bi::before,[class*=" bi-"]::before,[class^=bi-]::before{display:inline-block;font-family:bootstrap-icons!important;font-style:normal;font-weight:400!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.bi-123::before{content:"\f67f"}.bi-alarm-fill::before{content:"\f101"}.bi-alarm::before{content:"\f102"}.bi-align-bottom::before{content:"\f103"}.bi-align-center::before{content:"\f104"}.bi-align-end::before{content:"\f105"}.bi-align-middle::before{content:"\f106"}.bi-align-start::before{content:"\f107"}.bi-align-top::before{content:"\f108"}.bi-alt::before{content:"\f109"}.bi-app-indicator::before{content:"\f10a"}.bi-app::before{content:"\f10b"}.bi-archive-fill::before{content:"\f10c"}.bi-archive::before{content:"\f10d"}.bi-arrow-90deg-down::before{content:"\f10e"}.bi-arrow-90deg-left::before{content:"\f10f"}.bi-arrow-90deg-right::before{content:"\f110"}.bi-arrow-90deg-up::before{content:"\f111"}.bi-arrow-bar-down::before{content:"\f112"}.bi-arrow-bar-left::before{content:"\f113"}.bi-arrow-bar-right::before{content:"\f114"}.bi-arrow-bar-up::before{content:"\f115"}.bi-arrow-clockwise::before{content:"\f116"}.bi-arrow-counterclockwise::before{content:"\f117"}.bi-arrow-down-circle-fill::before{content:"\f118"}.bi-arrow-down-circle::before{content:"\f119"}.bi-arrow-down-left-circle-fill::before{content:"\f11a"}.bi-arrow-down-left-circle::before{content:"\f11b"}.bi-arrow-down-left-square-fill::before{content:"\f11c"}.bi-arrow-down-left-square::before{content:"\f11d"}.bi-arrow-down-left::before{content:"\f11e"}.bi-arrow-down-right-circle-fill::before{content:"\f11f"}.bi-arrow-down-right-circle::before{content:"\f120"}.bi-arrow-down-right-square-fill::before{content:"\f121"}.bi-arrow-down-right-square::before{content:"\f122"}.bi-arrow-down-right::before{content:"\f123"}.bi-arrow-down-short::before{content:"\f124"}.bi-arrow-down-square-fill::before{content:"\f125"}.bi-arrow-down-square::before{content:"\f126"}.bi-arrow-down-up::before{content:"\f127"}.bi-arrow-down::before{content:"\f128"}.bi-arrow-left-circle-fill::before{content:"\f129"}.bi-arrow-left-circle::before{content:"\f12a"}.bi-arrow-left-right::before{content:"\f12b"}.bi-arrow-left-short::before{content:"\f12c"}.bi-arrow-left-square-fill::before{content:"\f12d"}.bi-arrow-left-square::before{content:"\f12e"}.bi-arrow-left::before{content:"\f12f"}.bi-arrow-repeat::before{content:"\f130"}.bi-arrow-return-left::before{content:"\f131"}.bi-arrow-return-right::before{content:"\f132"}.bi-arrow-right-circle-fill::before{content:"\f133"}.bi-arrow-right-circle::before{content:"\f134"}.bi-arrow-right-short::before{content:"\f135"}.bi-arrow-right-square-fill::before{content:"\f136"}.bi-arrow-right-square::before{content:"\f137"}.bi-arrow-right::before{content:"\f138"}.bi-arrow-up-circle-fill::before{content:"\f139"}.bi-arrow-up-circle::before{content:"\f13a"}.bi-arrow-up-left-circle-fill::before{content:"\f13b"}.bi-arrow-up-left-circle::before{content:"\f13c"}.bi-arrow-up-left-square-fill::before{content:"\f13d"}.bi-arrow-up-left-square::before{content:"\f13e"}.bi-arrow-up-left::before{content:"\f13f"}.bi-arrow-up-right-circle-fill::before{content:"\f140"}.bi-arrow-up-right-circle::before{content:"\f141"}.bi-arrow-up-right-square-fill::before{content:"\f142"}.bi-arrow-up-right-square::before{content:"\f143"}.bi-arrow-up-right::before{content:"\f144"}.bi-arrow-up-short::before{content:"\f145"}.bi-arrow-up-square-fill::before{content:"\f146"}.bi-arrow-up-square::before{content:"\f147"}.bi-arrow-up::before{content:"\f148"}.bi-arrows-angle-contract::before{content:"\f149"}.bi-arrows-angle-expand::before{content:"\f14a"}.bi-arrows-collapse::before{content:"\f14b"}.bi-arrows-expand::before{content:"\f14c"}.bi-arrows-fullscreen::before{content:"\f14d"}.bi-arrows-move::before{content:"\f14e"}.bi-aspect-ratio-fill::before{content:"\f14f"}.bi-aspect-ratio::before{content:"\f150"}.bi-asterisk::before{content:"\f151"}.bi-at::before{content:"\f152"}.bi-award-fill::before{content:"\f153"}.bi-award::before{content:"\f154"}.bi-back::before{content:"\f155"}.bi-backspace-fill::before{content:"\f156"}.bi-backspace-reverse-fill::before{content:"\f157"}.bi-backspace-reverse::before{content:"\f158"}.bi-backspace::before{content:"\f159"}.bi-badge-3d-fill::before{content:"\f15a"}.bi-badge-3d::before{content:"\f15b"}.bi-badge-4k-fill::before{content:"\f15c"}.bi-badge-4k::before{content:"\f15d"}.bi-badge-8k-fill::before{content:"\f15e"}.bi-badge-8k::before{content:"\f15f"}.bi-badge-ad-fill::before{content:"\f160"}.bi-badge-ad::before{content:"\f161"}.bi-badge-ar-fill::before{content:"\f162"}.bi-badge-ar::before{content:"\f163"}.bi-badge-cc-fill::before{content:"\f164"}.bi-badge-cc::before{content:"\f165"}.bi-badge-hd-fill::before{content:"\f166"}.bi-badge-hd::before{content:"\f167"}.bi-badge-tm-fill::before{content:"\f168"}.bi-badge-tm::before{content:"\f169"}.bi-badge-vo-fill::before{content:"\f16a"}.bi-badge-vo::before{content:"\f16b"}.bi-badge-vr-fill::before{content:"\f16c"}.bi-badge-vr::before{content:"\f16d"}.bi-badge-wc-fill::before{content:"\f16e"}.bi-badge-wc::before{content:"\f16f"}.bi-bag-check-fill::before{content:"\f170"}.bi-bag-check::before{content:"\f171"}.bi-bag-dash-fill::before{content:"\f172"}.bi-bag-dash::before{content:"\f173"}.bi-bag-fill::before{content:"\f174"}.bi-bag-plus-fill::before{content:"\f175"}.bi-bag-plus::before{content:"\f176"}.bi-bag-x-fill::before{content:"\f177"}.bi-bag-x::before{content:"\f178"}.bi-bag::before{content:"\f179"}.bi-bar-chart-fill::before{content:"\f17a"}.bi-bar-chart-line-fill::before{content:"\f17b"}.bi-bar-chart-line::before{content:"\f17c"}.bi-bar-chart-steps::before{content:"\f17d"}.bi-bar-chart::before{content:"\f17e"}.bi-basket-fill::before{content:"\f17f"}.bi-basket::before{content:"\f180"}.bi-basket2-fill::before{content:"\f181"}.bi-basket2::before{content:"\f182"}.bi-basket3-fill::before{content:"\f183"}.bi-basket3::before{content:"\f184"}.bi-battery-charging::before{content:"\f185"}.bi-battery-full::before{content:"\f186"}.bi-battery-half::before{content:"\f187"}.bi-battery::before{content:"\f188"}.bi-bell-fill::before{content:"\f189"}.bi-bell::before{content:"\f18a"}.bi-bezier::before{content:"\f18b"}.bi-bezier2::before{content:"\f18c"}.bi-bicycle::before{content:"\f18d"}.bi-binoculars-fill::before{content:"\f18e"}.bi-binoculars::before{content:"\f18f"}.bi-blockquote-left::before{content:"\f190"}.bi-blockquote-right::before{content:"\f191"}.bi-book-fill::before{content:"\f192"}.bi-book-half::before{content:"\f193"}.bi-book::before{content:"\f194"}.bi-bookmark-check-fill::before{content:"\f195"}.bi-bookmark-check::before{content:"\f196"}.bi-bookmark-dash-fill::before{content:"\f197"}.bi-bookmark-dash::before{content:"\f198"}.bi-bookmark-fill::before{content:"\f199"}.bi-bookmark-heart-fill::before{content:"\f19a"}.bi-bookmark-heart::before{content:"\f19b"}.bi-bookmark-plus-fill::before{content:"\f19c"}.bi-bookmark-plus::before{content:"\f19d"}.bi-bookmark-star-fill::before{content:"\f19e"}.bi-bookmark-star::before{content:"\f19f"}.bi-bookmark-x-fill::before{content:"\f1a0"}.bi-bookmark-x::before{content:"\f1a1"}.bi-bookmark::before{content:"\f1a2"}.bi-bookmarks-fill::before{content:"\f1a3"}.bi-bookmarks::before{content:"\f1a4"}.bi-bookshelf::before{content:"\f1a5"}.bi-bootstrap-fill::before{content:"\f1a6"}.bi-bootstrap-reboot::before{content:"\f1a7"}.bi-bootstrap::before{content:"\f1a8"}.bi-border-all::before{content:"\f1a9"}.bi-border-bottom::before{content:"\f1aa"}.bi-border-center::before{content:"\f1ab"}.bi-border-inner::before{content:"\f1ac"}.bi-border-left::before{content:"\f1ad"}.bi-border-middle::before{content:"\f1ae"}.bi-border-outer::before{content:"\f1af"}.bi-border-right::before{content:"\f1b0"}.bi-border-style::before{content:"\f1b1"}.bi-border-top::before{content:"\f1b2"}.bi-border-width::before{content:"\f1b3"}.bi-border::before{content:"\f1b4"}.bi-bounding-box-circles::before{content:"\f1b5"}.bi-bounding-box::before{content:"\f1b6"}.bi-box-arrow-down-left::before{content:"\f1b7"}.bi-box-arrow-down-right::before{content:"\f1b8"}.bi-box-arrow-down::before{content:"\f1b9"}.bi-box-arrow-in-down-left::before{content:"\f1ba"}.bi-box-arrow-in-down-right::before{content:"\f1bb"}.bi-box-arrow-in-down::before{content:"\f1bc"}.bi-box-arrow-in-left::before{content:"\f1bd"}.bi-box-arrow-in-right::before{content:"\f1be"}.bi-box-arrow-in-up-left::before{content:"\f1bf"}.bi-box-arrow-in-up-right::before{content:"\f1c0"}.bi-box-arrow-in-up::before{content:"\f1c1"}.bi-box-arrow-left::before{content:"\f1c2"}.bi-box-arrow-right::before{content:"\f1c3"}.bi-box-arrow-up-left::before{content:"\f1c4"}.bi-box-arrow-up-right::before{content:"\f1c5"}.bi-box-arrow-up::before{content:"\f1c6"}.bi-box-seam::before{content:"\f1c7"}.bi-box::before{content:"\f1c8"}.bi-braces::before{content:"\f1c9"}.bi-bricks::before{content:"\f1ca"}.bi-briefcase-fill::before{content:"\f1cb"}.bi-briefcase::before{content:"\f1cc"}.bi-brightness-alt-high-fill::before{content:"\f1cd"}.bi-brightness-alt-high::before{content:"\f1ce"}.bi-brightness-alt-low-fill::before{content:"\f1cf"}.bi-brightness-alt-low::before{content:"\f1d0"}.bi-brightness-high-fill::before{content:"\f1d1"}.bi-brightness-high::before{content:"\f1d2"}.bi-brightness-low-fill::before{content:"\f1d3"}.bi-brightness-low::before{content:"\f1d4"}.bi-broadcast-pin::before{content:"\f1d5"}.bi-broadcast::before{content:"\f1d6"}.bi-brush-fill::before{content:"\f1d7"}.bi-brush::before{content:"\f1d8"}.bi-bucket-fill::before{content:"\f1d9"}.bi-bucket::before{content:"\f1da"}.bi-bug-fill::before{content:"\f1db"}.bi-bug::before{content:"\f1dc"}.bi-building::before{content:"\f1dd"}.bi-bullseye::before{content:"\f1de"}.bi-calculator-fill::before{content:"\f1df"}.bi-calculator::before{content:"\f1e0"}.bi-calendar-check-fill::before{content:"\f1e1"}.bi-calendar-check::before{content:"\f1e2"}.bi-calendar-date-fill::before{content:"\f1e3"}.bi-calendar-date::before{content:"\f1e4"}.bi-calendar-day-fill::before{content:"\f1e5"}.bi-calendar-day::before{content:"\f1e6"}.bi-calendar-event-fill::before{content:"\f1e7"}.bi-calendar-event::before{content:"\f1e8"}.bi-calendar-fill::before{content:"\f1e9"}.bi-calendar-minus-fill::before{content:"\f1ea"}.bi-calendar-minus::before{content:"\f1eb"}.bi-calendar-month-fill::before{content:"\f1ec"}.bi-calendar-month::before{content:"\f1ed"}.bi-calendar-plus-fill::before{content:"\f1ee"}.bi-calendar-plus::before{content:"\f1ef"}.bi-calendar-range-fill::before{content:"\f1f0"}.bi-calendar-range::before{content:"\f1f1"}.bi-calendar-week-fill::before{content:"\f1f2"}.bi-calendar-week::before{content:"\f1f3"}.bi-calendar-x-fill::before{content:"\f1f4"}.bi-calendar-x::before{content:"\f1f5"}.bi-calendar::before{content:"\f1f6"}.bi-calendar2-check-fill::before{content:"\f1f7"}.bi-calendar2-check::before{content:"\f1f8"}.bi-calendar2-date-fill::before{content:"\f1f9"}.bi-calendar2-date::before{content:"\f1fa"}.bi-calendar2-day-fill::before{content:"\f1fb"}.bi-calendar2-day::before{content:"\f1fc"}.bi-calendar2-event-fill::before{content:"\f1fd"}.bi-calendar2-event::before{content:"\f1fe"}.bi-calendar2-fill::before{content:"\f1ff"}.bi-calendar2-minus-fill::before{content:"\f200"}.bi-calendar2-minus::before{content:"\f201"}.bi-calendar2-month-fill::before{content:"\f202"}.bi-calendar2-month::before{content:"\f203"}.bi-calendar2-plus-fill::before{content:"\f204"}.bi-calendar2-plus::before{content:"\f205"}.bi-calendar2-range-fill::before{content:"\f206"}.bi-calendar2-range::before{content:"\f207"}.bi-calendar2-week-fill::before{content:"\f208"}.bi-calendar2-week::before{content:"\f209"}.bi-calendar2-x-fill::before{content:"\f20a"}.bi-calendar2-x::before{content:"\f20b"}.bi-calendar2::before{content:"\f20c"}.bi-calendar3-event-fill::before{content:"\f20d"}.bi-calendar3-event::before{content:"\f20e"}.bi-calendar3-fill::before{content:"\f20f"}.bi-calendar3-range-fill::before{content:"\f210"}.bi-calendar3-range::before{content:"\f211"}.bi-calendar3-week-fill::before{content:"\f212"}.bi-calendar3-week::before{content:"\f213"}.bi-calendar3::before{content:"\f214"}.bi-calendar4-event::before{content:"\f215"}.bi-calendar4-range::before{content:"\f216"}.bi-calendar4-week::before{content:"\f217"}.bi-calendar4::before{content:"\f218"}.bi-camera-fill::before{content:"\f219"}.bi-camera-reels-fill::before{content:"\f21a"}.bi-camera-reels::before{content:"\f21b"}.bi-camera-video-fill::before{content:"\f21c"}.bi-camera-video-off-fill::before{content:"\f21d"}.bi-camera-video-off::before{content:"\f21e"}.bi-camera-video::before{content:"\f21f"}.bi-camera::before{content:"\f220"}.bi-camera2::before{content:"\f221"}.bi-capslock-fill::before{content:"\f222"}.bi-capslock::before{content:"\f223"}.bi-card-checklist::before{content:"\f224"}.bi-card-heading::before{content:"\f225"}.bi-card-image::before{content:"\f226"}.bi-card-list::before{content:"\f227"}.bi-card-text::before{content:"\f228"}.bi-caret-down-fill::before{content:"\f229"}.bi-caret-down-square-fill::before{content:"\f22a"}.bi-caret-down-square::before{content:"\f22b"}.bi-caret-down::before{content:"\f22c"}.bi-caret-left-fill::before{content:"\f22d"}.bi-caret-left-square-fill::before{content:"\f22e"}.bi-caret-left-square::before{content:"\f22f"}.bi-caret-left::before{content:"\f230"}.bi-caret-right-fill::before{content:"\f231"}.bi-caret-right-square-fill::before{content:"\f232"}.bi-caret-right-square::before{content:"\f233"}.bi-caret-right::before{content:"\f234"}.bi-caret-up-fill::before{content:"\f235"}.bi-caret-up-square-fill::before{content:"\f236"}.bi-caret-up-square::before{content:"\f237"}.bi-caret-up::before{content:"\f238"}.bi-cart-check-fill::before{content:"\f239"}.bi-cart-check::before{content:"\f23a"}.bi-cart-dash-fill::before{content:"\f23b"}.bi-cart-dash::before{content:"\f23c"}.bi-cart-fill::before{content:"\f23d"}.bi-cart-plus-fill::before{content:"\f23e"}.bi-cart-plus::before{content:"\f23f"}.bi-cart-x-fill::before{content:"\f240"}.bi-cart-x::before{content:"\f241"}.bi-cart::before{content:"\f242"}.bi-cart2::before{content:"\f243"}.bi-cart3::before{content:"\f244"}.bi-cart4::before{content:"\f245"}.bi-cash-stack::before{content:"\f246"}.bi-cash::before{content:"\f247"}.bi-cast::before{content:"\f248"}.bi-chat-dots-fill::before{content:"\f249"}.bi-chat-dots::before{content:"\f24a"}.bi-chat-fill::before{content:"\f24b"}.bi-chat-left-dots-fill::before{content:"\f24c"}.bi-chat-left-dots::before{content:"\f24d"}.bi-chat-left-fill::before{content:"\f24e"}.bi-chat-left-quote-fill::before{content:"\f24f"}.bi-chat-left-quote::before{content:"\f250"}.bi-chat-left-text-fill::before{content:"\f251"}.bi-chat-left-text::before{content:"\f252"}.bi-chat-left::before{content:"\f253"}.bi-chat-quote-fill::before{content:"\f254"}.bi-chat-quote::before{content:"\f255"}.bi-chat-right-dots-fill::before{content:"\f256"}.bi-chat-right-dots::before{content:"\f257"}.bi-chat-right-fill::before{content:"\f258"}.bi-chat-right-quote-fill::before{content:"\f259"}.bi-chat-right-quote::before{content:"\f25a"}.bi-chat-right-text-fill::before{content:"\f25b"}.bi-chat-right-text::before{content:"\f25c"}.bi-chat-right::before{content:"\f25d"}.bi-chat-square-dots-fill::before{content:"\f25e"}.bi-chat-square-dots::before{content:"\f25f"}.bi-chat-square-fill::before{content:"\f260"}.bi-chat-square-quote-fill::before{content:"\f261"}.bi-chat-square-quote::before{content:"\f262"}.bi-chat-square-text-fill::before{content:"\f263"}.bi-chat-square-text::before{content:"\f264"}.bi-chat-square::before{content:"\f265"}.bi-chat-text-fill::before{content:"\f266"}.bi-chat-text::before{content:"\f267"}.bi-chat::before{content:"\f268"}.bi-check-all::before{content:"\f269"}.bi-check-circle-fill::before{content:"\f26a"}.bi-check-circle::before{content:"\f26b"}.bi-check-square-fill::before{content:"\f26c"}.bi-check-square::before{content:"\f26d"}.bi-check::before{content:"\f26e"}.bi-check2-all::before{content:"\f26f"}.bi-check2-circle::before{content:"\f270"}.bi-check2-square::before{content:"\f271"}.bi-check2::before{content:"\f272"}.bi-chevron-bar-contract::before{content:"\f273"}.bi-chevron-bar-down::before{content:"\f274"}.bi-chevron-bar-expand::before{content:"\f275"}.bi-chevron-bar-left::before{content:"\f276"}.bi-chevron-bar-right::before{content:"\f277"}.bi-chevron-bar-up::before{content:"\f278"}.bi-chevron-compact-down::before{content:"\f279"}.bi-chevron-compact-left::before{content:"\f27a"}.bi-chevron-compact-right::before{content:"\f27b"}.bi-chevron-compact-up::before{content:"\f27c"}.bi-chevron-contract::before{content:"\f27d"}.bi-chevron-double-down::before{content:"\f27e"}.bi-chevron-double-left::before{content:"\f27f"}.bi-chevron-double-right::before{content:"\f280"}.bi-chevron-double-up::before{content:"\f281"}.bi-chevron-down::before{content:"\f282"}.bi-chevron-expand::before{content:"\f283"}.bi-chevron-left::before{content:"\f284"}.bi-chevron-right::before{content:"\f285"}.bi-chevron-up::before{content:"\f286"}.bi-circle-fill::before{content:"\f287"}.bi-circle-half::before{content:"\f288"}.bi-circle-square::before{content:"\f289"}.bi-circle::before{content:"\f28a"}.bi-clipboard-check::before{content:"\f28b"}.bi-clipboard-data::before{content:"\f28c"}.bi-clipboard-minus::before{content:"\f28d"}.bi-clipboard-plus::before{content:"\f28e"}.bi-clipboard-x::before{content:"\f28f"}.bi-clipboard::before{content:"\f290"}.bi-clock-fill::before{content:"\f291"}.bi-clock-history::before{content:"\f292"}.bi-clock::before{content:"\f293"}.bi-cloud-arrow-down-fill::before{content:"\f294"}.bi-cloud-arrow-down::before{content:"\f295"}.bi-cloud-arrow-up-fill::before{content:"\f296"}.bi-cloud-arrow-up::before{content:"\f297"}.bi-cloud-check-fill::before{content:"\f298"}.bi-cloud-check::before{content:"\f299"}.bi-cloud-download-fill::before{content:"\f29a"}.bi-cloud-download::before{content:"\f29b"}.bi-cloud-drizzle-fill::before{content:"\f29c"}.bi-cloud-drizzle::before{content:"\f29d"}.bi-cloud-fill::before{content:"\f29e"}.bi-cloud-fog-fill::before{content:"\f29f"}.bi-cloud-fog::before{content:"\f2a0"}.bi-cloud-fog2-fill::before{content:"\f2a1"}.bi-cloud-fog2::before{content:"\f2a2"}.bi-cloud-hail-fill::before{content:"\f2a3"}.bi-cloud-hail::before{content:"\f2a4"}.bi-cloud-haze-1::before{content:"\f2a5"}.bi-cloud-haze-fill::before{content:"\f2a6"}.bi-cloud-haze::before{content:"\f2a7"}.bi-cloud-haze2-fill::before{content:"\f2a8"}.bi-cloud-lightning-fill::before{content:"\f2a9"}.bi-cloud-lightning-rain-fill::before{content:"\f2aa"}.bi-cloud-lightning-rain::before{content:"\f2ab"}.bi-cloud-lightning::before{content:"\f2ac"}.bi-cloud-minus-fill::before{content:"\f2ad"}.bi-cloud-minus::before{content:"\f2ae"}.bi-cloud-moon-fill::before{content:"\f2af"}.bi-cloud-moon::before{content:"\f2b0"}.bi-cloud-plus-fill::before{content:"\f2b1"}.bi-cloud-plus::before{content:"\f2b2"}.bi-cloud-rain-fill::before{content:"\f2b3"}.bi-cloud-rain-heavy-fill::before{content:"\f2b4"}.bi-cloud-rain-heavy::before{content:"\f2b5"}.bi-cloud-rain::before{content:"\f2b6"}.bi-cloud-slash-fill::before{content:"\f2b7"}.bi-cloud-slash::before{content:"\f2b8"}.bi-cloud-sleet-fill::before{content:"\f2b9"}.bi-cloud-sleet::before{content:"\f2ba"}.bi-cloud-snow-fill::before{content:"\f2bb"}.bi-cloud-snow::before{content:"\f2bc"}.bi-cloud-sun-fill::before{content:"\f2bd"}.bi-cloud-sun::before{content:"\f2be"}.bi-cloud-upload-fill::before{content:"\f2bf"}.bi-cloud-upload::before{content:"\f2c0"}.bi-cloud::before{content:"\f2c1"}.bi-clouds-fill::before{content:"\f2c2"}.bi-clouds::before{content:"\f2c3"}.bi-cloudy-fill::before{content:"\f2c4"}.bi-cloudy::before{content:"\f2c5"}.bi-code-slash::before{content:"\f2c6"}.bi-code-square::before{content:"\f2c7"}.bi-code::before{content:"\f2c8"}.bi-collection-fill::before{content:"\f2c9"}.bi-collection-play-fill::before{content:"\f2ca"}.bi-collection-play::before{content:"\f2cb"}.bi-collection::before{content:"\f2cc"}.bi-columns-gap::before{content:"\f2cd"}.bi-columns::before{content:"\f2ce"}.bi-command::before{content:"\f2cf"}.bi-compass-fill::before{content:"\f2d0"}.bi-compass::before{content:"\f2d1"}.bi-cone-striped::before{content:"\f2d2"}.bi-cone::before{content:"\f2d3"}.bi-controller::before{content:"\f2d4"}.bi-cpu-fill::before{content:"\f2d5"}.bi-cpu::before{content:"\f2d6"}.bi-credit-card-2-back-fill::before{content:"\f2d7"}.bi-credit-card-2-back::before{content:"\f2d8"}.bi-credit-card-2-front-fill::before{content:"\f2d9"}.bi-credit-card-2-front::before{content:"\f2da"}.bi-credit-card-fill::before{content:"\f2db"}.bi-credit-card::before{content:"\f2dc"}.bi-crop::before{content:"\f2dd"}.bi-cup-fill::before{content:"\f2de"}.bi-cup-straw::before{content:"\f2df"}.bi-cup::before{content:"\f2e0"}.bi-cursor-fill::before{content:"\f2e1"}.bi-cursor-text::before{content:"\f2e2"}.bi-cursor::before{content:"\f2e3"}.bi-dash-circle-dotted::before{content:"\f2e4"}.bi-dash-circle-fill::before{content:"\f2e5"}.bi-dash-circle::before{content:"\f2e6"}.bi-dash-square-dotted::before{content:"\f2e7"}.bi-dash-square-fill::before{content:"\f2e8"}.bi-dash-square::before{content:"\f2e9"}.bi-dash::before{content:"\f2ea"}.bi-diagram-2-fill::before{content:"\f2eb"}.bi-diagram-2::before{content:"\f2ec"}.bi-diagram-3-fill::before{content:"\f2ed"}.bi-diagram-3::before{content:"\f2ee"}.bi-diamond-fill::before{content:"\f2ef"}.bi-diamond-half::before{content:"\f2f0"}.bi-diamond::before{content:"\f2f1"}.bi-dice-1-fill::before{content:"\f2f2"}.bi-dice-1::before{content:"\f2f3"}.bi-dice-2-fill::before{content:"\f2f4"}.bi-dice-2::before{content:"\f2f5"}.bi-dice-3-fill::before{content:"\f2f6"}.bi-dice-3::before{content:"\f2f7"}.bi-dice-4-fill::before{content:"\f2f8"}.bi-dice-4::before{content:"\f2f9"}.bi-dice-5-fill::before{content:"\f2fa"}.bi-dice-5::before{content:"\f2fb"}.bi-dice-6-fill::before{content:"\f2fc"}.bi-dice-6::before{content:"\f2fd"}.bi-disc-fill::before{content:"\f2fe"}.bi-disc::before{content:"\f2ff"}.bi-discord::before{content:"\f300"}.bi-display-fill::before{content:"\f301"}.bi-display::before{content:"\f302"}.bi-distribute-horizontal::before{content:"\f303"}.bi-distribute-vertical::before{content:"\f304"}.bi-door-closed-fill::before{content:"\f305"}.bi-door-closed::before{content:"\f306"}.bi-door-open-fill::before{content:"\f307"}.bi-door-open::before{content:"\f308"}.bi-dot::before{content:"\f309"}.bi-download::before{content:"\f30a"}.bi-droplet-fill::before{content:"\f30b"}.bi-droplet-half::before{content:"\f30c"}.bi-droplet::before{content:"\f30d"}.bi-earbuds::before{content:"\f30e"}.bi-easel-fill::before{content:"\f30f"}.bi-easel::before{content:"\f310"}.bi-egg-fill::before{content:"\f311"}.bi-egg-fried::before{content:"\f312"}.bi-egg::before{content:"\f313"}.bi-eject-fill::before{content:"\f314"}.bi-eject::before{content:"\f315"}.bi-emoji-angry-fill::before{content:"\f316"}.bi-emoji-angry::before{content:"\f317"}.bi-emoji-dizzy-fill::before{content:"\f318"}.bi-emoji-dizzy::before{content:"\f319"}.bi-emoji-expressionless-fill::before{content:"\f31a"}.bi-emoji-expressionless::before{content:"\f31b"}.bi-emoji-frown-fill::before{content:"\f31c"}.bi-emoji-frown::before{content:"\f31d"}.bi-emoji-heart-eyes-fill::before{content:"\f31e"}.bi-emoji-heart-eyes::before{content:"\f31f"}.bi-emoji-laughing-fill::before{content:"\f320"}.bi-emoji-laughing::before{content:"\f321"}.bi-emoji-neutral-fill::before{content:"\f322"}.bi-emoji-neutral::before{content:"\f323"}.bi-emoji-smile-fill::before{content:"\f324"}.bi-emoji-smile-upside-down-fill::before{content:"\f325"}.bi-emoji-smile-upside-down::before{content:"\f326"}.bi-emoji-smile::before{content:"\f327"}.bi-emoji-sunglasses-fill::before{content:"\f328"}.bi-emoji-sunglasses::before{content:"\f329"}.bi-emoji-wink-fill::before{content:"\f32a"}.bi-emoji-wink::before{content:"\f32b"}.bi-envelope-fill::before{content:"\f32c"}.bi-envelope-open-fill::before{content:"\f32d"}.bi-envelope-open::before{content:"\f32e"}.bi-envelope::before{content:"\f32f"}.bi-eraser-fill::before{content:"\f330"}.bi-eraser::before{content:"\f331"}.bi-exclamation-circle-fill::before{content:"\f332"}.bi-exclamation-circle::before{content:"\f333"}.bi-exclamation-diamond-fill::before{content:"\f334"}.bi-exclamation-diamond::before{content:"\f335"}.bi-exclamation-octagon-fill::before{content:"\f336"}.bi-exclamation-octagon::before{content:"\f337"}.bi-exclamation-square-fill::before{content:"\f338"}.bi-exclamation-square::before{content:"\f339"}.bi-exclamation-triangle-fill::before{content:"\f33a"}.bi-exclamation-triangle::before{content:"\f33b"}.bi-exclamation::before{content:"\f33c"}.bi-exclude::before{content:"\f33d"}.bi-eye-fill::before{content:"\f33e"}.bi-eye-slash-fill::before{content:"\f33f"}.bi-eye-slash::before{content:"\f340"}.bi-eye::before{content:"\f341"}.bi-eyedropper::before{content:"\f342"}.bi-eyeglasses::before{content:"\f343"}.bi-facebook::before{content:"\f344"}.bi-file-arrow-down-fill::before{content:"\f345"}.bi-file-arrow-down::before{content:"\f346"}.bi-file-arrow-up-fill::before{content:"\f347"}.bi-file-arrow-up::before{content:"\f348"}.bi-file-bar-graph-fill::before{content:"\f349"}.bi-file-bar-graph::before{content:"\f34a"}.bi-file-binary-fill::before{content:"\f34b"}.bi-file-binary::before{content:"\f34c"}.bi-file-break-fill::before{content:"\f34d"}.bi-file-break::before{content:"\f34e"}.bi-file-check-fill::before{content:"\f34f"}.bi-file-check::before{content:"\f350"}.bi-file-code-fill::before{content:"\f351"}.bi-file-code::before{content:"\f352"}.bi-file-diff-fill::before{content:"\f353"}.bi-file-diff::before{content:"\f354"}.bi-file-earmark-arrow-down-fill::before{content:"\f355"}.bi-file-earmark-arrow-down::before{content:"\f356"}.bi-file-earmark-arrow-up-fill::before{content:"\f357"}.bi-file-earmark-arrow-up::before{content:"\f358"}.bi-file-earmark-bar-graph-fill::before{content:"\f359"}.bi-file-earmark-bar-graph::before{content:"\f35a"}.bi-file-earmark-binary-fill::before{content:"\f35b"}.bi-file-earmark-binary::before{content:"\f35c"}.bi-file-earmark-break-fill::before{content:"\f35d"}.bi-file-earmark-break::before{content:"\f35e"}.bi-file-earmark-check-fill::before{content:"\f35f"}.bi-file-earmark-check::before{content:"\f360"}.bi-file-earmark-code-fill::before{content:"\f361"}.bi-file-earmark-code::before{content:"\f362"}.bi-file-earmark-diff-fill::before{content:"\f363"}.bi-file-earmark-diff::before{content:"\f364"}.bi-file-earmark-easel-fill::before{content:"\f365"}.bi-file-earmark-easel::before{content:"\f366"}.bi-file-earmark-excel-fill::before{content:"\f367"}.bi-file-earmark-excel::before{content:"\f368"}.bi-file-earmark-fill::before{content:"\f369"}.bi-file-earmark-font-fill::before{content:"\f36a"}.bi-file-earmark-font::before{content:"\f36b"}.bi-file-earmark-image-fill::before{content:"\f36c"}.bi-file-earmark-image::before{content:"\f36d"}.bi-file-earmark-lock-fill::before{content:"\f36e"}.bi-file-earmark-lock::before{content:"\f36f"}.bi-file-earmark-lock2-fill::before{content:"\f370"}.bi-file-earmark-lock2::before{content:"\f371"}.bi-file-earmark-medical-fill::before{content:"\f372"}.bi-file-earmark-medical::before{content:"\f373"}.bi-file-earmark-minus-fill::before{content:"\f374"}.bi-file-earmark-minus::before{content:"\f375"}.bi-file-earmark-music-fill::before{content:"\f376"}.bi-file-earmark-music::before{content:"\f377"}.bi-file-earmark-person-fill::before{content:"\f378"}.bi-file-earmark-person::before{content:"\f379"}.bi-file-earmark-play-fill::before{content:"\f37a"}.bi-file-earmark-play::before{content:"\f37b"}.bi-file-earmark-plus-fill::before{content:"\f37c"}.bi-file-earmark-plus::before{content:"\f37d"}.bi-file-earmark-post-fill::before{content:"\f37e"}.bi-file-earmark-post::before{content:"\f37f"}.bi-file-earmark-ppt-fill::before{content:"\f380"}.bi-file-earmark-ppt::before{content:"\f381"}.bi-file-earmark-richtext-fill::before{content:"\f382"}.bi-file-earmark-richtext::before{content:"\f383"}.bi-file-earmark-ruled-fill::before{content:"\f384"}.bi-file-earmark-ruled::before{content:"\f385"}.bi-file-earmark-slides-fill::before{content:"\f386"}.bi-file-earmark-slides::before{content:"\f387"}.bi-file-earmark-spreadsheet-fill::before{content:"\f388"}.bi-file-earmark-spreadsheet::before{content:"\f389"}.bi-file-earmark-text-fill::before{content:"\f38a"}.bi-file-earmark-text::before{content:"\f38b"}.bi-file-earmark-word-fill::before{content:"\f38c"}.bi-file-earmark-word::before{content:"\f38d"}.bi-file-earmark-x-fill::before{content:"\f38e"}.bi-file-earmark-x::before{content:"\f38f"}.bi-file-earmark-zip-fill::before{content:"\f390"}.bi-file-earmark-zip::before{content:"\f391"}.bi-file-earmark::before{content:"\f392"}.bi-file-easel-fill::before{content:"\f393"}.bi-file-easel::before{content:"\f394"}.bi-file-excel-fill::before{content:"\f395"}.bi-file-excel::before{content:"\f396"}.bi-file-fill::before{content:"\f397"}.bi-file-font-fill::before{content:"\f398"}.bi-file-font::before{content:"\f399"}.bi-file-image-fill::before{content:"\f39a"}.bi-file-image::before{content:"\f39b"}.bi-file-lock-fill::before{content:"\f39c"}.bi-file-lock::before{content:"\f39d"}.bi-file-lock2-fill::before{content:"\f39e"}.bi-file-lock2::before{content:"\f39f"}.bi-file-medical-fill::before{content:"\f3a0"}.bi-file-medical::before{content:"\f3a1"}.bi-file-minus-fill::before{content:"\f3a2"}.bi-file-minus::before{content:"\f3a3"}.bi-file-music-fill::before{content:"\f3a4"}.bi-file-music::before{content:"\f3a5"}.bi-file-person-fill::before{content:"\f3a6"}.bi-file-person::before{content:"\f3a7"}.bi-file-play-fill::before{content:"\f3a8"}.bi-file-play::before{content:"\f3a9"}.bi-file-plus-fill::before{content:"\f3aa"}.bi-file-plus::before{content:"\f3ab"}.bi-file-post-fill::before{content:"\f3ac"}.bi-file-post::before{content:"\f3ad"}.bi-file-ppt-fill::before{content:"\f3ae"}.bi-file-ppt::before{content:"\f3af"}.bi-file-richtext-fill::before{content:"\f3b0"}.bi-file-richtext::before{content:"\f3b1"}.bi-file-ruled-fill::before{content:"\f3b2"}.bi-file-ruled::before{content:"\f3b3"}.bi-file-slides-fill::before{content:"\f3b4"}.bi-file-slides::before{content:"\f3b5"}.bi-file-spreadsheet-fill::before{content:"\f3b6"}.bi-file-spreadsheet::before{content:"\f3b7"}.bi-file-text-fill::before{content:"\f3b8"}.bi-file-text::before{content:"\f3b9"}.bi-file-word-fill::before{content:"\f3ba"}.bi-file-word::before{content:"\f3bb"}.bi-file-x-fill::before{content:"\f3bc"}.bi-file-x::before{content:"\f3bd"}.bi-file-zip-fill::before{content:"\f3be"}.bi-file-zip::before{content:"\f3bf"}.bi-file::before{content:"\f3c0"}.bi-files-alt::before{content:"\f3c1"}.bi-files::before{content:"\f3c2"}.bi-film::before{content:"\f3c3"}.bi-filter-circle-fill::before{content:"\f3c4"}.bi-filter-circle::before{content:"\f3c5"}.bi-filter-left::before{content:"\f3c6"}.bi-filter-right::before{content:"\f3c7"}.bi-filter-square-fill::before{content:"\f3c8"}.bi-filter-square::before{content:"\f3c9"}.bi-filter::before{content:"\f3ca"}.bi-flag-fill::before{content:"\f3cb"}.bi-flag::before{content:"\f3cc"}.bi-flower1::before{content:"\f3cd"}.bi-flower2::before{content:"\f3ce"}.bi-flower3::before{content:"\f3cf"}.bi-folder-check::before{content:"\f3d0"}.bi-folder-fill::before{content:"\f3d1"}.bi-folder-minus::before{content:"\f3d2"}.bi-folder-plus::before{content:"\f3d3"}.bi-folder-symlink-fill::before{content:"\f3d4"}.bi-folder-symlink::before{content:"\f3d5"}.bi-folder-x::before{content:"\f3d6"}.bi-folder::before{content:"\f3d7"}.bi-folder2-open::before{content:"\f3d8"}.bi-folder2::before{content:"\f3d9"}.bi-fonts::before{content:"\f3da"}.bi-forward-fill::before{content:"\f3db"}.bi-forward::before{content:"\f3dc"}.bi-front::before{content:"\f3dd"}.bi-fullscreen-exit::before{content:"\f3de"}.bi-fullscreen::before{content:"\f3df"}.bi-funnel-fill::before{content:"\f3e0"}.bi-funnel::before{content:"\f3e1"}.bi-gear-fill::before{content:"\f3e2"}.bi-gear-wide-connected::before{content:"\f3e3"}.bi-gear-wide::before{content:"\f3e4"}.bi-gear::before{content:"\f3e5"}.bi-gem::before{content:"\f3e6"}.bi-geo-alt-fill::before{content:"\f3e7"}.bi-geo-alt::before{content:"\f3e8"}.bi-geo-fill::before{content:"\f3e9"}.bi-geo::before{content:"\f3ea"}.bi-gift-fill::before{content:"\f3eb"}.bi-gift::before{content:"\f3ec"}.bi-github::before{content:"\f3ed"}.bi-globe::before{content:"\f3ee"}.bi-globe2::before{content:"\f3ef"}.bi-google::before{content:"\f3f0"}.bi-graph-down::before{content:"\f3f1"}.bi-graph-up::before{content:"\f3f2"}.bi-grid-1x2-fill::before{content:"\f3f3"}.bi-grid-1x2::before{content:"\f3f4"}.bi-grid-3x2-gap-fill::before{content:"\f3f5"}.bi-grid-3x2-gap::before{content:"\f3f6"}.bi-grid-3x2::before{content:"\f3f7"}.bi-grid-3x3-gap-fill::before{content:"\f3f8"}.bi-grid-3x3-gap::before{content:"\f3f9"}.bi-grid-3x3::before{content:"\f3fa"}.bi-grid-fill::before{content:"\f3fb"}.bi-grid::before{content:"\f3fc"}.bi-grip-horizontal::before{content:"\f3fd"}.bi-grip-vertical::before{content:"\f3fe"}.bi-hammer::before{content:"\f3ff"}.bi-hand-index-fill::before{content:"\f400"}.bi-hand-index-thumb-fill::before{content:"\f401"}.bi-hand-index-thumb::before{content:"\f402"}.bi-hand-index::before{content:"\f403"}.bi-hand-thumbs-down-fill::before{content:"\f404"}.bi-hand-thumbs-down::before{content:"\f405"}.bi-hand-thumbs-up-fill::before{content:"\f406"}.bi-hand-thumbs-up::before{content:"\f407"}.bi-handbag-fill::before{content:"\f408"}.bi-handbag::before{content:"\f409"}.bi-hash::before{content:"\f40a"}.bi-hdd-fill::before{content:"\f40b"}.bi-hdd-network-fill::before{content:"\f40c"}.bi-hdd-network::before{content:"\f40d"}.bi-hdd-rack-fill::before{content:"\f40e"}.bi-hdd-rack::before{content:"\f40f"}.bi-hdd-stack-fill::before{content:"\f410"}.bi-hdd-stack::before{content:"\f411"}.bi-hdd::before{content:"\f412"}.bi-headphones::before{content:"\f413"}.bi-headset::before{content:"\f414"}.bi-heart-fill::before{content:"\f415"}.bi-heart-half::before{content:"\f416"}.bi-heart::before{content:"\f417"}.bi-heptagon-fill::before{content:"\f418"}.bi-heptagon-half::before{content:"\f419"}.bi-heptagon::before{content:"\f41a"}.bi-hexagon-fill::before{content:"\f41b"}.bi-hexagon-half::before{content:"\f41c"}.bi-hexagon::before{content:"\f41d"}.bi-hourglass-bottom::before{content:"\f41e"}.bi-hourglass-split::before{content:"\f41f"}.bi-hourglass-top::before{content:"\f420"}.bi-hourglass::before{content:"\f421"}.bi-house-door-fill::before{content:"\f422"}.bi-house-door::before{content:"\f423"}.bi-house-fill::before{content:"\f424"}.bi-house::before{content:"\f425"}.bi-hr::before{content:"\f426"}.bi-hurricane::before{content:"\f427"}.bi-image-alt::before{content:"\f428"}.bi-image-fill::before{content:"\f429"}.bi-image::before{content:"\f42a"}.bi-images::before{content:"\f42b"}.bi-inbox-fill::before{content:"\f42c"}.bi-inbox::before{content:"\f42d"}.bi-inboxes-fill::before{content:"\f42e"}.bi-inboxes::before{content:"\f42f"}.bi-info-circle-fill::before{content:"\f430"}.bi-info-circle::before{content:"\f431"}.bi-info-square-fill::before{content:"\f432"}.bi-info-square::before{content:"\f433"}.bi-info::before{content:"\f434"}.bi-input-cursor-text::before{content:"\f435"}.bi-input-cursor::before{content:"\f436"}.bi-instagram::before{content:"\f437"}.bi-intersect::before{content:"\f438"}.bi-journal-album::before{content:"\f439"}.bi-journal-arrow-down::before{content:"\f43a"}.bi-journal-arrow-up::before{content:"\f43b"}.bi-journal-bookmark-fill::before{content:"\f43c"}.bi-journal-bookmark::before{content:"\f43d"}.bi-journal-check::before{content:"\f43e"}.bi-journal-code::before{content:"\f43f"}.bi-journal-medical::before{content:"\f440"}.bi-journal-minus::before{content:"\f441"}.bi-journal-plus::before{content:"\f442"}.bi-journal-richtext::before{content:"\f443"}.bi-journal-text::before{content:"\f444"}.bi-journal-x::before{content:"\f445"}.bi-journal::before{content:"\f446"}.bi-journals::before{content:"\f447"}.bi-joystick::before{content:"\f448"}.bi-justify-left::before{content:"\f449"}.bi-justify-right::before{content:"\f44a"}.bi-justify::before{content:"\f44b"}.bi-kanban-fill::before{content:"\f44c"}.bi-kanban::before{content:"\f44d"}.bi-key-fill::before{content:"\f44e"}.bi-key::before{content:"\f44f"}.bi-keyboard-fill::before{content:"\f450"}.bi-keyboard::before{content:"\f451"}.bi-ladder::before{content:"\f452"}.bi-lamp-fill::before{content:"\f453"}.bi-lamp::before{content:"\f454"}.bi-laptop-fill::before{content:"\f455"}.bi-laptop::before{content:"\f456"}.bi-layer-backward::before{content:"\f457"}.bi-layer-forward::before{content:"\f458"}.bi-layers-fill::before{content:"\f459"}.bi-layers-half::before{content:"\f45a"}.bi-layers::before{content:"\f45b"}.bi-layout-sidebar-inset-reverse::before{content:"\f45c"}.bi-layout-sidebar-inset::before{content:"\f45d"}.bi-layout-sidebar-reverse::before{content:"\f45e"}.bi-layout-sidebar::before{content:"\f45f"}.bi-layout-split::before{content:"\f460"}.bi-layout-text-sidebar-reverse::before{content:"\f461"}.bi-layout-text-sidebar::before{content:"\f462"}.bi-layout-text-window-reverse::before{content:"\f463"}.bi-layout-text-window::before{content:"\f464"}.bi-layout-three-columns::before{content:"\f465"}.bi-layout-wtf::before{content:"\f466"}.bi-life-preserver::before{content:"\f467"}.bi-lightbulb-fill::before{content:"\f468"}.bi-lightbulb-off-fill::before{content:"\f469"}.bi-lightbulb-off::before{content:"\f46a"}.bi-lightbulb::before{content:"\f46b"}.bi-lightning-charge-fill::before{content:"\f46c"}.bi-lightning-charge::before{content:"\f46d"}.bi-lightning-fill::before{content:"\f46e"}.bi-lightning::before{content:"\f46f"}.bi-link-45deg::before{content:"\f470"}.bi-link::before{content:"\f471"}.bi-linkedin::before{content:"\f472"}.bi-list-check::before{content:"\f473"}.bi-list-nested::before{content:"\f474"}.bi-list-ol::before{content:"\f475"}.bi-list-stars::before{content:"\f476"}.bi-list-task::before{content:"\f477"}.bi-list-ul::before{content:"\f478"}.bi-list::before{content:"\f479"}.bi-lock-fill::before{content:"\f47a"}.bi-lock::before{content:"\f47b"}.bi-mailbox::before{content:"\f47c"}.bi-mailbox2::before{content:"\f47d"}.bi-map-fill::before{content:"\f47e"}.bi-map::before{content:"\f47f"}.bi-markdown-fill::before{content:"\f480"}.bi-markdown::before{content:"\f481"}.bi-mask::before{content:"\f482"}.bi-megaphone-fill::before{content:"\f483"}.bi-megaphone::before{content:"\f484"}.bi-menu-app-fill::before{content:"\f485"}.bi-menu-app::before{content:"\f486"}.bi-menu-button-fill::before{content:"\f487"}.bi-menu-button-wide-fill::before{content:"\f488"}.bi-menu-button-wide::before{content:"\f489"}.bi-menu-button::before{content:"\f48a"}.bi-menu-down::before{content:"\f48b"}.bi-menu-up::before{content:"\f48c"}.bi-mic-fill::before{content:"\f48d"}.bi-mic-mute-fill::before{content:"\f48e"}.bi-mic-mute::before{content:"\f48f"}.bi-mic::before{content:"\f490"}.bi-minecart-loaded::before{content:"\f491"}.bi-minecart::before{content:"\f492"}.bi-moisture::before{content:"\f493"}.bi-moon-fill::before{content:"\f494"}.bi-moon-stars-fill::before{content:"\f495"}.bi-moon-stars::before{content:"\f496"}.bi-moon::before{content:"\f497"}.bi-mouse-fill::before{content:"\f498"}.bi-mouse::before{content:"\f499"}.bi-mouse2-fill::before{content:"\f49a"}.bi-mouse2::before{content:"\f49b"}.bi-mouse3-fill::before{content:"\f49c"}.bi-mouse3::before{content:"\f49d"}.bi-music-note-beamed::before{content:"\f49e"}.bi-music-note-list::before{content:"\f49f"}.bi-music-note::before{content:"\f4a0"}.bi-music-player-fill::before{content:"\f4a1"}.bi-music-player::before{content:"\f4a2"}.bi-newspaper::before{content:"\f4a3"}.bi-node-minus-fill::before{content:"\f4a4"}.bi-node-minus::before{content:"\f4a5"}.bi-node-plus-fill::before{content:"\f4a6"}.bi-node-plus::before{content:"\f4a7"}.bi-nut-fill::before{content:"\f4a8"}.bi-nut::before{content:"\f4a9"}.bi-octagon-fill::before{content:"\f4aa"}.bi-octagon-half::before{content:"\f4ab"}.bi-octagon::before{content:"\f4ac"}.bi-option::before{content:"\f4ad"}.bi-outlet::before{content:"\f4ae"}.bi-paint-bucket::before{content:"\f4af"}.bi-palette-fill::before{content:"\f4b0"}.bi-palette::before{content:"\f4b1"}.bi-palette2::before{content:"\f4b2"}.bi-paperclip::before{content:"\f4b3"}.bi-paragraph::before{content:"\f4b4"}.bi-patch-check-fill::before{content:"\f4b5"}.bi-patch-check::before{content:"\f4b6"}.bi-patch-exclamation-fill::before{content:"\f4b7"}.bi-patch-exclamation::before{content:"\f4b8"}.bi-patch-minus-fill::before{content:"\f4b9"}.bi-patch-minus::before{content:"\f4ba"}.bi-patch-plus-fill::before{content:"\f4bb"}.bi-patch-plus::before{content:"\f4bc"}.bi-patch-question-fill::before{content:"\f4bd"}.bi-patch-question::before{content:"\f4be"}.bi-pause-btn-fill::before{content:"\f4bf"}.bi-pause-btn::before{content:"\f4c0"}.bi-pause-circle-fill::before{content:"\f4c1"}.bi-pause-circle::before{content:"\f4c2"}.bi-pause-fill::before{content:"\f4c3"}.bi-pause::before{content:"\f4c4"}.bi-peace-fill::before{content:"\f4c5"}.bi-peace::before{content:"\f4c6"}.bi-pen-fill::before{content:"\f4c7"}.bi-pen::before{content:"\f4c8"}.bi-pencil-fill::before{content:"\f4c9"}.bi-pencil-square::before{content:"\f4ca"}.bi-pencil::before{content:"\f4cb"}.bi-pentagon-fill::before{content:"\f4cc"}.bi-pentagon-half::before{content:"\f4cd"}.bi-pentagon::before{content:"\f4ce"}.bi-people-fill::before{content:"\f4cf"}.bi-people::before{content:"\f4d0"}.bi-percent::before{content:"\f4d1"}.bi-person-badge-fill::before{content:"\f4d2"}.bi-person-badge::before{content:"\f4d3"}.bi-person-bounding-box::before{content:"\f4d4"}.bi-person-check-fill::before{content:"\f4d5"}.bi-person-check::before{content:"\f4d6"}.bi-person-circle::before{content:"\f4d7"}.bi-person-dash-fill::before{content:"\f4d8"}.bi-person-dash::before{content:"\f4d9"}.bi-person-fill::before{content:"\f4da"}.bi-person-lines-fill::before{content:"\f4db"}.bi-person-plus-fill::before{content:"\f4dc"}.bi-person-plus::before{content:"\f4dd"}.bi-person-square::before{content:"\f4de"}.bi-person-x-fill::before{content:"\f4df"}.bi-person-x::before{content:"\f4e0"}.bi-person::before{content:"\f4e1"}.bi-phone-fill::before{content:"\f4e2"}.bi-phone-landscape-fill::before{content:"\f4e3"}.bi-phone-landscape::before{content:"\f4e4"}.bi-phone-vibrate-fill::before{content:"\f4e5"}.bi-phone-vibrate::before{content:"\f4e6"}.bi-phone::before{content:"\f4e7"}.bi-pie-chart-fill::before{content:"\f4e8"}.bi-pie-chart::before{content:"\f4e9"}.bi-pin-angle-fill::before{content:"\f4ea"}.bi-pin-angle::before{content:"\f4eb"}.bi-pin-fill::before{content:"\f4ec"}.bi-pin::before{content:"\f4ed"}.bi-pip-fill::before{content:"\f4ee"}.bi-pip::before{content:"\f4ef"}.bi-play-btn-fill::before{content:"\f4f0"}.bi-play-btn::before{content:"\f4f1"}.bi-play-circle-fill::before{content:"\f4f2"}.bi-play-circle::before{content:"\f4f3"}.bi-play-fill::before{content:"\f4f4"}.bi-play::before{content:"\f4f5"}.bi-plug-fill::before{content:"\f4f6"}.bi-plug::before{content:"\f4f7"}.bi-plus-circle-dotted::before{content:"\f4f8"}.bi-plus-circle-fill::before{content:"\f4f9"}.bi-plus-circle::before{content:"\f4fa"}.bi-plus-square-dotted::before{content:"\f4fb"}.bi-plus-square-fill::before{content:"\f4fc"}.bi-plus-square::before{content:"\f4fd"}.bi-plus::before{content:"\f4fe"}.bi-power::before{content:"\f4ff"}.bi-printer-fill::before{content:"\f500"}.bi-printer::before{content:"\f501"}.bi-puzzle-fill::before{content:"\f502"}.bi-puzzle::before{content:"\f503"}.bi-question-circle-fill::before{content:"\f504"}.bi-question-circle::before{content:"\f505"}.bi-question-diamond-fill::before{content:"\f506"}.bi-question-diamond::before{content:"\f507"}.bi-question-octagon-fill::before{content:"\f508"}.bi-question-octagon::before{content:"\f509"}.bi-question-square-fill::before{content:"\f50a"}.bi-question-square::before{content:"\f50b"}.bi-question::before{content:"\f50c"}.bi-rainbow::before{content:"\f50d"}.bi-receipt-cutoff::before{content:"\f50e"}.bi-receipt::before{content:"\f50f"}.bi-reception-0::before{content:"\f510"}.bi-reception-1::before{content:"\f511"}.bi-reception-2::before{content:"\f512"}.bi-reception-3::before{content:"\f513"}.bi-reception-4::before{content:"\f514"}.bi-record-btn-fill::before{content:"\f515"}.bi-record-btn::before{content:"\f516"}.bi-record-circle-fill::before{content:"\f517"}.bi-record-circle::before{content:"\f518"}.bi-record-fill::before{content:"\f519"}.bi-record::before{content:"\f51a"}.bi-record2-fill::before{content:"\f51b"}.bi-record2::before{content:"\f51c"}.bi-reply-all-fill::before{content:"\f51d"}.bi-reply-all::before{content:"\f51e"}.bi-reply-fill::before{content:"\f51f"}.bi-reply::before{content:"\f520"}.bi-rss-fill::before{content:"\f521"}.bi-rss::before{content:"\f522"}.bi-rulers::before{content:"\f523"}.bi-save-fill::before{content:"\f524"}.bi-save::before{content:"\f525"}.bi-save2-fill::before{content:"\f526"}.bi-save2::before{content:"\f527"}.bi-scissors::before{content:"\f528"}.bi-screwdriver::before{content:"\f529"}.bi-search::before{content:"\f52a"}.bi-segmented-nav::before{content:"\f52b"}.bi-server::before{content:"\f52c"}.bi-share-fill::before{content:"\f52d"}.bi-share::before{content:"\f52e"}.bi-shield-check::before{content:"\f52f"}.bi-shield-exclamation::before{content:"\f530"}.bi-shield-fill-check::before{content:"\f531"}.bi-shield-fill-exclamation::before{content:"\f532"}.bi-shield-fill-minus::before{content:"\f533"}.bi-shield-fill-plus::before{content:"\f534"}.bi-shield-fill-x::before{content:"\f535"}.bi-shield-fill::before{content:"\f536"}.bi-shield-lock-fill::before{content:"\f537"}.bi-shield-lock::before{content:"\f538"}.bi-shield-minus::before{content:"\f539"}.bi-shield-plus::before{content:"\f53a"}.bi-shield-shaded::before{content:"\f53b"}.bi-shield-slash-fill::before{content:"\f53c"}.bi-shield-slash::before{content:"\f53d"}.bi-shield-x::before{content:"\f53e"}.bi-shield::before{content:"\f53f"}.bi-shift-fill::before{content:"\f540"}.bi-shift::before{content:"\f541"}.bi-shop-window::before{content:"\f542"}.bi-shop::before{content:"\f543"}.bi-shuffle::before{content:"\f544"}.bi-signpost-2-fill::before{content:"\f545"}.bi-signpost-2::before{content:"\f546"}.bi-signpost-fill::before{content:"\f547"}.bi-signpost-split-fill::before{content:"\f548"}.bi-signpost-split::before{content:"\f549"}.bi-signpost::before{content:"\f54a"}.bi-sim-fill::before{content:"\f54b"}.bi-sim::before{content:"\f54c"}.bi-skip-backward-btn-fill::before{content:"\f54d"}.bi-skip-backward-btn::before{content:"\f54e"}.bi-skip-backward-circle-fill::before{content:"\f54f"}.bi-skip-backward-circle::before{content:"\f550"}.bi-skip-backward-fill::before{content:"\f551"}.bi-skip-backward::before{content:"\f552"}.bi-skip-end-btn-fill::before{content:"\f553"}.bi-skip-end-btn::before{content:"\f554"}.bi-skip-end-circle-fill::before{content:"\f555"}.bi-skip-end-circle::before{content:"\f556"}.bi-skip-end-fill::before{content:"\f557"}.bi-skip-end::before{content:"\f558"}.bi-skip-forward-btn-fill::before{content:"\f559"}.bi-skip-forward-btn::before{content:"\f55a"}.bi-skip-forward-circle-fill::before{content:"\f55b"}.bi-skip-forward-circle::before{content:"\f55c"}.bi-skip-forward-fill::before{content:"\f55d"}.bi-skip-forward::before{content:"\f55e"}.bi-skip-start-btn-fill::before{content:"\f55f"}.bi-skip-start-btn::before{content:"\f560"}.bi-skip-start-circle-fill::before{content:"\f561"}.bi-skip-start-circle::before{content:"\f562"}.bi-skip-start-fill::before{content:"\f563"}.bi-skip-start::before{content:"\f564"}.bi-slack::before{content:"\f565"}.bi-slash-circle-fill::before{content:"\f566"}.bi-slash-circle::before{content:"\f567"}.bi-slash-square-fill::before{content:"\f568"}.bi-slash-square::before{content:"\f569"}.bi-slash::before{content:"\f56a"}.bi-sliders::before{content:"\f56b"}.bi-smartwatch::before{content:"\f56c"}.bi-snow::before{content:"\f56d"}.bi-snow2::before{content:"\f56e"}.bi-snow3::before{content:"\f56f"}.bi-sort-alpha-down-alt::before{content:"\f570"}.bi-sort-alpha-down::before{content:"\f571"}.bi-sort-alpha-up-alt::before{content:"\f572"}.bi-sort-alpha-up::before{content:"\f573"}.bi-sort-down-alt::before{content:"\f574"}.bi-sort-down::before{content:"\f575"}.bi-sort-numeric-down-alt::before{content:"\f576"}.bi-sort-numeric-down::before{content:"\f577"}.bi-sort-numeric-up-alt::before{content:"\f578"}.bi-sort-numeric-up::before{content:"\f579"}.bi-sort-up-alt::before{content:"\f57a"}.bi-sort-up::before{content:"\f57b"}.bi-soundwave::before{content:"\f57c"}.bi-speaker-fill::before{content:"\f57d"}.bi-speaker::before{content:"\f57e"}.bi-speedometer::before{content:"\f57f"}.bi-speedometer2::before{content:"\f580"}.bi-spellcheck::before{content:"\f581"}.bi-square-fill::before{content:"\f582"}.bi-square-half::before{content:"\f583"}.bi-square::before{content:"\f584"}.bi-stack::before{content:"\f585"}.bi-star-fill::before{content:"\f586"}.bi-star-half::before{content:"\f587"}.bi-star::before{content:"\f588"}.bi-stars::before{content:"\f589"}.bi-stickies-fill::before{content:"\f58a"}.bi-stickies::before{content:"\f58b"}.bi-sticky-fill::before{content:"\f58c"}.bi-sticky::before{content:"\f58d"}.bi-stop-btn-fill::before{content:"\f58e"}.bi-stop-btn::before{content:"\f58f"}.bi-stop-circle-fill::before{content:"\f590"}.bi-stop-circle::before{content:"\f591"}.bi-stop-fill::before{content:"\f592"}.bi-stop::before{content:"\f593"}.bi-stoplights-fill::before{content:"\f594"}.bi-stoplights::before{content:"\f595"}.bi-stopwatch-fill::before{content:"\f596"}.bi-stopwatch::before{content:"\f597"}.bi-subtract::before{content:"\f598"}.bi-suit-club-fill::before{content:"\f599"}.bi-suit-club::before{content:"\f59a"}.bi-suit-diamond-fill::before{content:"\f59b"}.bi-suit-diamond::before{content:"\f59c"}.bi-suit-heart-fill::before{content:"\f59d"}.bi-suit-heart::before{content:"\f59e"}.bi-suit-spade-fill::before{content:"\f59f"}.bi-suit-spade::before{content:"\f5a0"}.bi-sun-fill::before{content:"\f5a1"}.bi-sun::before{content:"\f5a2"}.bi-sunglasses::before{content:"\f5a3"}.bi-sunrise-fill::before{content:"\f5a4"}.bi-sunrise::before{content:"\f5a5"}.bi-sunset-fill::before{content:"\f5a6"}.bi-sunset::before{content:"\f5a7"}.bi-symmetry-horizontal::before{content:"\f5a8"}.bi-symmetry-vertical::before{content:"\f5a9"}.bi-table::before{content:"\f5aa"}.bi-tablet-fill::before{content:"\f5ab"}.bi-tablet-landscape-fill::before{content:"\f5ac"}.bi-tablet-landscape::before{content:"\f5ad"}.bi-tablet::before{content:"\f5ae"}.bi-tag-fill::before{content:"\f5af"}.bi-tag::before{content:"\f5b0"}.bi-tags-fill::before{content:"\f5b1"}.bi-tags::before{content:"\f5b2"}.bi-telegram::before{content:"\f5b3"}.bi-telephone-fill::before{content:"\f5b4"}.bi-telephone-forward-fill::before{content:"\f5b5"}.bi-telephone-forward::before{content:"\f5b6"}.bi-telephone-inbound-fill::before{content:"\f5b7"}.bi-telephone-inbound::before{content:"\f5b8"}.bi-telephone-minus-fill::before{content:"\f5b9"}.bi-telephone-minus::before{content:"\f5ba"}.bi-telephone-outbound-fill::before{content:"\f5bb"}.bi-telephone-outbound::before{content:"\f5bc"}.bi-telephone-plus-fill::before{content:"\f5bd"}.bi-telephone-plus::before{content:"\f5be"}.bi-telephone-x-fill::before{content:"\f5bf"}.bi-telephone-x::before{content:"\f5c0"}.bi-telephone::before{content:"\f5c1"}.bi-terminal-fill::before{content:"\f5c2"}.bi-terminal::before{content:"\f5c3"}.bi-text-center::before{content:"\f5c4"}.bi-text-indent-left::before{content:"\f5c5"}.bi-text-indent-right::before{content:"\f5c6"}.bi-text-left::before{content:"\f5c7"}.bi-text-paragraph::before{content:"\f5c8"}.bi-text-right::before{content:"\f5c9"}.bi-textarea-resize::before{content:"\f5ca"}.bi-textarea-t::before{content:"\f5cb"}.bi-textarea::before{content:"\f5cc"}.bi-thermometer-half::before{content:"\f5cd"}.bi-thermometer-high::before{content:"\f5ce"}.bi-thermometer-low::before{content:"\f5cf"}.bi-thermometer-snow::before{content:"\f5d0"}.bi-thermometer-sun::before{content:"\f5d1"}.bi-thermometer::before{content:"\f5d2"}.bi-three-dots-vertical::before{content:"\f5d3"}.bi-three-dots::before{content:"\f5d4"}.bi-toggle-off::before{content:"\f5d5"}.bi-toggle-on::before{content:"\f5d6"}.bi-toggle2-off::before{content:"\f5d7"}.bi-toggle2-on::before{content:"\f5d8"}.bi-toggles::before{content:"\f5d9"}.bi-toggles2::before{content:"\f5da"}.bi-tools::before{content:"\f5db"}.bi-tornado::before{content:"\f5dc"}.bi-trash-fill::before{content:"\f5dd"}.bi-trash::before{content:"\f5de"}.bi-trash2-fill::before{content:"\f5df"}.bi-trash2::before{content:"\f5e0"}.bi-tree-fill::before{content:"\f5e1"}.bi-tree::before{content:"\f5e2"}.bi-triangle-fill::before{content:"\f5e3"}.bi-triangle-half::before{content:"\f5e4"}.bi-triangle::before{content:"\f5e5"}.bi-trophy-fill::before{content:"\f5e6"}.bi-trophy::before{content:"\f5e7"}.bi-tropical-storm::before{content:"\f5e8"}.bi-truck-flatbed::before{content:"\f5e9"}.bi-truck::before{content:"\f5ea"}.bi-tsunami::before{content:"\f5eb"}.bi-tv-fill::before{content:"\f5ec"}.bi-tv::before{content:"\f5ed"}.bi-twitch::before{content:"\f5ee"}.bi-twitter::before{content:"\f5ef"}.bi-type-bold::before{content:"\f5f0"}.bi-type-h1::before{content:"\f5f1"}.bi-type-h2::before{content:"\f5f2"}.bi-type-h3::before{content:"\f5f3"}.bi-type-italic::before{content:"\f5f4"}.bi-type-strikethrough::before{content:"\f5f5"}.bi-type-underline::before{content:"\f5f6"}.bi-type::before{content:"\f5f7"}.bi-ui-checks-grid::before{content:"\f5f8"}.bi-ui-checks::before{content:"\f5f9"}.bi-ui-radios-grid::before{content:"\f5fa"}.bi-ui-radios::before{content:"\f5fb"}.bi-umbrella-fill::before{content:"\f5fc"}.bi-umbrella::before{content:"\f5fd"}.bi-union::before{content:"\f5fe"}.bi-unlock-fill::before{content:"\f5ff"}.bi-unlock::before{content:"\f600"}.bi-upc-scan::before{content:"\f601"}.bi-upc::before{content:"\f602"}.bi-upload::before{content:"\f603"}.bi-vector-pen::before{content:"\f604"}.bi-view-list::before{content:"\f605"}.bi-view-stacked::before{content:"\f606"}.bi-vinyl-fill::before{content:"\f607"}.bi-vinyl::before{content:"\f608"}.bi-voicemail::before{content:"\f609"}.bi-volume-down-fill::before{content:"\f60a"}.bi-volume-down::before{content:"\f60b"}.bi-volume-mute-fill::before{content:"\f60c"}.bi-volume-mute::before{content:"\f60d"}.bi-volume-off-fill::before{content:"\f60e"}.bi-volume-off::before{content:"\f60f"}.bi-volume-up-fill::before{content:"\f610"}.bi-volume-up::before{content:"\f611"}.bi-vr::before{content:"\f612"}.bi-wallet-fill::before{content:"\f613"}.bi-wallet::before{content:"\f614"}.bi-wallet2::before{content:"\f615"}.bi-watch::before{content:"\f616"}.bi-water::before{content:"\f617"}.bi-whatsapp::before{content:"\f618"}.bi-wifi-1::before{content:"\f619"}.bi-wifi-2::before{content:"\f61a"}.bi-wifi-off::before{content:"\f61b"}.bi-wifi::before{content:"\f61c"}.bi-wind::before{content:"\f61d"}.bi-window-dock::before{content:"\f61e"}.bi-window-sidebar::before{content:"\f61f"}.bi-window::before{content:"\f620"}.bi-wrench::before{content:"\f621"}.bi-x-circle-fill::before{content:"\f622"}.bi-x-circle::before{content:"\f623"}.bi-x-diamond-fill::before{content:"\f624"}.bi-x-diamond::before{content:"\f625"}.bi-x-octagon-fill::before{content:"\f626"}.bi-x-octagon::before{content:"\f627"}.bi-x-square-fill::before{content:"\f628"}.bi-x-square::before{content:"\f629"}.bi-x::before{content:"\f62a"}.bi-youtube::before{content:"\f62b"}.bi-zoom-in::before{content:"\f62c"}.bi-zoom-out::before{content:"\f62d"}.bi-bank::before{content:"\f62e"}.bi-bank2::before{content:"\f62f"}.bi-bell-slash-fill::before{content:"\f630"}.bi-bell-slash::before{content:"\f631"}.bi-cash-coin::before{content:"\f632"}.bi-check-lg::before{content:"\f633"}.bi-coin::before{content:"\f634"}.bi-currency-bitcoin::before{content:"\f635"}.bi-currency-dollar::before{content:"\f636"}.bi-currency-euro::before{content:"\f637"}.bi-currency-exchange::before{content:"\f638"}.bi-currency-pound::before{content:"\f639"}.bi-currency-yen::before{content:"\f63a"}.bi-dash-lg::before{content:"\f63b"}.bi-exclamation-lg::before{content:"\f63c"}.bi-file-earmark-pdf-fill::before{content:"\f63d"}.bi-file-earmark-pdf::before{content:"\f63e"}.bi-file-pdf-fill::before{content:"\f63f"}.bi-file-pdf::before{content:"\f640"}.bi-gender-ambiguous::before{content:"\f641"}.bi-gender-female::before{content:"\f642"}.bi-gender-male::before{content:"\f643"}.bi-gender-trans::before{content:"\f644"}.bi-headset-vr::before{content:"\f645"}.bi-info-lg::before{content:"\f646"}.bi-mastodon::before{content:"\f647"}.bi-messenger::before{content:"\f648"}.bi-piggy-bank-fill::before{content:"\f649"}.bi-piggy-bank::before{content:"\f64a"}.bi-pin-map-fill::before{content:"\f64b"}.bi-pin-map::before{content:"\f64c"}.bi-plus-lg::before{content:"\f64d"}.bi-question-lg::before{content:"\f64e"}.bi-recycle::before{content:"\f64f"}.bi-reddit::before{content:"\f650"}.bi-safe-fill::before{content:"\f651"}.bi-safe2-fill::before{content:"\f652"}.bi-safe2::before{content:"\f653"}.bi-sd-card-fill::before{content:"\f654"}.bi-sd-card::before{content:"\f655"}.bi-skype::before{content:"\f656"}.bi-slash-lg::before{content:"\f657"}.bi-translate::before{content:"\f658"}.bi-x-lg::before{content:"\f659"}.bi-safe::before{content:"\f65a"}.bi-apple::before{content:"\f65b"}.bi-microsoft::before{content:"\f65d"}.bi-windows::before{content:"\f65e"}.bi-behance::before{content:"\f65c"}.bi-dribbble::before{content:"\f65f"}.bi-line::before{content:"\f660"}.bi-medium::before{content:"\f661"}.bi-paypal::before{content:"\f662"}.bi-pinterest::before{content:"\f663"}.bi-signal::before{content:"\f664"}.bi-snapchat::before{content:"\f665"}.bi-spotify::before{content:"\f666"}.bi-stack-overflow::before{content:"\f667"}.bi-strava::before{content:"\f668"}.bi-wordpress::before{content:"\f669"}.bi-vimeo::before{content:"\f66a"}.bi-activity::before{content:"\f66b"}.bi-easel2-fill::before{content:"\f66c"}.bi-easel2::before{content:"\f66d"}.bi-easel3-fill::before{content:"\f66e"}.bi-easel3::before{content:"\f66f"}.bi-fan::before{content:"\f670"}.bi-fingerprint::before{content:"\f671"}.bi-graph-down-arrow::before{content:"\f672"}.bi-graph-up-arrow::before{content:"\f673"}.bi-hypnotize::before{content:"\f674"}.bi-magic::before{content:"\f675"}.bi-person-rolodex::before{content:"\f676"}.bi-person-video::before{content:"\f677"}.bi-person-video2::before{content:"\f678"}.bi-person-video3::before{content:"\f679"}.bi-person-workspace::before{content:"\f67a"}.bi-radioactive::before{content:"\f67b"}.bi-webcam-fill::before{content:"\f67c"}.bi-webcam::before{content:"\f67d"}.bi-yin-yang::before{content:"\f67e"}.bi-bandaid-fill::before{content:"\f680"}.bi-bandaid::before{content:"\f681"}.bi-bluetooth::before{content:"\f682"}.bi-body-text::before{content:"\f683"}.bi-boombox::before{content:"\f684"}.bi-boxes::before{content:"\f685"}.bi-dpad-fill::before{content:"\f686"}.bi-dpad::before{content:"\f687"}.bi-ear-fill::before{content:"\f688"}.bi-ear::before{content:"\f689"}.bi-envelope-check-1::before{content:"\f68a"}.bi-envelope-check-fill::before{content:"\f68b"}.bi-envelope-check::before{content:"\f68c"}.bi-envelope-dash-1::before{content:"\f68d"}.bi-envelope-dash-fill::before{content:"\f68e"}.bi-envelope-dash::before{content:"\f68f"}.bi-envelope-exclamation-1::before{content:"\f690"}.bi-envelope-exclamation-fill::before{content:"\f691"}.bi-envelope-exclamation::before{content:"\f692"}.bi-envelope-plus-fill::before{content:"\f693"}.bi-envelope-plus::before{content:"\f694"}.bi-envelope-slash-1::before{content:"\f695"}.bi-envelope-slash-fill::before{content:"\f696"}.bi-envelope-slash::before{content:"\f697"}.bi-envelope-x-1::before{content:"\f698"}.bi-envelope-x-fill::before{content:"\f699"}.bi-envelope-x::before{content:"\f69a"}.bi-explicit-fill::before{content:"\f69b"}.bi-explicit::before{content:"\f69c"}.bi-git::before{content:"\f69d"}.bi-infinity::before{content:"\f69e"}.bi-list-columns-reverse::before{content:"\f69f"}.bi-list-columns::before{content:"\f6a0"}.bi-meta::before{content:"\f6a1"}.bi-mortorboard-fill::before{content:"\f6a2"}.bi-mortorboard::before{content:"\f6a3"}.bi-nintendo-switch::before{content:"\f6a4"}.bi-pc-display-horizontal::before{content:"\f6a5"}.bi-pc-display::before{content:"\f6a6"}.bi-pc-horizontal::before{content:"\f6a7"}.bi-pc::before{content:"\f6a8"}.bi-playstation::before{content:"\f6a9"}.bi-plus-slash-minus::before{content:"\f6aa"}.bi-projector-fill::before{content:"\f6ab"}.bi-projector::before{content:"\f6ac"}.bi-qr-code-scan::before{content:"\f6ad"}.bi-qr-code::before{content:"\f6ae"}.bi-quora::before{content:"\f6af"}.bi-quote::before{content:"\f6b0"}.bi-robot::before{content:"\f6b1"}.bi-send-check-fill::before{content:"\f6b2"}.bi-send-check::before{content:"\f6b3"}.bi-send-dash-fill::before{content:"\f6b4"}.bi-send-dash::before{content:"\f6b5"}.bi-send-exclamation-1::before{content:"\f6b6"}.bi-send-exclamation-fill::before{content:"\f6b7"}.bi-send-exclamation::before{content:"\f6b8"}.bi-send-fill::before{content:"\f6b9"}.bi-send-plus-fill::before{content:"\f6ba"}.bi-send-plus::before{content:"\f6bb"}.bi-send-slash-fill::before{content:"\f6bc"}.bi-send-slash::before{content:"\f6bd"}.bi-send-x-fill::before{content:"\f6be"}.bi-send-x::before{content:"\f6bf"}.bi-send::before{content:"\f6c0"}.bi-steam::before{content:"\f6c1"}.bi-terminal-dash-1::before{content:"\f6c2"}.bi-terminal-dash::before{content:"\f6c3"}.bi-terminal-plus::before{content:"\f6c4"}.bi-terminal-split::before{content:"\f6c5"}.bi-ticket-detailed-fill::before{content:"\f6c6"}.bi-ticket-detailed::before{content:"\f6c7"}.bi-ticket-fill::before{content:"\f6c8"}.bi-ticket-perforated-fill::before{content:"\f6c9"}.bi-ticket-perforated::before{content:"\f6ca"}.bi-ticket::before{content:"\f6cb"}.bi-tiktok::before{content:"\f6cc"}.bi-window-dash::before{content:"\f6cd"}.bi-window-desktop::before{content:"\f6ce"}.bi-window-fullscreen::before{content:"\f6cf"}.bi-window-plus::before{content:"\f6d0"}.bi-window-split::before{content:"\f6d1"}.bi-window-stack::before{content:"\f6d2"}.bi-window-x::before{content:"\f6d3"}.bi-xbox::before{content:"\f6d4"}.bi-ethernet::before{content:"\f6d5"}.bi-hdmi-fill::before{content:"\f6d6"}.bi-hdmi::before{content:"\f6d7"}.bi-usb-c-fill::before{content:"\f6d8"}.bi-usb-c::before{content:"\f6d9"}.bi-usb-fill::before{content:"\f6da"}.bi-usb-plug-fill::before{content:"\f6db"}.bi-usb-plug::before{content:"\f6dc"}.bi-usb-symbol::before{content:"\f6dd"}.bi-usb::before{content:"\f6de"}.bi-boombox-fill::before{content:"\f6df"}.bi-displayport-1::before{content:"\f6e0"}.bi-displayport::before{content:"\f6e1"}.bi-gpu-card::before{content:"\f6e2"}.bi-memory::before{content:"\f6e3"}.bi-modem-fill::before{content:"\f6e4"}.bi-modem::before{content:"\f6e5"}.bi-motherboard-fill::before{content:"\f6e6"}.bi-motherboard::before{content:"\f6e7"}.bi-optical-audio-fill::before{content:"\f6e8"}.bi-optical-audio::before{content:"\f6e9"}.bi-pci-card::before{content:"\f6ea"}.bi-router-fill::before{content:"\f6eb"}.bi-router::before{content:"\f6ec"}.bi-ssd-fill::before{content:"\f6ed"}.bi-ssd::before{content:"\f6ee"}.bi-thunderbolt-fill::before{content:"\f6ef"}.bi-thunderbolt::before{content:"\f6f0"}.bi-usb-drive-fill::before{content:"\f6f1"}.bi-usb-drive::before{content:"\f6f2"}.bi-usb-micro-fill::before{content:"\f6f3"}.bi-usb-micro::before{content:"\f6f4"}.bi-usb-mini-fill::before{content:"\f6f5"}.bi-usb-mini::before{content:"\f6f6"}.bi-cloud-haze2::before{content:"\f6f7"}.bi-device-hdd-fill::before{content:"\f6f8"}.bi-device-hdd::before{content:"\f6f9"}.bi-device-ssd-fill::before{content:"\f6fa"}.bi-device-ssd::before{content:"\f6fb"}.bi-displayport-fill::before{content:"\f6fc"}.bi-mortarboard-fill::before{content:"\f6fd"}.bi-mortarboard::before{content:"\f6fe"}.bi-terminal-x::before{content:"\f6ff"}.bi-arrow-through-heart-fill::before{content:"\f700"}.bi-arrow-through-heart::before{content:"\f701"}.bi-badge-sd-fill::before{content:"\f702"}.bi-badge-sd::before{content:"\f703"}.bi-bag-heart-fill::before{content:"\f704"}.bi-bag-heart::before{content:"\f705"}.bi-balloon-fill::before{content:"\f706"}.bi-balloon-heart-fill::before{content:"\f707"}.bi-balloon-heart::before{content:"\f708"}.bi-balloon::before{content:"\f709"}.bi-box2-fill::before{content:"\f70a"}.bi-box2-heart-fill::before{content:"\f70b"}.bi-box2-heart::before{content:"\f70c"}.bi-box2::before{content:"\f70d"}.bi-braces-asterisk::before{content:"\f70e"}.bi-calendar-heart-fill::before{content:"\f70f"}.bi-calendar-heart::before{content:"\f710"}.bi-calendar2-heart-fill::before{content:"\f711"}.bi-calendar2-heart::before{content:"\f712"}.bi-chat-heart-fill::before{content:"\f713"}.bi-chat-heart::before{content:"\f714"}.bi-chat-left-heart-fill::before{content:"\f715"}.bi-chat-left-heart::before{content:"\f716"}.bi-chat-right-heart-fill::before{content:"\f717"}.bi-chat-right-heart::before{content:"\f718"}.bi-chat-square-heart-fill::before{content:"\f719"}.bi-chat-square-heart::before{content:"\f71a"}.bi-clipboard-check-fill::before{content:"\f71b"}.bi-clipboard-data-fill::before{content:"\f71c"}.bi-clipboard-fill::before{content:"\f71d"}.bi-clipboard-heart-fill::before{content:"\f71e"}.bi-clipboard-heart::before{content:"\f71f"}.bi-clipboard-minus-fill::before{content:"\f720"}.bi-clipboard-plus-fill::before{content:"\f721"}.bi-clipboard-pulse::before{content:"\f722"}.bi-clipboard-x-fill::before{content:"\f723"}.bi-clipboard2-check-fill::before{content:"\f724"}.bi-clipboard2-check::before{content:"\f725"}.bi-clipboard2-data-fill::before{content:"\f726"}.bi-clipboard2-data::before{content:"\f727"}.bi-clipboard2-fill::before{content:"\f728"}.bi-clipboard2-heart-fill::before{content:"\f729"}.bi-clipboard2-heart::before{content:"\f72a"}.bi-clipboard2-minus-fill::before{content:"\f72b"}.bi-clipboard2-minus::before{content:"\f72c"}.bi-clipboard2-plus-fill::before{content:"\f72d"}.bi-clipboard2-plus::before{content:"\f72e"}.bi-clipboard2-pulse-fill::before{content:"\f72f"}.bi-clipboard2-pulse::before{content:"\f730"}.bi-clipboard2-x-fill::before{content:"\f731"}.bi-clipboard2-x::before{content:"\f732"}.bi-clipboard2::before{content:"\f733"}.bi-emoji-kiss-fill::before{content:"\f734"}.bi-emoji-kiss::before{content:"\f735"}.bi-envelope-heart-fill::before{content:"\f736"}.bi-envelope-heart::before{content:"\f737"}.bi-envelope-open-heart-fill::before{content:"\f738"}.bi-envelope-open-heart::before{content:"\f739"}.bi-envelope-paper-fill::before{content:"\f73a"}.bi-envelope-paper-heart-fill::before{content:"\f73b"}.bi-envelope-paper-heart::before{content:"\f73c"}.bi-envelope-paper::before{content:"\f73d"}.bi-filetype-aac::before{content:"\f73e"}.bi-filetype-ai::before{content:"\f73f"}.bi-filetype-bmp::before{content:"\f740"}.bi-filetype-cs::before{content:"\f741"}.bi-filetype-css::before{content:"\f742"}.bi-filetype-csv::before{content:"\f743"}.bi-filetype-doc::before{content:"\f744"}.bi-filetype-docx::before{content:"\f745"}.bi-filetype-exe::before{content:"\f746"}.bi-filetype-gif::before{content:"\f747"}.bi-filetype-heic::before{content:"\f748"}.bi-filetype-html::before{content:"\f749"}.bi-filetype-java::before{content:"\f74a"}.bi-filetype-jpg::before{content:"\f74b"}.bi-filetype-js::before{content:"\f74c"}.bi-filetype-jsx::before{content:"\f74d"}.bi-filetype-key::before{content:"\f74e"}.bi-filetype-m4p::before{content:"\f74f"}.bi-filetype-md::before{content:"\f750"}.bi-filetype-mdx::before{content:"\f751"}.bi-filetype-mov::before{content:"\f752"}.bi-filetype-mp3::before{content:"\f753"}.bi-filetype-mp4::before{content:"\f754"}.bi-filetype-otf::before{content:"\f755"}.bi-filetype-pdf::before{content:"\f756"}.bi-filetype-php::before{content:"\f757"}.bi-filetype-png::before{content:"\f758"}.bi-filetype-ppt-1::before{content:"\f759"}.bi-filetype-ppt::before{content:"\f75a"}.bi-filetype-psd::before{content:"\f75b"}.bi-filetype-py::before{content:"\f75c"}.bi-filetype-raw::before{content:"\f75d"}.bi-filetype-rb::before{content:"\f75e"}.bi-filetype-sass::before{content:"\f75f"}.bi-filetype-scss::before{content:"\f760"}.bi-filetype-sh::before{content:"\f761"}.bi-filetype-svg::before{content:"\f762"}.bi-filetype-tiff::before{content:"\f763"}.bi-filetype-tsx::before{content:"\f764"}.bi-filetype-ttf::before{content:"\f765"}.bi-filetype-txt::before{content:"\f766"}.bi-filetype-wav::before{content:"\f767"}.bi-filetype-woff::before{content:"\f768"}.bi-filetype-xls-1::before{content:"\f769"}.bi-filetype-xls::before{content:"\f76a"}.bi-filetype-xml::before{content:"\f76b"}.bi-filetype-yml::before{content:"\f76c"}.bi-heart-arrow::before{content:"\f76d"}.bi-heart-pulse-fill::before{content:"\f76e"}.bi-heart-pulse::before{content:"\f76f"}.bi-heartbreak-fill::before{content:"\f770"}.bi-heartbreak::before{content:"\f771"}.bi-hearts::before{content:"\f772"}.bi-hospital-fill::before{content:"\f773"}.bi-hospital::before{content:"\f774"}.bi-house-heart-fill::before{content:"\f775"}.bi-house-heart::before{content:"\f776"}.bi-incognito::before{content:"\f777"}.bi-magnet-fill::before{content:"\f778"}.bi-magnet::before{content:"\f779"}.bi-person-heart::before{content:"\f77a"}.bi-person-hearts::before{content:"\f77b"}.bi-phone-flip::before{content:"\f77c"}.bi-plugin::before{content:"\f77d"}.bi-postage-fill::before{content:"\f77e"}.bi-postage-heart-fill::before{content:"\f77f"}.bi-postage-heart::before{content:"\f780"}.bi-postage::before{content:"\f781"}.bi-postcard-fill::before{content:"\f782"}.bi-postcard-heart-fill::before{content:"\f783"}.bi-postcard-heart::before{content:"\f784"}.bi-postcard::before{content:"\f785"}.bi-search-heart-fill::before{content:"\f786"}.bi-search-heart::before{content:"\f787"}.bi-sliders2-vertical::before{content:"\f788"}.bi-sliders2::before{content:"\f789"}.bi-trash3-fill::before{content:"\f78a"}.bi-trash3::before{content:"\f78b"}.bi-valentine::before{content:"\f78c"}.bi-valentine2::before{content:"\f78d"}.bi-wrench-adjustable-circle-fill::before{content:"\f78e"}.bi-wrench-adjustable-circle::before{content:"\f78f"}.bi-wrench-adjustable::before{content:"\f790"}.bi-filetype-json::before{content:"\f791"}.bi-filetype-pptx::before{content:"\f792"}.bi-filetype-xlsx::before{content:"\f793"}.bi-1-circle-1::before{content:"\f794"}.bi-1-circle-fill-1::before{content:"\f795"}.bi-1-circle-fill::before{content:"\f796"}.bi-1-circle::before{content:"\f797"}.bi-1-square-fill::before{content:"\f798"}.bi-1-square::before{content:"\f799"}.bi-2-circle-1::before{content:"\f79a"}.bi-2-circle-fill-1::before{content:"\f79b"}.bi-2-circle-fill::before{content:"\f79c"}.bi-2-circle::before{content:"\f79d"}.bi-2-square-fill::before{content:"\f79e"}.bi-2-square::before{content:"\f79f"}.bi-3-circle-1::before{content:"\f7a0"}.bi-3-circle-fill-1::before{content:"\f7a1"}.bi-3-circle-fill::before{content:"\f7a2"}.bi-3-circle::before{content:"\f7a3"}.bi-3-square-fill::before{content:"\f7a4"}.bi-3-square::before{content:"\f7a5"}.bi-4-circle-1::before{content:"\f7a6"}.bi-4-circle-fill-1::before{content:"\f7a7"}.bi-4-circle-fill::before{content:"\f7a8"}.bi-4-circle::before{content:"\f7a9"}.bi-4-square-fill::before{content:"\f7aa"}.bi-4-square::before{content:"\f7ab"}.bi-5-circle-1::before{content:"\f7ac"}.bi-5-circle-fill-1::before{content:"\f7ad"}.bi-5-circle-fill::before{content:"\f7ae"}.bi-5-circle::before{content:"\f7af"}.bi-5-square-fill::before{content:"\f7b0"}.bi-5-square::before{content:"\f7b1"}.bi-6-circle-1::before{content:"\f7b2"}.bi-6-circle-fill-1::before{content:"\f7b3"}.bi-6-circle-fill::before{content:"\f7b4"}.bi-6-circle::before{content:"\f7b5"}.bi-6-square-fill::before{content:"\f7b6"}.bi-6-square::before{content:"\f7b7"}.bi-7-circle-1::before{content:"\f7b8"}.bi-7-circle-fill-1::before{content:"\f7b9"}.bi-7-circle-fill::before{content:"\f7ba"}.bi-7-circle::before{content:"\f7bb"}.bi-7-square-fill::before{content:"\f7bc"}.bi-7-square::before{content:"\f7bd"}.bi-8-circle-1::before{content:"\f7be"}.bi-8-circle-fill-1::before{content:"\f7bf"}.bi-8-circle-fill::before{content:"\f7c0"}.bi-8-circle::before{content:"\f7c1"}.bi-8-square-fill::before{content:"\f7c2"}.bi-8-square::before{content:"\f7c3"}.bi-9-circle-1::before{content:"\f7c4"}.bi-9-circle-fill-1::before{content:"\f7c5"}.bi-9-circle-fill::before{content:"\f7c6"}.bi-9-circle::before{content:"\f7c7"}.bi-9-square-fill::before{content:"\f7c8"}.bi-9-square::before{content:"\f7c9"}.bi-airplane-engines-fill::before{content:"\f7ca"}.bi-airplane-engines::before{content:"\f7cb"}.bi-airplane-fill::before{content:"\f7cc"}.bi-airplane::before{content:"\f7cd"}.bi-alexa::before{content:"\f7ce"}.bi-alipay::before{content:"\f7cf"}.bi-android::before{content:"\f7d0"}.bi-android2::before{content:"\f7d1"}.bi-box-fill::before{content:"\f7d2"}.bi-box-seam-fill::before{content:"\f7d3"}.bi-browser-chrome::before{content:"\f7d4"}.bi-browser-edge::before{content:"\f7d5"}.bi-browser-firefox::before{content:"\f7d6"}.bi-browser-safari::before{content:"\f7d7"}.bi-c-circle-1::before{content:"\f7d8"}.bi-c-circle-fill-1::before{content:"\f7d9"}.bi-c-circle-fill::before{content:"\f7da"}.bi-c-circle::before{content:"\f7db"}.bi-c-square-fill::before{content:"\f7dc"}.bi-c-square::before{content:"\f7dd"}.bi-capsule-pill::before{content:"\f7de"}.bi-capsule::before{content:"\f7df"}.bi-car-front-fill::before{content:"\f7e0"}.bi-car-front::before{content:"\f7e1"}.bi-cassette-fill::before{content:"\f7e2"}.bi-cassette::before{content:"\f7e3"}.bi-cc-circle-1::before{content:"\f7e4"}.bi-cc-circle-fill-1::before{content:"\f7e5"}.bi-cc-circle-fill::before{content:"\f7e6"}.bi-cc-circle::before{content:"\f7e7"}.bi-cc-square-fill::before{content:"\f7e8"}.bi-cc-square::before{content:"\f7e9"}.bi-cup-hot-fill::before{content:"\f7ea"}.bi-cup-hot::before{content:"\f7eb"}.bi-currency-rupee::before{content:"\f7ec"}.bi-dropbox::before{content:"\f7ed"}.bi-escape::before{content:"\f7ee"}.bi-fast-forward-btn-fill::before{content:"\f7ef"}.bi-fast-forward-btn::before{content:"\f7f0"}.bi-fast-forward-circle-fill::before{content:"\f7f1"}.bi-fast-forward-circle::before{content:"\f7f2"}.bi-fast-forward-fill::before{content:"\f7f3"}.bi-fast-forward::before{content:"\f7f4"}.bi-filetype-sql::before{content:"\f7f5"}.bi-fire::before{content:"\f7f6"}.bi-google-play::before{content:"\f7f7"}.bi-h-circle-1::before{content:"\f7f8"}.bi-h-circle-fill-1::before{content:"\f7f9"}.bi-h-circle-fill::before{content:"\f7fa"}.bi-h-circle::before{content:"\f7fb"}.bi-h-square-fill::before{content:"\f7fc"}.bi-h-square::before{content:"\f7fd"}.bi-indent::before{content:"\f7fe"}.bi-lungs-fill::before{content:"\f7ff"}.bi-lungs::before{content:"\f800"}.bi-microsoft-teams::before{content:"\f801"}.bi-p-circle-1::before{content:"\f802"}.bi-p-circle-fill-1::before{content:"\f803"}.bi-p-circle-fill::before{content:"\f804"}.bi-p-circle::before{content:"\f805"}.bi-p-square-fill::before{content:"\f806"}.bi-p-square::before{content:"\f807"}.bi-pass-fill::before{content:"\f808"}.bi-pass::before{content:"\f809"}.bi-prescription::before{content:"\f80a"}.bi-prescription2::before{content:"\f80b"}.bi-r-circle-1::before{content:"\f80c"}.bi-r-circle-fill-1::before{content:"\f80d"}.bi-r-circle-fill::before{content:"\f80e"}.bi-r-circle::before{content:"\f80f"}.bi-r-square-fill::before{content:"\f810"}.bi-r-square::before{content:"\f811"}.bi-repeat-1::before{content:"\f812"}.bi-repeat::before{content:"\f813"}.bi-rewind-btn-fill::before{content:"\f814"}.bi-rewind-btn::before{content:"\f815"}.bi-rewind-circle-fill::before{content:"\f816"}.bi-rewind-circle::before{content:"\f817"}.bi-rewind-fill::before{content:"\f818"}.bi-rewind::before{content:"\f819"}.bi-train-freight-front-fill::before{content:"\f81a"}.bi-train-freight-front::before{content:"\f81b"}.bi-train-front-fill::before{content:"\f81c"}.bi-train-front::before{content:"\f81d"}.bi-train-lightrail-front-fill::before{content:"\f81e"}.bi-train-lightrail-front::before{content:"\f81f"}.bi-truck-front-fill::before{content:"\f820"}.bi-truck-front::before{content:"\f821"}.bi-ubuntu::before{content:"\f822"}.bi-unindent::before{content:"\f823"}.bi-unity::before{content:"\f824"}.bi-universal-access-circle::before{content:"\f825"}.bi-universal-access::before{content:"\f826"}.bi-virus::before{content:"\f827"}.bi-virus2::before{content:"\f828"}.bi-wechat::before{content:"\f829"}.bi-yelp::before{content:"\f82a"}.bi-sign-stop-fill::before{content:"\f82b"}.bi-sign-stop-lights-fill::before{content:"\f82c"}.bi-sign-stop-lights::before{content:"\f82d"}.bi-sign-stop::before{content:"\f82e"}.bi-sign-turn-left-fill::before{content:"\f82f"}.bi-sign-turn-left::before{content:"\f830"}.bi-sign-turn-right-fill::before{content:"\f831"}.bi-sign-turn-right::before{content:"\f832"}.bi-sign-turn-slight-left-fill::before{content:"\f833"}.bi-sign-turn-slight-left::before{content:"\f834"}.bi-sign-turn-slight-right-fill::before{content:"\f835"}.bi-sign-turn-slight-right::before{content:"\f836"}.bi-sign-yield-fill::before{content:"\f837"}.bi-sign-yield::before{content:"\f838"}.bi-ev-station-fill::before{content:"\f839"}.bi-ev-station::before{content:"\f83a"}.bi-fuel-pump-diesel-fill::before{content:"\f83b"}.bi-fuel-pump-diesel::before{content:"\f83c"}.bi-fuel-pump-fill::before{content:"\f83d"}.bi-fuel-pump::before{content:"\f83e"} -/*# sourceMappingURL=/sm/d206ec34385c8521671a1967f920c286a45bf739842d26fd53d751161e52d8f9.map */ \ No newline at end of file +/*! + * Bootstrap Icons v1.11.3 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */@font-face{font-display:block;font-family:bootstrap-icons;src:url("fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format("woff2"),url("fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff")}.bi::before,[class*=" bi-"]::before,[class^=bi-]::before{display:inline-block;font-family:bootstrap-icons!important;font-style:normal;font-weight:400!important;font-variant:normal;text-transform:none;line-height:1;vertical-align:-.125em;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.bi-123::before{content:"\f67f"}.bi-alarm-fill::before{content:"\f101"}.bi-alarm::before{content:"\f102"}.bi-align-bottom::before{content:"\f103"}.bi-align-center::before{content:"\f104"}.bi-align-end::before{content:"\f105"}.bi-align-middle::before{content:"\f106"}.bi-align-start::before{content:"\f107"}.bi-align-top::before{content:"\f108"}.bi-alt::before{content:"\f109"}.bi-app-indicator::before{content:"\f10a"}.bi-app::before{content:"\f10b"}.bi-archive-fill::before{content:"\f10c"}.bi-archive::before{content:"\f10d"}.bi-arrow-90deg-down::before{content:"\f10e"}.bi-arrow-90deg-left::before{content:"\f10f"}.bi-arrow-90deg-right::before{content:"\f110"}.bi-arrow-90deg-up::before{content:"\f111"}.bi-arrow-bar-down::before{content:"\f112"}.bi-arrow-bar-left::before{content:"\f113"}.bi-arrow-bar-right::before{content:"\f114"}.bi-arrow-bar-up::before{content:"\f115"}.bi-arrow-clockwise::before{content:"\f116"}.bi-arrow-counterclockwise::before{content:"\f117"}.bi-arrow-down-circle-fill::before{content:"\f118"}.bi-arrow-down-circle::before{content:"\f119"}.bi-arrow-down-left-circle-fill::before{content:"\f11a"}.bi-arrow-down-left-circle::before{content:"\f11b"}.bi-arrow-down-left-square-fill::before{content:"\f11c"}.bi-arrow-down-left-square::before{content:"\f11d"}.bi-arrow-down-left::before{content:"\f11e"}.bi-arrow-down-right-circle-fill::before{content:"\f11f"}.bi-arrow-down-right-circle::before{content:"\f120"}.bi-arrow-down-right-square-fill::before{content:"\f121"}.bi-arrow-down-right-square::before{content:"\f122"}.bi-arrow-down-right::before{content:"\f123"}.bi-arrow-down-short::before{content:"\f124"}.bi-arrow-down-square-fill::before{content:"\f125"}.bi-arrow-down-square::before{content:"\f126"}.bi-arrow-down-up::before{content:"\f127"}.bi-arrow-down::before{content:"\f128"}.bi-arrow-left-circle-fill::before{content:"\f129"}.bi-arrow-left-circle::before{content:"\f12a"}.bi-arrow-left-right::before{content:"\f12b"}.bi-arrow-left-short::before{content:"\f12c"}.bi-arrow-left-square-fill::before{content:"\f12d"}.bi-arrow-left-square::before{content:"\f12e"}.bi-arrow-left::before{content:"\f12f"}.bi-arrow-repeat::before{content:"\f130"}.bi-arrow-return-left::before{content:"\f131"}.bi-arrow-return-right::before{content:"\f132"}.bi-arrow-right-circle-fill::before{content:"\f133"}.bi-arrow-right-circle::before{content:"\f134"}.bi-arrow-right-short::before{content:"\f135"}.bi-arrow-right-square-fill::before{content:"\f136"}.bi-arrow-right-square::before{content:"\f137"}.bi-arrow-right::before{content:"\f138"}.bi-arrow-up-circle-fill::before{content:"\f139"}.bi-arrow-up-circle::before{content:"\f13a"}.bi-arrow-up-left-circle-fill::before{content:"\f13b"}.bi-arrow-up-left-circle::before{content:"\f13c"}.bi-arrow-up-left-square-fill::before{content:"\f13d"}.bi-arrow-up-left-square::before{content:"\f13e"}.bi-arrow-up-left::before{content:"\f13f"}.bi-arrow-up-right-circle-fill::before{content:"\f140"}.bi-arrow-up-right-circle::before{content:"\f141"}.bi-arrow-up-right-square-fill::before{content:"\f142"}.bi-arrow-up-right-square::before{content:"\f143"}.bi-arrow-up-right::before{content:"\f144"}.bi-arrow-up-short::before{content:"\f145"}.bi-arrow-up-square-fill::before{content:"\f146"}.bi-arrow-up-square::before{content:"\f147"}.bi-arrow-up::before{content:"\f148"}.bi-arrows-angle-contract::before{content:"\f149"}.bi-arrows-angle-expand::before{content:"\f14a"}.bi-arrows-collapse::before{content:"\f14b"}.bi-arrows-expand::before{content:"\f14c"}.bi-arrows-fullscreen::before{content:"\f14d"}.bi-arrows-move::before{content:"\f14e"}.bi-aspect-ratio-fill::before{content:"\f14f"}.bi-aspect-ratio::before{content:"\f150"}.bi-asterisk::before{content:"\f151"}.bi-at::before{content:"\f152"}.bi-award-fill::before{content:"\f153"}.bi-award::before{content:"\f154"}.bi-back::before{content:"\f155"}.bi-backspace-fill::before{content:"\f156"}.bi-backspace-reverse-fill::before{content:"\f157"}.bi-backspace-reverse::before{content:"\f158"}.bi-backspace::before{content:"\f159"}.bi-badge-3d-fill::before{content:"\f15a"}.bi-badge-3d::before{content:"\f15b"}.bi-badge-4k-fill::before{content:"\f15c"}.bi-badge-4k::before{content:"\f15d"}.bi-badge-8k-fill::before{content:"\f15e"}.bi-badge-8k::before{content:"\f15f"}.bi-badge-ad-fill::before{content:"\f160"}.bi-badge-ad::before{content:"\f161"}.bi-badge-ar-fill::before{content:"\f162"}.bi-badge-ar::before{content:"\f163"}.bi-badge-cc-fill::before{content:"\f164"}.bi-badge-cc::before{content:"\f165"}.bi-badge-hd-fill::before{content:"\f166"}.bi-badge-hd::before{content:"\f167"}.bi-badge-tm-fill::before{content:"\f168"}.bi-badge-tm::before{content:"\f169"}.bi-badge-vo-fill::before{content:"\f16a"}.bi-badge-vo::before{content:"\f16b"}.bi-badge-vr-fill::before{content:"\f16c"}.bi-badge-vr::before{content:"\f16d"}.bi-badge-wc-fill::before{content:"\f16e"}.bi-badge-wc::before{content:"\f16f"}.bi-bag-check-fill::before{content:"\f170"}.bi-bag-check::before{content:"\f171"}.bi-bag-dash-fill::before{content:"\f172"}.bi-bag-dash::before{content:"\f173"}.bi-bag-fill::before{content:"\f174"}.bi-bag-plus-fill::before{content:"\f175"}.bi-bag-plus::before{content:"\f176"}.bi-bag-x-fill::before{content:"\f177"}.bi-bag-x::before{content:"\f178"}.bi-bag::before{content:"\f179"}.bi-bar-chart-fill::before{content:"\f17a"}.bi-bar-chart-line-fill::before{content:"\f17b"}.bi-bar-chart-line::before{content:"\f17c"}.bi-bar-chart-steps::before{content:"\f17d"}.bi-bar-chart::before{content:"\f17e"}.bi-basket-fill::before{content:"\f17f"}.bi-basket::before{content:"\f180"}.bi-basket2-fill::before{content:"\f181"}.bi-basket2::before{content:"\f182"}.bi-basket3-fill::before{content:"\f183"}.bi-basket3::before{content:"\f184"}.bi-battery-charging::before{content:"\f185"}.bi-battery-full::before{content:"\f186"}.bi-battery-half::before{content:"\f187"}.bi-battery::before{content:"\f188"}.bi-bell-fill::before{content:"\f189"}.bi-bell::before{content:"\f18a"}.bi-bezier::before{content:"\f18b"}.bi-bezier2::before{content:"\f18c"}.bi-bicycle::before{content:"\f18d"}.bi-binoculars-fill::before{content:"\f18e"}.bi-binoculars::before{content:"\f18f"}.bi-blockquote-left::before{content:"\f190"}.bi-blockquote-right::before{content:"\f191"}.bi-book-fill::before{content:"\f192"}.bi-book-half::before{content:"\f193"}.bi-book::before{content:"\f194"}.bi-bookmark-check-fill::before{content:"\f195"}.bi-bookmark-check::before{content:"\f196"}.bi-bookmark-dash-fill::before{content:"\f197"}.bi-bookmark-dash::before{content:"\f198"}.bi-bookmark-fill::before{content:"\f199"}.bi-bookmark-heart-fill::before{content:"\f19a"}.bi-bookmark-heart::before{content:"\f19b"}.bi-bookmark-plus-fill::before{content:"\f19c"}.bi-bookmark-plus::before{content:"\f19d"}.bi-bookmark-star-fill::before{content:"\f19e"}.bi-bookmark-star::before{content:"\f19f"}.bi-bookmark-x-fill::before{content:"\f1a0"}.bi-bookmark-x::before{content:"\f1a1"}.bi-bookmark::before{content:"\f1a2"}.bi-bookmarks-fill::before{content:"\f1a3"}.bi-bookmarks::before{content:"\f1a4"}.bi-bookshelf::before{content:"\f1a5"}.bi-bootstrap-fill::before{content:"\f1a6"}.bi-bootstrap-reboot::before{content:"\f1a7"}.bi-bootstrap::before{content:"\f1a8"}.bi-border-all::before{content:"\f1a9"}.bi-border-bottom::before{content:"\f1aa"}.bi-border-center::before{content:"\f1ab"}.bi-border-inner::before{content:"\f1ac"}.bi-border-left::before{content:"\f1ad"}.bi-border-middle::before{content:"\f1ae"}.bi-border-outer::before{content:"\f1af"}.bi-border-right::before{content:"\f1b0"}.bi-border-style::before{content:"\f1b1"}.bi-border-top::before{content:"\f1b2"}.bi-border-width::before{content:"\f1b3"}.bi-border::before{content:"\f1b4"}.bi-bounding-box-circles::before{content:"\f1b5"}.bi-bounding-box::before{content:"\f1b6"}.bi-box-arrow-down-left::before{content:"\f1b7"}.bi-box-arrow-down-right::before{content:"\f1b8"}.bi-box-arrow-down::before{content:"\f1b9"}.bi-box-arrow-in-down-left::before{content:"\f1ba"}.bi-box-arrow-in-down-right::before{content:"\f1bb"}.bi-box-arrow-in-down::before{content:"\f1bc"}.bi-box-arrow-in-left::before{content:"\f1bd"}.bi-box-arrow-in-right::before{content:"\f1be"}.bi-box-arrow-in-up-left::before{content:"\f1bf"}.bi-box-arrow-in-up-right::before{content:"\f1c0"}.bi-box-arrow-in-up::before{content:"\f1c1"}.bi-box-arrow-left::before{content:"\f1c2"}.bi-box-arrow-right::before{content:"\f1c3"}.bi-box-arrow-up-left::before{content:"\f1c4"}.bi-box-arrow-up-right::before{content:"\f1c5"}.bi-box-arrow-up::before{content:"\f1c6"}.bi-box-seam::before{content:"\f1c7"}.bi-box::before{content:"\f1c8"}.bi-braces::before{content:"\f1c9"}.bi-bricks::before{content:"\f1ca"}.bi-briefcase-fill::before{content:"\f1cb"}.bi-briefcase::before{content:"\f1cc"}.bi-brightness-alt-high-fill::before{content:"\f1cd"}.bi-brightness-alt-high::before{content:"\f1ce"}.bi-brightness-alt-low-fill::before{content:"\f1cf"}.bi-brightness-alt-low::before{content:"\f1d0"}.bi-brightness-high-fill::before{content:"\f1d1"}.bi-brightness-high::before{content:"\f1d2"}.bi-brightness-low-fill::before{content:"\f1d3"}.bi-brightness-low::before{content:"\f1d4"}.bi-broadcast-pin::before{content:"\f1d5"}.bi-broadcast::before{content:"\f1d6"}.bi-brush-fill::before{content:"\f1d7"}.bi-brush::before{content:"\f1d8"}.bi-bucket-fill::before{content:"\f1d9"}.bi-bucket::before{content:"\f1da"}.bi-bug-fill::before{content:"\f1db"}.bi-bug::before{content:"\f1dc"}.bi-building::before{content:"\f1dd"}.bi-bullseye::before{content:"\f1de"}.bi-calculator-fill::before{content:"\f1df"}.bi-calculator::before{content:"\f1e0"}.bi-calendar-check-fill::before{content:"\f1e1"}.bi-calendar-check::before{content:"\f1e2"}.bi-calendar-date-fill::before{content:"\f1e3"}.bi-calendar-date::before{content:"\f1e4"}.bi-calendar-day-fill::before{content:"\f1e5"}.bi-calendar-day::before{content:"\f1e6"}.bi-calendar-event-fill::before{content:"\f1e7"}.bi-calendar-event::before{content:"\f1e8"}.bi-calendar-fill::before{content:"\f1e9"}.bi-calendar-minus-fill::before{content:"\f1ea"}.bi-calendar-minus::before{content:"\f1eb"}.bi-calendar-month-fill::before{content:"\f1ec"}.bi-calendar-month::before{content:"\f1ed"}.bi-calendar-plus-fill::before{content:"\f1ee"}.bi-calendar-plus::before{content:"\f1ef"}.bi-calendar-range-fill::before{content:"\f1f0"}.bi-calendar-range::before{content:"\f1f1"}.bi-calendar-week-fill::before{content:"\f1f2"}.bi-calendar-week::before{content:"\f1f3"}.bi-calendar-x-fill::before{content:"\f1f4"}.bi-calendar-x::before{content:"\f1f5"}.bi-calendar::before{content:"\f1f6"}.bi-calendar2-check-fill::before{content:"\f1f7"}.bi-calendar2-check::before{content:"\f1f8"}.bi-calendar2-date-fill::before{content:"\f1f9"}.bi-calendar2-date::before{content:"\f1fa"}.bi-calendar2-day-fill::before{content:"\f1fb"}.bi-calendar2-day::before{content:"\f1fc"}.bi-calendar2-event-fill::before{content:"\f1fd"}.bi-calendar2-event::before{content:"\f1fe"}.bi-calendar2-fill::before{content:"\f1ff"}.bi-calendar2-minus-fill::before{content:"\f200"}.bi-calendar2-minus::before{content:"\f201"}.bi-calendar2-month-fill::before{content:"\f202"}.bi-calendar2-month::before{content:"\f203"}.bi-calendar2-plus-fill::before{content:"\f204"}.bi-calendar2-plus::before{content:"\f205"}.bi-calendar2-range-fill::before{content:"\f206"}.bi-calendar2-range::before{content:"\f207"}.bi-calendar2-week-fill::before{content:"\f208"}.bi-calendar2-week::before{content:"\f209"}.bi-calendar2-x-fill::before{content:"\f20a"}.bi-calendar2-x::before{content:"\f20b"}.bi-calendar2::before{content:"\f20c"}.bi-calendar3-event-fill::before{content:"\f20d"}.bi-calendar3-event::before{content:"\f20e"}.bi-calendar3-fill::before{content:"\f20f"}.bi-calendar3-range-fill::before{content:"\f210"}.bi-calendar3-range::before{content:"\f211"}.bi-calendar3-week-fill::before{content:"\f212"}.bi-calendar3-week::before{content:"\f213"}.bi-calendar3::before{content:"\f214"}.bi-calendar4-event::before{content:"\f215"}.bi-calendar4-range::before{content:"\f216"}.bi-calendar4-week::before{content:"\f217"}.bi-calendar4::before{content:"\f218"}.bi-camera-fill::before{content:"\f219"}.bi-camera-reels-fill::before{content:"\f21a"}.bi-camera-reels::before{content:"\f21b"}.bi-camera-video-fill::before{content:"\f21c"}.bi-camera-video-off-fill::before{content:"\f21d"}.bi-camera-video-off::before{content:"\f21e"}.bi-camera-video::before{content:"\f21f"}.bi-camera::before{content:"\f220"}.bi-camera2::before{content:"\f221"}.bi-capslock-fill::before{content:"\f222"}.bi-capslock::before{content:"\f223"}.bi-card-checklist::before{content:"\f224"}.bi-card-heading::before{content:"\f225"}.bi-card-image::before{content:"\f226"}.bi-card-list::before{content:"\f227"}.bi-card-text::before{content:"\f228"}.bi-caret-down-fill::before{content:"\f229"}.bi-caret-down-square-fill::before{content:"\f22a"}.bi-caret-down-square::before{content:"\f22b"}.bi-caret-down::before{content:"\f22c"}.bi-caret-left-fill::before{content:"\f22d"}.bi-caret-left-square-fill::before{content:"\f22e"}.bi-caret-left-square::before{content:"\f22f"}.bi-caret-left::before{content:"\f230"}.bi-caret-right-fill::before{content:"\f231"}.bi-caret-right-square-fill::before{content:"\f232"}.bi-caret-right-square::before{content:"\f233"}.bi-caret-right::before{content:"\f234"}.bi-caret-up-fill::before{content:"\f235"}.bi-caret-up-square-fill::before{content:"\f236"}.bi-caret-up-square::before{content:"\f237"}.bi-caret-up::before{content:"\f238"}.bi-cart-check-fill::before{content:"\f239"}.bi-cart-check::before{content:"\f23a"}.bi-cart-dash-fill::before{content:"\f23b"}.bi-cart-dash::before{content:"\f23c"}.bi-cart-fill::before{content:"\f23d"}.bi-cart-plus-fill::before{content:"\f23e"}.bi-cart-plus::before{content:"\f23f"}.bi-cart-x-fill::before{content:"\f240"}.bi-cart-x::before{content:"\f241"}.bi-cart::before{content:"\f242"}.bi-cart2::before{content:"\f243"}.bi-cart3::before{content:"\f244"}.bi-cart4::before{content:"\f245"}.bi-cash-stack::before{content:"\f246"}.bi-cash::before{content:"\f247"}.bi-cast::before{content:"\f248"}.bi-chat-dots-fill::before{content:"\f249"}.bi-chat-dots::before{content:"\f24a"}.bi-chat-fill::before{content:"\f24b"}.bi-chat-left-dots-fill::before{content:"\f24c"}.bi-chat-left-dots::before{content:"\f24d"}.bi-chat-left-fill::before{content:"\f24e"}.bi-chat-left-quote-fill::before{content:"\f24f"}.bi-chat-left-quote::before{content:"\f250"}.bi-chat-left-text-fill::before{content:"\f251"}.bi-chat-left-text::before{content:"\f252"}.bi-chat-left::before{content:"\f253"}.bi-chat-quote-fill::before{content:"\f254"}.bi-chat-quote::before{content:"\f255"}.bi-chat-right-dots-fill::before{content:"\f256"}.bi-chat-right-dots::before{content:"\f257"}.bi-chat-right-fill::before{content:"\f258"}.bi-chat-right-quote-fill::before{content:"\f259"}.bi-chat-right-quote::before{content:"\f25a"}.bi-chat-right-text-fill::before{content:"\f25b"}.bi-chat-right-text::before{content:"\f25c"}.bi-chat-right::before{content:"\f25d"}.bi-chat-square-dots-fill::before{content:"\f25e"}.bi-chat-square-dots::before{content:"\f25f"}.bi-chat-square-fill::before{content:"\f260"}.bi-chat-square-quote-fill::before{content:"\f261"}.bi-chat-square-quote::before{content:"\f262"}.bi-chat-square-text-fill::before{content:"\f263"}.bi-chat-square-text::before{content:"\f264"}.bi-chat-square::before{content:"\f265"}.bi-chat-text-fill::before{content:"\f266"}.bi-chat-text::before{content:"\f267"}.bi-chat::before{content:"\f268"}.bi-check-all::before{content:"\f269"}.bi-check-circle-fill::before{content:"\f26a"}.bi-check-circle::before{content:"\f26b"}.bi-check-square-fill::before{content:"\f26c"}.bi-check-square::before{content:"\f26d"}.bi-check::before{content:"\f26e"}.bi-check2-all::before{content:"\f26f"}.bi-check2-circle::before{content:"\f270"}.bi-check2-square::before{content:"\f271"}.bi-check2::before{content:"\f272"}.bi-chevron-bar-contract::before{content:"\f273"}.bi-chevron-bar-down::before{content:"\f274"}.bi-chevron-bar-expand::before{content:"\f275"}.bi-chevron-bar-left::before{content:"\f276"}.bi-chevron-bar-right::before{content:"\f277"}.bi-chevron-bar-up::before{content:"\f278"}.bi-chevron-compact-down::before{content:"\f279"}.bi-chevron-compact-left::before{content:"\f27a"}.bi-chevron-compact-right::before{content:"\f27b"}.bi-chevron-compact-up::before{content:"\f27c"}.bi-chevron-contract::before{content:"\f27d"}.bi-chevron-double-down::before{content:"\f27e"}.bi-chevron-double-left::before{content:"\f27f"}.bi-chevron-double-right::before{content:"\f280"}.bi-chevron-double-up::before{content:"\f281"}.bi-chevron-down::before{content:"\f282"}.bi-chevron-expand::before{content:"\f283"}.bi-chevron-left::before{content:"\f284"}.bi-chevron-right::before{content:"\f285"}.bi-chevron-up::before{content:"\f286"}.bi-circle-fill::before{content:"\f287"}.bi-circle-half::before{content:"\f288"}.bi-circle-square::before{content:"\f289"}.bi-circle::before{content:"\f28a"}.bi-clipboard-check::before{content:"\f28b"}.bi-clipboard-data::before{content:"\f28c"}.bi-clipboard-minus::before{content:"\f28d"}.bi-clipboard-plus::before{content:"\f28e"}.bi-clipboard-x::before{content:"\f28f"}.bi-clipboard::before{content:"\f290"}.bi-clock-fill::before{content:"\f291"}.bi-clock-history::before{content:"\f292"}.bi-clock::before{content:"\f293"}.bi-cloud-arrow-down-fill::before{content:"\f294"}.bi-cloud-arrow-down::before{content:"\f295"}.bi-cloud-arrow-up-fill::before{content:"\f296"}.bi-cloud-arrow-up::before{content:"\f297"}.bi-cloud-check-fill::before{content:"\f298"}.bi-cloud-check::before{content:"\f299"}.bi-cloud-download-fill::before{content:"\f29a"}.bi-cloud-download::before{content:"\f29b"}.bi-cloud-drizzle-fill::before{content:"\f29c"}.bi-cloud-drizzle::before{content:"\f29d"}.bi-cloud-fill::before{content:"\f29e"}.bi-cloud-fog-fill::before{content:"\f29f"}.bi-cloud-fog::before{content:"\f2a0"}.bi-cloud-fog2-fill::before{content:"\f2a1"}.bi-cloud-fog2::before{content:"\f2a2"}.bi-cloud-hail-fill::before{content:"\f2a3"}.bi-cloud-hail::before{content:"\f2a4"}.bi-cloud-haze-fill::before{content:"\f2a6"}.bi-cloud-haze::before{content:"\f2a7"}.bi-cloud-haze2-fill::before{content:"\f2a8"}.bi-cloud-lightning-fill::before{content:"\f2a9"}.bi-cloud-lightning-rain-fill::before{content:"\f2aa"}.bi-cloud-lightning-rain::before{content:"\f2ab"}.bi-cloud-lightning::before{content:"\f2ac"}.bi-cloud-minus-fill::before{content:"\f2ad"}.bi-cloud-minus::before{content:"\f2ae"}.bi-cloud-moon-fill::before{content:"\f2af"}.bi-cloud-moon::before{content:"\f2b0"}.bi-cloud-plus-fill::before{content:"\f2b1"}.bi-cloud-plus::before{content:"\f2b2"}.bi-cloud-rain-fill::before{content:"\f2b3"}.bi-cloud-rain-heavy-fill::before{content:"\f2b4"}.bi-cloud-rain-heavy::before{content:"\f2b5"}.bi-cloud-rain::before{content:"\f2b6"}.bi-cloud-slash-fill::before{content:"\f2b7"}.bi-cloud-slash::before{content:"\f2b8"}.bi-cloud-sleet-fill::before{content:"\f2b9"}.bi-cloud-sleet::before{content:"\f2ba"}.bi-cloud-snow-fill::before{content:"\f2bb"}.bi-cloud-snow::before{content:"\f2bc"}.bi-cloud-sun-fill::before{content:"\f2bd"}.bi-cloud-sun::before{content:"\f2be"}.bi-cloud-upload-fill::before{content:"\f2bf"}.bi-cloud-upload::before{content:"\f2c0"}.bi-cloud::before{content:"\f2c1"}.bi-clouds-fill::before{content:"\f2c2"}.bi-clouds::before{content:"\f2c3"}.bi-cloudy-fill::before{content:"\f2c4"}.bi-cloudy::before{content:"\f2c5"}.bi-code-slash::before{content:"\f2c6"}.bi-code-square::before{content:"\f2c7"}.bi-code::before{content:"\f2c8"}.bi-collection-fill::before{content:"\f2c9"}.bi-collection-play-fill::before{content:"\f2ca"}.bi-collection-play::before{content:"\f2cb"}.bi-collection::before{content:"\f2cc"}.bi-columns-gap::before{content:"\f2cd"}.bi-columns::before{content:"\f2ce"}.bi-command::before{content:"\f2cf"}.bi-compass-fill::before{content:"\f2d0"}.bi-compass::before{content:"\f2d1"}.bi-cone-striped::before{content:"\f2d2"}.bi-cone::before{content:"\f2d3"}.bi-controller::before{content:"\f2d4"}.bi-cpu-fill::before{content:"\f2d5"}.bi-cpu::before{content:"\f2d6"}.bi-credit-card-2-back-fill::before{content:"\f2d7"}.bi-credit-card-2-back::before{content:"\f2d8"}.bi-credit-card-2-front-fill::before{content:"\f2d9"}.bi-credit-card-2-front::before{content:"\f2da"}.bi-credit-card-fill::before{content:"\f2db"}.bi-credit-card::before{content:"\f2dc"}.bi-crop::before{content:"\f2dd"}.bi-cup-fill::before{content:"\f2de"}.bi-cup-straw::before{content:"\f2df"}.bi-cup::before{content:"\f2e0"}.bi-cursor-fill::before{content:"\f2e1"}.bi-cursor-text::before{content:"\f2e2"}.bi-cursor::before{content:"\f2e3"}.bi-dash-circle-dotted::before{content:"\f2e4"}.bi-dash-circle-fill::before{content:"\f2e5"}.bi-dash-circle::before{content:"\f2e6"}.bi-dash-square-dotted::before{content:"\f2e7"}.bi-dash-square-fill::before{content:"\f2e8"}.bi-dash-square::before{content:"\f2e9"}.bi-dash::before{content:"\f2ea"}.bi-diagram-2-fill::before{content:"\f2eb"}.bi-diagram-2::before{content:"\f2ec"}.bi-diagram-3-fill::before{content:"\f2ed"}.bi-diagram-3::before{content:"\f2ee"}.bi-diamond-fill::before{content:"\f2ef"}.bi-diamond-half::before{content:"\f2f0"}.bi-diamond::before{content:"\f2f1"}.bi-dice-1-fill::before{content:"\f2f2"}.bi-dice-1::before{content:"\f2f3"}.bi-dice-2-fill::before{content:"\f2f4"}.bi-dice-2::before{content:"\f2f5"}.bi-dice-3-fill::before{content:"\f2f6"}.bi-dice-3::before{content:"\f2f7"}.bi-dice-4-fill::before{content:"\f2f8"}.bi-dice-4::before{content:"\f2f9"}.bi-dice-5-fill::before{content:"\f2fa"}.bi-dice-5::before{content:"\f2fb"}.bi-dice-6-fill::before{content:"\f2fc"}.bi-dice-6::before{content:"\f2fd"}.bi-disc-fill::before{content:"\f2fe"}.bi-disc::before{content:"\f2ff"}.bi-discord::before{content:"\f300"}.bi-display-fill::before{content:"\f301"}.bi-display::before{content:"\f302"}.bi-distribute-horizontal::before{content:"\f303"}.bi-distribute-vertical::before{content:"\f304"}.bi-door-closed-fill::before{content:"\f305"}.bi-door-closed::before{content:"\f306"}.bi-door-open-fill::before{content:"\f307"}.bi-door-open::before{content:"\f308"}.bi-dot::before{content:"\f309"}.bi-download::before{content:"\f30a"}.bi-droplet-fill::before{content:"\f30b"}.bi-droplet-half::before{content:"\f30c"}.bi-droplet::before{content:"\f30d"}.bi-earbuds::before{content:"\f30e"}.bi-easel-fill::before{content:"\f30f"}.bi-easel::before{content:"\f310"}.bi-egg-fill::before{content:"\f311"}.bi-egg-fried::before{content:"\f312"}.bi-egg::before{content:"\f313"}.bi-eject-fill::before{content:"\f314"}.bi-eject::before{content:"\f315"}.bi-emoji-angry-fill::before{content:"\f316"}.bi-emoji-angry::before{content:"\f317"}.bi-emoji-dizzy-fill::before{content:"\f318"}.bi-emoji-dizzy::before{content:"\f319"}.bi-emoji-expressionless-fill::before{content:"\f31a"}.bi-emoji-expressionless::before{content:"\f31b"}.bi-emoji-frown-fill::before{content:"\f31c"}.bi-emoji-frown::before{content:"\f31d"}.bi-emoji-heart-eyes-fill::before{content:"\f31e"}.bi-emoji-heart-eyes::before{content:"\f31f"}.bi-emoji-laughing-fill::before{content:"\f320"}.bi-emoji-laughing::before{content:"\f321"}.bi-emoji-neutral-fill::before{content:"\f322"}.bi-emoji-neutral::before{content:"\f323"}.bi-emoji-smile-fill::before{content:"\f324"}.bi-emoji-smile-upside-down-fill::before{content:"\f325"}.bi-emoji-smile-upside-down::before{content:"\f326"}.bi-emoji-smile::before{content:"\f327"}.bi-emoji-sunglasses-fill::before{content:"\f328"}.bi-emoji-sunglasses::before{content:"\f329"}.bi-emoji-wink-fill::before{content:"\f32a"}.bi-emoji-wink::before{content:"\f32b"}.bi-envelope-fill::before{content:"\f32c"}.bi-envelope-open-fill::before{content:"\f32d"}.bi-envelope-open::before{content:"\f32e"}.bi-envelope::before{content:"\f32f"}.bi-eraser-fill::before{content:"\f330"}.bi-eraser::before{content:"\f331"}.bi-exclamation-circle-fill::before{content:"\f332"}.bi-exclamation-circle::before{content:"\f333"}.bi-exclamation-diamond-fill::before{content:"\f334"}.bi-exclamation-diamond::before{content:"\f335"}.bi-exclamation-octagon-fill::before{content:"\f336"}.bi-exclamation-octagon::before{content:"\f337"}.bi-exclamation-square-fill::before{content:"\f338"}.bi-exclamation-square::before{content:"\f339"}.bi-exclamation-triangle-fill::before{content:"\f33a"}.bi-exclamation-triangle::before{content:"\f33b"}.bi-exclamation::before{content:"\f33c"}.bi-exclude::before{content:"\f33d"}.bi-eye-fill::before{content:"\f33e"}.bi-eye-slash-fill::before{content:"\f33f"}.bi-eye-slash::before{content:"\f340"}.bi-eye::before{content:"\f341"}.bi-eyedropper::before{content:"\f342"}.bi-eyeglasses::before{content:"\f343"}.bi-facebook::before{content:"\f344"}.bi-file-arrow-down-fill::before{content:"\f345"}.bi-file-arrow-down::before{content:"\f346"}.bi-file-arrow-up-fill::before{content:"\f347"}.bi-file-arrow-up::before{content:"\f348"}.bi-file-bar-graph-fill::before{content:"\f349"}.bi-file-bar-graph::before{content:"\f34a"}.bi-file-binary-fill::before{content:"\f34b"}.bi-file-binary::before{content:"\f34c"}.bi-file-break-fill::before{content:"\f34d"}.bi-file-break::before{content:"\f34e"}.bi-file-check-fill::before{content:"\f34f"}.bi-file-check::before{content:"\f350"}.bi-file-code-fill::before{content:"\f351"}.bi-file-code::before{content:"\f352"}.bi-file-diff-fill::before{content:"\f353"}.bi-file-diff::before{content:"\f354"}.bi-file-earmark-arrow-down-fill::before{content:"\f355"}.bi-file-earmark-arrow-down::before{content:"\f356"}.bi-file-earmark-arrow-up-fill::before{content:"\f357"}.bi-file-earmark-arrow-up::before{content:"\f358"}.bi-file-earmark-bar-graph-fill::before{content:"\f359"}.bi-file-earmark-bar-graph::before{content:"\f35a"}.bi-file-earmark-binary-fill::before{content:"\f35b"}.bi-file-earmark-binary::before{content:"\f35c"}.bi-file-earmark-break-fill::before{content:"\f35d"}.bi-file-earmark-break::before{content:"\f35e"}.bi-file-earmark-check-fill::before{content:"\f35f"}.bi-file-earmark-check::before{content:"\f360"}.bi-file-earmark-code-fill::before{content:"\f361"}.bi-file-earmark-code::before{content:"\f362"}.bi-file-earmark-diff-fill::before{content:"\f363"}.bi-file-earmark-diff::before{content:"\f364"}.bi-file-earmark-easel-fill::before{content:"\f365"}.bi-file-earmark-easel::before{content:"\f366"}.bi-file-earmark-excel-fill::before{content:"\f367"}.bi-file-earmark-excel::before{content:"\f368"}.bi-file-earmark-fill::before{content:"\f369"}.bi-file-earmark-font-fill::before{content:"\f36a"}.bi-file-earmark-font::before{content:"\f36b"}.bi-file-earmark-image-fill::before{content:"\f36c"}.bi-file-earmark-image::before{content:"\f36d"}.bi-file-earmark-lock-fill::before{content:"\f36e"}.bi-file-earmark-lock::before{content:"\f36f"}.bi-file-earmark-lock2-fill::before{content:"\f370"}.bi-file-earmark-lock2::before{content:"\f371"}.bi-file-earmark-medical-fill::before{content:"\f372"}.bi-file-earmark-medical::before{content:"\f373"}.bi-file-earmark-minus-fill::before{content:"\f374"}.bi-file-earmark-minus::before{content:"\f375"}.bi-file-earmark-music-fill::before{content:"\f376"}.bi-file-earmark-music::before{content:"\f377"}.bi-file-earmark-person-fill::before{content:"\f378"}.bi-file-earmark-person::before{content:"\f379"}.bi-file-earmark-play-fill::before{content:"\f37a"}.bi-file-earmark-play::before{content:"\f37b"}.bi-file-earmark-plus-fill::before{content:"\f37c"}.bi-file-earmark-plus::before{content:"\f37d"}.bi-file-earmark-post-fill::before{content:"\f37e"}.bi-file-earmark-post::before{content:"\f37f"}.bi-file-earmark-ppt-fill::before{content:"\f380"}.bi-file-earmark-ppt::before{content:"\f381"}.bi-file-earmark-richtext-fill::before{content:"\f382"}.bi-file-earmark-richtext::before{content:"\f383"}.bi-file-earmark-ruled-fill::before{content:"\f384"}.bi-file-earmark-ruled::before{content:"\f385"}.bi-file-earmark-slides-fill::before{content:"\f386"}.bi-file-earmark-slides::before{content:"\f387"}.bi-file-earmark-spreadsheet-fill::before{content:"\f388"}.bi-file-earmark-spreadsheet::before{content:"\f389"}.bi-file-earmark-text-fill::before{content:"\f38a"}.bi-file-earmark-text::before{content:"\f38b"}.bi-file-earmark-word-fill::before{content:"\f38c"}.bi-file-earmark-word::before{content:"\f38d"}.bi-file-earmark-x-fill::before{content:"\f38e"}.bi-file-earmark-x::before{content:"\f38f"}.bi-file-earmark-zip-fill::before{content:"\f390"}.bi-file-earmark-zip::before{content:"\f391"}.bi-file-earmark::before{content:"\f392"}.bi-file-easel-fill::before{content:"\f393"}.bi-file-easel::before{content:"\f394"}.bi-file-excel-fill::before{content:"\f395"}.bi-file-excel::before{content:"\f396"}.bi-file-fill::before{content:"\f397"}.bi-file-font-fill::before{content:"\f398"}.bi-file-font::before{content:"\f399"}.bi-file-image-fill::before{content:"\f39a"}.bi-file-image::before{content:"\f39b"}.bi-file-lock-fill::before{content:"\f39c"}.bi-file-lock::before{content:"\f39d"}.bi-file-lock2-fill::before{content:"\f39e"}.bi-file-lock2::before{content:"\f39f"}.bi-file-medical-fill::before{content:"\f3a0"}.bi-file-medical::before{content:"\f3a1"}.bi-file-minus-fill::before{content:"\f3a2"}.bi-file-minus::before{content:"\f3a3"}.bi-file-music-fill::before{content:"\f3a4"}.bi-file-music::before{content:"\f3a5"}.bi-file-person-fill::before{content:"\f3a6"}.bi-file-person::before{content:"\f3a7"}.bi-file-play-fill::before{content:"\f3a8"}.bi-file-play::before{content:"\f3a9"}.bi-file-plus-fill::before{content:"\f3aa"}.bi-file-plus::before{content:"\f3ab"}.bi-file-post-fill::before{content:"\f3ac"}.bi-file-post::before{content:"\f3ad"}.bi-file-ppt-fill::before{content:"\f3ae"}.bi-file-ppt::before{content:"\f3af"}.bi-file-richtext-fill::before{content:"\f3b0"}.bi-file-richtext::before{content:"\f3b1"}.bi-file-ruled-fill::before{content:"\f3b2"}.bi-file-ruled::before{content:"\f3b3"}.bi-file-slides-fill::before{content:"\f3b4"}.bi-file-slides::before{content:"\f3b5"}.bi-file-spreadsheet-fill::before{content:"\f3b6"}.bi-file-spreadsheet::before{content:"\f3b7"}.bi-file-text-fill::before{content:"\f3b8"}.bi-file-text::before{content:"\f3b9"}.bi-file-word-fill::before{content:"\f3ba"}.bi-file-word::before{content:"\f3bb"}.bi-file-x-fill::before{content:"\f3bc"}.bi-file-x::before{content:"\f3bd"}.bi-file-zip-fill::before{content:"\f3be"}.bi-file-zip::before{content:"\f3bf"}.bi-file::before{content:"\f3c0"}.bi-files-alt::before{content:"\f3c1"}.bi-files::before{content:"\f3c2"}.bi-film::before{content:"\f3c3"}.bi-filter-circle-fill::before{content:"\f3c4"}.bi-filter-circle::before{content:"\f3c5"}.bi-filter-left::before{content:"\f3c6"}.bi-filter-right::before{content:"\f3c7"}.bi-filter-square-fill::before{content:"\f3c8"}.bi-filter-square::before{content:"\f3c9"}.bi-filter::before{content:"\f3ca"}.bi-flag-fill::before{content:"\f3cb"}.bi-flag::before{content:"\f3cc"}.bi-flower1::before{content:"\f3cd"}.bi-flower2::before{content:"\f3ce"}.bi-flower3::before{content:"\f3cf"}.bi-folder-check::before{content:"\f3d0"}.bi-folder-fill::before{content:"\f3d1"}.bi-folder-minus::before{content:"\f3d2"}.bi-folder-plus::before{content:"\f3d3"}.bi-folder-symlink-fill::before{content:"\f3d4"}.bi-folder-symlink::before{content:"\f3d5"}.bi-folder-x::before{content:"\f3d6"}.bi-folder::before{content:"\f3d7"}.bi-folder2-open::before{content:"\f3d8"}.bi-folder2::before{content:"\f3d9"}.bi-fonts::before{content:"\f3da"}.bi-forward-fill::before{content:"\f3db"}.bi-forward::before{content:"\f3dc"}.bi-front::before{content:"\f3dd"}.bi-fullscreen-exit::before{content:"\f3de"}.bi-fullscreen::before{content:"\f3df"}.bi-funnel-fill::before{content:"\f3e0"}.bi-funnel::before{content:"\f3e1"}.bi-gear-fill::before{content:"\f3e2"}.bi-gear-wide-connected::before{content:"\f3e3"}.bi-gear-wide::before{content:"\f3e4"}.bi-gear::before{content:"\f3e5"}.bi-gem::before{content:"\f3e6"}.bi-geo-alt-fill::before{content:"\f3e7"}.bi-geo-alt::before{content:"\f3e8"}.bi-geo-fill::before{content:"\f3e9"}.bi-geo::before{content:"\f3ea"}.bi-gift-fill::before{content:"\f3eb"}.bi-gift::before{content:"\f3ec"}.bi-github::before{content:"\f3ed"}.bi-globe::before{content:"\f3ee"}.bi-globe2::before{content:"\f3ef"}.bi-google::before{content:"\f3f0"}.bi-graph-down::before{content:"\f3f1"}.bi-graph-up::before{content:"\f3f2"}.bi-grid-1x2-fill::before{content:"\f3f3"}.bi-grid-1x2::before{content:"\f3f4"}.bi-grid-3x2-gap-fill::before{content:"\f3f5"}.bi-grid-3x2-gap::before{content:"\f3f6"}.bi-grid-3x2::before{content:"\f3f7"}.bi-grid-3x3-gap-fill::before{content:"\f3f8"}.bi-grid-3x3-gap::before{content:"\f3f9"}.bi-grid-3x3::before{content:"\f3fa"}.bi-grid-fill::before{content:"\f3fb"}.bi-grid::before{content:"\f3fc"}.bi-grip-horizontal::before{content:"\f3fd"}.bi-grip-vertical::before{content:"\f3fe"}.bi-hammer::before{content:"\f3ff"}.bi-hand-index-fill::before{content:"\f400"}.bi-hand-index-thumb-fill::before{content:"\f401"}.bi-hand-index-thumb::before{content:"\f402"}.bi-hand-index::before{content:"\f403"}.bi-hand-thumbs-down-fill::before{content:"\f404"}.bi-hand-thumbs-down::before{content:"\f405"}.bi-hand-thumbs-up-fill::before{content:"\f406"}.bi-hand-thumbs-up::before{content:"\f407"}.bi-handbag-fill::before{content:"\f408"}.bi-handbag::before{content:"\f409"}.bi-hash::before{content:"\f40a"}.bi-hdd-fill::before{content:"\f40b"}.bi-hdd-network-fill::before{content:"\f40c"}.bi-hdd-network::before{content:"\f40d"}.bi-hdd-rack-fill::before{content:"\f40e"}.bi-hdd-rack::before{content:"\f40f"}.bi-hdd-stack-fill::before{content:"\f410"}.bi-hdd-stack::before{content:"\f411"}.bi-hdd::before{content:"\f412"}.bi-headphones::before{content:"\f413"}.bi-headset::before{content:"\f414"}.bi-heart-fill::before{content:"\f415"}.bi-heart-half::before{content:"\f416"}.bi-heart::before{content:"\f417"}.bi-heptagon-fill::before{content:"\f418"}.bi-heptagon-half::before{content:"\f419"}.bi-heptagon::before{content:"\f41a"}.bi-hexagon-fill::before{content:"\f41b"}.bi-hexagon-half::before{content:"\f41c"}.bi-hexagon::before{content:"\f41d"}.bi-hourglass-bottom::before{content:"\f41e"}.bi-hourglass-split::before{content:"\f41f"}.bi-hourglass-top::before{content:"\f420"}.bi-hourglass::before{content:"\f421"}.bi-house-door-fill::before{content:"\f422"}.bi-house-door::before{content:"\f423"}.bi-house-fill::before{content:"\f424"}.bi-house::before{content:"\f425"}.bi-hr::before{content:"\f426"}.bi-hurricane::before{content:"\f427"}.bi-image-alt::before{content:"\f428"}.bi-image-fill::before{content:"\f429"}.bi-image::before{content:"\f42a"}.bi-images::before{content:"\f42b"}.bi-inbox-fill::before{content:"\f42c"}.bi-inbox::before{content:"\f42d"}.bi-inboxes-fill::before{content:"\f42e"}.bi-inboxes::before{content:"\f42f"}.bi-info-circle-fill::before{content:"\f430"}.bi-info-circle::before{content:"\f431"}.bi-info-square-fill::before{content:"\f432"}.bi-info-square::before{content:"\f433"}.bi-info::before{content:"\f434"}.bi-input-cursor-text::before{content:"\f435"}.bi-input-cursor::before{content:"\f436"}.bi-instagram::before{content:"\f437"}.bi-intersect::before{content:"\f438"}.bi-journal-album::before{content:"\f439"}.bi-journal-arrow-down::before{content:"\f43a"}.bi-journal-arrow-up::before{content:"\f43b"}.bi-journal-bookmark-fill::before{content:"\f43c"}.bi-journal-bookmark::before{content:"\f43d"}.bi-journal-check::before{content:"\f43e"}.bi-journal-code::before{content:"\f43f"}.bi-journal-medical::before{content:"\f440"}.bi-journal-minus::before{content:"\f441"}.bi-journal-plus::before{content:"\f442"}.bi-journal-richtext::before{content:"\f443"}.bi-journal-text::before{content:"\f444"}.bi-journal-x::before{content:"\f445"}.bi-journal::before{content:"\f446"}.bi-journals::before{content:"\f447"}.bi-joystick::before{content:"\f448"}.bi-justify-left::before{content:"\f449"}.bi-justify-right::before{content:"\f44a"}.bi-justify::before{content:"\f44b"}.bi-kanban-fill::before{content:"\f44c"}.bi-kanban::before{content:"\f44d"}.bi-key-fill::before{content:"\f44e"}.bi-key::before{content:"\f44f"}.bi-keyboard-fill::before{content:"\f450"}.bi-keyboard::before{content:"\f451"}.bi-ladder::before{content:"\f452"}.bi-lamp-fill::before{content:"\f453"}.bi-lamp::before{content:"\f454"}.bi-laptop-fill::before{content:"\f455"}.bi-laptop::before{content:"\f456"}.bi-layer-backward::before{content:"\f457"}.bi-layer-forward::before{content:"\f458"}.bi-layers-fill::before{content:"\f459"}.bi-layers-half::before{content:"\f45a"}.bi-layers::before{content:"\f45b"}.bi-layout-sidebar-inset-reverse::before{content:"\f45c"}.bi-layout-sidebar-inset::before{content:"\f45d"}.bi-layout-sidebar-reverse::before{content:"\f45e"}.bi-layout-sidebar::before{content:"\f45f"}.bi-layout-split::before{content:"\f460"}.bi-layout-text-sidebar-reverse::before{content:"\f461"}.bi-layout-text-sidebar::before{content:"\f462"}.bi-layout-text-window-reverse::before{content:"\f463"}.bi-layout-text-window::before{content:"\f464"}.bi-layout-three-columns::before{content:"\f465"}.bi-layout-wtf::before{content:"\f466"}.bi-life-preserver::before{content:"\f467"}.bi-lightbulb-fill::before{content:"\f468"}.bi-lightbulb-off-fill::before{content:"\f469"}.bi-lightbulb-off::before{content:"\f46a"}.bi-lightbulb::before{content:"\f46b"}.bi-lightning-charge-fill::before{content:"\f46c"}.bi-lightning-charge::before{content:"\f46d"}.bi-lightning-fill::before{content:"\f46e"}.bi-lightning::before{content:"\f46f"}.bi-link-45deg::before{content:"\f470"}.bi-link::before{content:"\f471"}.bi-linkedin::before{content:"\f472"}.bi-list-check::before{content:"\f473"}.bi-list-nested::before{content:"\f474"}.bi-list-ol::before{content:"\f475"}.bi-list-stars::before{content:"\f476"}.bi-list-task::before{content:"\f477"}.bi-list-ul::before{content:"\f478"}.bi-list::before{content:"\f479"}.bi-lock-fill::before{content:"\f47a"}.bi-lock::before{content:"\f47b"}.bi-mailbox::before{content:"\f47c"}.bi-mailbox2::before{content:"\f47d"}.bi-map-fill::before{content:"\f47e"}.bi-map::before{content:"\f47f"}.bi-markdown-fill::before{content:"\f480"}.bi-markdown::before{content:"\f481"}.bi-mask::before{content:"\f482"}.bi-megaphone-fill::before{content:"\f483"}.bi-megaphone::before{content:"\f484"}.bi-menu-app-fill::before{content:"\f485"}.bi-menu-app::before{content:"\f486"}.bi-menu-button-fill::before{content:"\f487"}.bi-menu-button-wide-fill::before{content:"\f488"}.bi-menu-button-wide::before{content:"\f489"}.bi-menu-button::before{content:"\f48a"}.bi-menu-down::before{content:"\f48b"}.bi-menu-up::before{content:"\f48c"}.bi-mic-fill::before{content:"\f48d"}.bi-mic-mute-fill::before{content:"\f48e"}.bi-mic-mute::before{content:"\f48f"}.bi-mic::before{content:"\f490"}.bi-minecart-loaded::before{content:"\f491"}.bi-minecart::before{content:"\f492"}.bi-moisture::before{content:"\f493"}.bi-moon-fill::before{content:"\f494"}.bi-moon-stars-fill::before{content:"\f495"}.bi-moon-stars::before{content:"\f496"}.bi-moon::before{content:"\f497"}.bi-mouse-fill::before{content:"\f498"}.bi-mouse::before{content:"\f499"}.bi-mouse2-fill::before{content:"\f49a"}.bi-mouse2::before{content:"\f49b"}.bi-mouse3-fill::before{content:"\f49c"}.bi-mouse3::before{content:"\f49d"}.bi-music-note-beamed::before{content:"\f49e"}.bi-music-note-list::before{content:"\f49f"}.bi-music-note::before{content:"\f4a0"}.bi-music-player-fill::before{content:"\f4a1"}.bi-music-player::before{content:"\f4a2"}.bi-newspaper::before{content:"\f4a3"}.bi-node-minus-fill::before{content:"\f4a4"}.bi-node-minus::before{content:"\f4a5"}.bi-node-plus-fill::before{content:"\f4a6"}.bi-node-plus::before{content:"\f4a7"}.bi-nut-fill::before{content:"\f4a8"}.bi-nut::before{content:"\f4a9"}.bi-octagon-fill::before{content:"\f4aa"}.bi-octagon-half::before{content:"\f4ab"}.bi-octagon::before{content:"\f4ac"}.bi-option::before{content:"\f4ad"}.bi-outlet::before{content:"\f4ae"}.bi-paint-bucket::before{content:"\f4af"}.bi-palette-fill::before{content:"\f4b0"}.bi-palette::before{content:"\f4b1"}.bi-palette2::before{content:"\f4b2"}.bi-paperclip::before{content:"\f4b3"}.bi-paragraph::before{content:"\f4b4"}.bi-patch-check-fill::before{content:"\f4b5"}.bi-patch-check::before{content:"\f4b6"}.bi-patch-exclamation-fill::before{content:"\f4b7"}.bi-patch-exclamation::before{content:"\f4b8"}.bi-patch-minus-fill::before{content:"\f4b9"}.bi-patch-minus::before{content:"\f4ba"}.bi-patch-plus-fill::before{content:"\f4bb"}.bi-patch-plus::before{content:"\f4bc"}.bi-patch-question-fill::before{content:"\f4bd"}.bi-patch-question::before{content:"\f4be"}.bi-pause-btn-fill::before{content:"\f4bf"}.bi-pause-btn::before{content:"\f4c0"}.bi-pause-circle-fill::before{content:"\f4c1"}.bi-pause-circle::before{content:"\f4c2"}.bi-pause-fill::before{content:"\f4c3"}.bi-pause::before{content:"\f4c4"}.bi-peace-fill::before{content:"\f4c5"}.bi-peace::before{content:"\f4c6"}.bi-pen-fill::before{content:"\f4c7"}.bi-pen::before{content:"\f4c8"}.bi-pencil-fill::before{content:"\f4c9"}.bi-pencil-square::before{content:"\f4ca"}.bi-pencil::before{content:"\f4cb"}.bi-pentagon-fill::before{content:"\f4cc"}.bi-pentagon-half::before{content:"\f4cd"}.bi-pentagon::before{content:"\f4ce"}.bi-people-fill::before{content:"\f4cf"}.bi-people::before{content:"\f4d0"}.bi-percent::before{content:"\f4d1"}.bi-person-badge-fill::before{content:"\f4d2"}.bi-person-badge::before{content:"\f4d3"}.bi-person-bounding-box::before{content:"\f4d4"}.bi-person-check-fill::before{content:"\f4d5"}.bi-person-check::before{content:"\f4d6"}.bi-person-circle::before{content:"\f4d7"}.bi-person-dash-fill::before{content:"\f4d8"}.bi-person-dash::before{content:"\f4d9"}.bi-person-fill::before{content:"\f4da"}.bi-person-lines-fill::before{content:"\f4db"}.bi-person-plus-fill::before{content:"\f4dc"}.bi-person-plus::before{content:"\f4dd"}.bi-person-square::before{content:"\f4de"}.bi-person-x-fill::before{content:"\f4df"}.bi-person-x::before{content:"\f4e0"}.bi-person::before{content:"\f4e1"}.bi-phone-fill::before{content:"\f4e2"}.bi-phone-landscape-fill::before{content:"\f4e3"}.bi-phone-landscape::before{content:"\f4e4"}.bi-phone-vibrate-fill::before{content:"\f4e5"}.bi-phone-vibrate::before{content:"\f4e6"}.bi-phone::before{content:"\f4e7"}.bi-pie-chart-fill::before{content:"\f4e8"}.bi-pie-chart::before{content:"\f4e9"}.bi-pin-angle-fill::before{content:"\f4ea"}.bi-pin-angle::before{content:"\f4eb"}.bi-pin-fill::before{content:"\f4ec"}.bi-pin::before{content:"\f4ed"}.bi-pip-fill::before{content:"\f4ee"}.bi-pip::before{content:"\f4ef"}.bi-play-btn-fill::before{content:"\f4f0"}.bi-play-btn::before{content:"\f4f1"}.bi-play-circle-fill::before{content:"\f4f2"}.bi-play-circle::before{content:"\f4f3"}.bi-play-fill::before{content:"\f4f4"}.bi-play::before{content:"\f4f5"}.bi-plug-fill::before{content:"\f4f6"}.bi-plug::before{content:"\f4f7"}.bi-plus-circle-dotted::before{content:"\f4f8"}.bi-plus-circle-fill::before{content:"\f4f9"}.bi-plus-circle::before{content:"\f4fa"}.bi-plus-square-dotted::before{content:"\f4fb"}.bi-plus-square-fill::before{content:"\f4fc"}.bi-plus-square::before{content:"\f4fd"}.bi-plus::before{content:"\f4fe"}.bi-power::before{content:"\f4ff"}.bi-printer-fill::before{content:"\f500"}.bi-printer::before{content:"\f501"}.bi-puzzle-fill::before{content:"\f502"}.bi-puzzle::before{content:"\f503"}.bi-question-circle-fill::before{content:"\f504"}.bi-question-circle::before{content:"\f505"}.bi-question-diamond-fill::before{content:"\f506"}.bi-question-diamond::before{content:"\f507"}.bi-question-octagon-fill::before{content:"\f508"}.bi-question-octagon::before{content:"\f509"}.bi-question-square-fill::before{content:"\f50a"}.bi-question-square::before{content:"\f50b"}.bi-question::before{content:"\f50c"}.bi-rainbow::before{content:"\f50d"}.bi-receipt-cutoff::before{content:"\f50e"}.bi-receipt::before{content:"\f50f"}.bi-reception-0::before{content:"\f510"}.bi-reception-1::before{content:"\f511"}.bi-reception-2::before{content:"\f512"}.bi-reception-3::before{content:"\f513"}.bi-reception-4::before{content:"\f514"}.bi-record-btn-fill::before{content:"\f515"}.bi-record-btn::before{content:"\f516"}.bi-record-circle-fill::before{content:"\f517"}.bi-record-circle::before{content:"\f518"}.bi-record-fill::before{content:"\f519"}.bi-record::before{content:"\f51a"}.bi-record2-fill::before{content:"\f51b"}.bi-record2::before{content:"\f51c"}.bi-reply-all-fill::before{content:"\f51d"}.bi-reply-all::before{content:"\f51e"}.bi-reply-fill::before{content:"\f51f"}.bi-reply::before{content:"\f520"}.bi-rss-fill::before{content:"\f521"}.bi-rss::before{content:"\f522"}.bi-rulers::before{content:"\f523"}.bi-save-fill::before{content:"\f524"}.bi-save::before{content:"\f525"}.bi-save2-fill::before{content:"\f526"}.bi-save2::before{content:"\f527"}.bi-scissors::before{content:"\f528"}.bi-screwdriver::before{content:"\f529"}.bi-search::before{content:"\f52a"}.bi-segmented-nav::before{content:"\f52b"}.bi-server::before{content:"\f52c"}.bi-share-fill::before{content:"\f52d"}.bi-share::before{content:"\f52e"}.bi-shield-check::before{content:"\f52f"}.bi-shield-exclamation::before{content:"\f530"}.bi-shield-fill-check::before{content:"\f531"}.bi-shield-fill-exclamation::before{content:"\f532"}.bi-shield-fill-minus::before{content:"\f533"}.bi-shield-fill-plus::before{content:"\f534"}.bi-shield-fill-x::before{content:"\f535"}.bi-shield-fill::before{content:"\f536"}.bi-shield-lock-fill::before{content:"\f537"}.bi-shield-lock::before{content:"\f538"}.bi-shield-minus::before{content:"\f539"}.bi-shield-plus::before{content:"\f53a"}.bi-shield-shaded::before{content:"\f53b"}.bi-shield-slash-fill::before{content:"\f53c"}.bi-shield-slash::before{content:"\f53d"}.bi-shield-x::before{content:"\f53e"}.bi-shield::before{content:"\f53f"}.bi-shift-fill::before{content:"\f540"}.bi-shift::before{content:"\f541"}.bi-shop-window::before{content:"\f542"}.bi-shop::before{content:"\f543"}.bi-shuffle::before{content:"\f544"}.bi-signpost-2-fill::before{content:"\f545"}.bi-signpost-2::before{content:"\f546"}.bi-signpost-fill::before{content:"\f547"}.bi-signpost-split-fill::before{content:"\f548"}.bi-signpost-split::before{content:"\f549"}.bi-signpost::before{content:"\f54a"}.bi-sim-fill::before{content:"\f54b"}.bi-sim::before{content:"\f54c"}.bi-skip-backward-btn-fill::before{content:"\f54d"}.bi-skip-backward-btn::before{content:"\f54e"}.bi-skip-backward-circle-fill::before{content:"\f54f"}.bi-skip-backward-circle::before{content:"\f550"}.bi-skip-backward-fill::before{content:"\f551"}.bi-skip-backward::before{content:"\f552"}.bi-skip-end-btn-fill::before{content:"\f553"}.bi-skip-end-btn::before{content:"\f554"}.bi-skip-end-circle-fill::before{content:"\f555"}.bi-skip-end-circle::before{content:"\f556"}.bi-skip-end-fill::before{content:"\f557"}.bi-skip-end::before{content:"\f558"}.bi-skip-forward-btn-fill::before{content:"\f559"}.bi-skip-forward-btn::before{content:"\f55a"}.bi-skip-forward-circle-fill::before{content:"\f55b"}.bi-skip-forward-circle::before{content:"\f55c"}.bi-skip-forward-fill::before{content:"\f55d"}.bi-skip-forward::before{content:"\f55e"}.bi-skip-start-btn-fill::before{content:"\f55f"}.bi-skip-start-btn::before{content:"\f560"}.bi-skip-start-circle-fill::before{content:"\f561"}.bi-skip-start-circle::before{content:"\f562"}.bi-skip-start-fill::before{content:"\f563"}.bi-skip-start::before{content:"\f564"}.bi-slack::before{content:"\f565"}.bi-slash-circle-fill::before{content:"\f566"}.bi-slash-circle::before{content:"\f567"}.bi-slash-square-fill::before{content:"\f568"}.bi-slash-square::before{content:"\f569"}.bi-slash::before{content:"\f56a"}.bi-sliders::before{content:"\f56b"}.bi-smartwatch::before{content:"\f56c"}.bi-snow::before{content:"\f56d"}.bi-snow2::before{content:"\f56e"}.bi-snow3::before{content:"\f56f"}.bi-sort-alpha-down-alt::before{content:"\f570"}.bi-sort-alpha-down::before{content:"\f571"}.bi-sort-alpha-up-alt::before{content:"\f572"}.bi-sort-alpha-up::before{content:"\f573"}.bi-sort-down-alt::before{content:"\f574"}.bi-sort-down::before{content:"\f575"}.bi-sort-numeric-down-alt::before{content:"\f576"}.bi-sort-numeric-down::before{content:"\f577"}.bi-sort-numeric-up-alt::before{content:"\f578"}.bi-sort-numeric-up::before{content:"\f579"}.bi-sort-up-alt::before{content:"\f57a"}.bi-sort-up::before{content:"\f57b"}.bi-soundwave::before{content:"\f57c"}.bi-speaker-fill::before{content:"\f57d"}.bi-speaker::before{content:"\f57e"}.bi-speedometer::before{content:"\f57f"}.bi-speedometer2::before{content:"\f580"}.bi-spellcheck::before{content:"\f581"}.bi-square-fill::before{content:"\f582"}.bi-square-half::before{content:"\f583"}.bi-square::before{content:"\f584"}.bi-stack::before{content:"\f585"}.bi-star-fill::before{content:"\f586"}.bi-star-half::before{content:"\f587"}.bi-star::before{content:"\f588"}.bi-stars::before{content:"\f589"}.bi-stickies-fill::before{content:"\f58a"}.bi-stickies::before{content:"\f58b"}.bi-sticky-fill::before{content:"\f58c"}.bi-sticky::before{content:"\f58d"}.bi-stop-btn-fill::before{content:"\f58e"}.bi-stop-btn::before{content:"\f58f"}.bi-stop-circle-fill::before{content:"\f590"}.bi-stop-circle::before{content:"\f591"}.bi-stop-fill::before{content:"\f592"}.bi-stop::before{content:"\f593"}.bi-stoplights-fill::before{content:"\f594"}.bi-stoplights::before{content:"\f595"}.bi-stopwatch-fill::before{content:"\f596"}.bi-stopwatch::before{content:"\f597"}.bi-subtract::before{content:"\f598"}.bi-suit-club-fill::before{content:"\f599"}.bi-suit-club::before{content:"\f59a"}.bi-suit-diamond-fill::before{content:"\f59b"}.bi-suit-diamond::before{content:"\f59c"}.bi-suit-heart-fill::before{content:"\f59d"}.bi-suit-heart::before{content:"\f59e"}.bi-suit-spade-fill::before{content:"\f59f"}.bi-suit-spade::before{content:"\f5a0"}.bi-sun-fill::before{content:"\f5a1"}.bi-sun::before{content:"\f5a2"}.bi-sunglasses::before{content:"\f5a3"}.bi-sunrise-fill::before{content:"\f5a4"}.bi-sunrise::before{content:"\f5a5"}.bi-sunset-fill::before{content:"\f5a6"}.bi-sunset::before{content:"\f5a7"}.bi-symmetry-horizontal::before{content:"\f5a8"}.bi-symmetry-vertical::before{content:"\f5a9"}.bi-table::before{content:"\f5aa"}.bi-tablet-fill::before{content:"\f5ab"}.bi-tablet-landscape-fill::before{content:"\f5ac"}.bi-tablet-landscape::before{content:"\f5ad"}.bi-tablet::before{content:"\f5ae"}.bi-tag-fill::before{content:"\f5af"}.bi-tag::before{content:"\f5b0"}.bi-tags-fill::before{content:"\f5b1"}.bi-tags::before{content:"\f5b2"}.bi-telegram::before{content:"\f5b3"}.bi-telephone-fill::before{content:"\f5b4"}.bi-telephone-forward-fill::before{content:"\f5b5"}.bi-telephone-forward::before{content:"\f5b6"}.bi-telephone-inbound-fill::before{content:"\f5b7"}.bi-telephone-inbound::before{content:"\f5b8"}.bi-telephone-minus-fill::before{content:"\f5b9"}.bi-telephone-minus::before{content:"\f5ba"}.bi-telephone-outbound-fill::before{content:"\f5bb"}.bi-telephone-outbound::before{content:"\f5bc"}.bi-telephone-plus-fill::before{content:"\f5bd"}.bi-telephone-plus::before{content:"\f5be"}.bi-telephone-x-fill::before{content:"\f5bf"}.bi-telephone-x::before{content:"\f5c0"}.bi-telephone::before{content:"\f5c1"}.bi-terminal-fill::before{content:"\f5c2"}.bi-terminal::before{content:"\f5c3"}.bi-text-center::before{content:"\f5c4"}.bi-text-indent-left::before{content:"\f5c5"}.bi-text-indent-right::before{content:"\f5c6"}.bi-text-left::before{content:"\f5c7"}.bi-text-paragraph::before{content:"\f5c8"}.bi-text-right::before{content:"\f5c9"}.bi-textarea-resize::before{content:"\f5ca"}.bi-textarea-t::before{content:"\f5cb"}.bi-textarea::before{content:"\f5cc"}.bi-thermometer-half::before{content:"\f5cd"}.bi-thermometer-high::before{content:"\f5ce"}.bi-thermometer-low::before{content:"\f5cf"}.bi-thermometer-snow::before{content:"\f5d0"}.bi-thermometer-sun::before{content:"\f5d1"}.bi-thermometer::before{content:"\f5d2"}.bi-three-dots-vertical::before{content:"\f5d3"}.bi-three-dots::before{content:"\f5d4"}.bi-toggle-off::before{content:"\f5d5"}.bi-toggle-on::before{content:"\f5d6"}.bi-toggle2-off::before{content:"\f5d7"}.bi-toggle2-on::before{content:"\f5d8"}.bi-toggles::before{content:"\f5d9"}.bi-toggles2::before{content:"\f5da"}.bi-tools::before{content:"\f5db"}.bi-tornado::before{content:"\f5dc"}.bi-trash-fill::before{content:"\f5dd"}.bi-trash::before{content:"\f5de"}.bi-trash2-fill::before{content:"\f5df"}.bi-trash2::before{content:"\f5e0"}.bi-tree-fill::before{content:"\f5e1"}.bi-tree::before{content:"\f5e2"}.bi-triangle-fill::before{content:"\f5e3"}.bi-triangle-half::before{content:"\f5e4"}.bi-triangle::before{content:"\f5e5"}.bi-trophy-fill::before{content:"\f5e6"}.bi-trophy::before{content:"\f5e7"}.bi-tropical-storm::before{content:"\f5e8"}.bi-truck-flatbed::before{content:"\f5e9"}.bi-truck::before{content:"\f5ea"}.bi-tsunami::before{content:"\f5eb"}.bi-tv-fill::before{content:"\f5ec"}.bi-tv::before{content:"\f5ed"}.bi-twitch::before{content:"\f5ee"}.bi-twitter::before{content:"\f5ef"}.bi-type-bold::before{content:"\f5f0"}.bi-type-h1::before{content:"\f5f1"}.bi-type-h2::before{content:"\f5f2"}.bi-type-h3::before{content:"\f5f3"}.bi-type-italic::before{content:"\f5f4"}.bi-type-strikethrough::before{content:"\f5f5"}.bi-type-underline::before{content:"\f5f6"}.bi-type::before{content:"\f5f7"}.bi-ui-checks-grid::before{content:"\f5f8"}.bi-ui-checks::before{content:"\f5f9"}.bi-ui-radios-grid::before{content:"\f5fa"}.bi-ui-radios::before{content:"\f5fb"}.bi-umbrella-fill::before{content:"\f5fc"}.bi-umbrella::before{content:"\f5fd"}.bi-union::before{content:"\f5fe"}.bi-unlock-fill::before{content:"\f5ff"}.bi-unlock::before{content:"\f600"}.bi-upc-scan::before{content:"\f601"}.bi-upc::before{content:"\f602"}.bi-upload::before{content:"\f603"}.bi-vector-pen::before{content:"\f604"}.bi-view-list::before{content:"\f605"}.bi-view-stacked::before{content:"\f606"}.bi-vinyl-fill::before{content:"\f607"}.bi-vinyl::before{content:"\f608"}.bi-voicemail::before{content:"\f609"}.bi-volume-down-fill::before{content:"\f60a"}.bi-volume-down::before{content:"\f60b"}.bi-volume-mute-fill::before{content:"\f60c"}.bi-volume-mute::before{content:"\f60d"}.bi-volume-off-fill::before{content:"\f60e"}.bi-volume-off::before{content:"\f60f"}.bi-volume-up-fill::before{content:"\f610"}.bi-volume-up::before{content:"\f611"}.bi-vr::before{content:"\f612"}.bi-wallet-fill::before{content:"\f613"}.bi-wallet::before{content:"\f614"}.bi-wallet2::before{content:"\f615"}.bi-watch::before{content:"\f616"}.bi-water::before{content:"\f617"}.bi-whatsapp::before{content:"\f618"}.bi-wifi-1::before{content:"\f619"}.bi-wifi-2::before{content:"\f61a"}.bi-wifi-off::before{content:"\f61b"}.bi-wifi::before{content:"\f61c"}.bi-wind::before{content:"\f61d"}.bi-window-dock::before{content:"\f61e"}.bi-window-sidebar::before{content:"\f61f"}.bi-window::before{content:"\f620"}.bi-wrench::before{content:"\f621"}.bi-x-circle-fill::before{content:"\f622"}.bi-x-circle::before{content:"\f623"}.bi-x-diamond-fill::before{content:"\f624"}.bi-x-diamond::before{content:"\f625"}.bi-x-octagon-fill::before{content:"\f626"}.bi-x-octagon::before{content:"\f627"}.bi-x-square-fill::before{content:"\f628"}.bi-x-square::before{content:"\f629"}.bi-x::before{content:"\f62a"}.bi-youtube::before{content:"\f62b"}.bi-zoom-in::before{content:"\f62c"}.bi-zoom-out::before{content:"\f62d"}.bi-bank::before{content:"\f62e"}.bi-bank2::before{content:"\f62f"}.bi-bell-slash-fill::before{content:"\f630"}.bi-bell-slash::before{content:"\f631"}.bi-cash-coin::before{content:"\f632"}.bi-check-lg::before{content:"\f633"}.bi-coin::before{content:"\f634"}.bi-currency-bitcoin::before{content:"\f635"}.bi-currency-dollar::before{content:"\f636"}.bi-currency-euro::before{content:"\f637"}.bi-currency-exchange::before{content:"\f638"}.bi-currency-pound::before{content:"\f639"}.bi-currency-yen::before{content:"\f63a"}.bi-dash-lg::before{content:"\f63b"}.bi-exclamation-lg::before{content:"\f63c"}.bi-file-earmark-pdf-fill::before{content:"\f63d"}.bi-file-earmark-pdf::before{content:"\f63e"}.bi-file-pdf-fill::before{content:"\f63f"}.bi-file-pdf::before{content:"\f640"}.bi-gender-ambiguous::before{content:"\f641"}.bi-gender-female::before{content:"\f642"}.bi-gender-male::before{content:"\f643"}.bi-gender-trans::before{content:"\f644"}.bi-headset-vr::before{content:"\f645"}.bi-info-lg::before{content:"\f646"}.bi-mastodon::before{content:"\f647"}.bi-messenger::before{content:"\f648"}.bi-piggy-bank-fill::before{content:"\f649"}.bi-piggy-bank::before{content:"\f64a"}.bi-pin-map-fill::before{content:"\f64b"}.bi-pin-map::before{content:"\f64c"}.bi-plus-lg::before{content:"\f64d"}.bi-question-lg::before{content:"\f64e"}.bi-recycle::before{content:"\f64f"}.bi-reddit::before{content:"\f650"}.bi-safe-fill::before{content:"\f651"}.bi-safe2-fill::before{content:"\f652"}.bi-safe2::before{content:"\f653"}.bi-sd-card-fill::before{content:"\f654"}.bi-sd-card::before{content:"\f655"}.bi-skype::before{content:"\f656"}.bi-slash-lg::before{content:"\f657"}.bi-translate::before{content:"\f658"}.bi-x-lg::before{content:"\f659"}.bi-safe::before{content:"\f65a"}.bi-apple::before{content:"\f65b"}.bi-microsoft::before{content:"\f65d"}.bi-windows::before{content:"\f65e"}.bi-behance::before{content:"\f65c"}.bi-dribbble::before{content:"\f65f"}.bi-line::before{content:"\f660"}.bi-medium::before{content:"\f661"}.bi-paypal::before{content:"\f662"}.bi-pinterest::before{content:"\f663"}.bi-signal::before{content:"\f664"}.bi-snapchat::before{content:"\f665"}.bi-spotify::before{content:"\f666"}.bi-stack-overflow::before{content:"\f667"}.bi-strava::before{content:"\f668"}.bi-wordpress::before{content:"\f669"}.bi-vimeo::before{content:"\f66a"}.bi-activity::before{content:"\f66b"}.bi-easel2-fill::before{content:"\f66c"}.bi-easel2::before{content:"\f66d"}.bi-easel3-fill::before{content:"\f66e"}.bi-easel3::before{content:"\f66f"}.bi-fan::before{content:"\f670"}.bi-fingerprint::before{content:"\f671"}.bi-graph-down-arrow::before{content:"\f672"}.bi-graph-up-arrow::before{content:"\f673"}.bi-hypnotize::before{content:"\f674"}.bi-magic::before{content:"\f675"}.bi-person-rolodex::before{content:"\f676"}.bi-person-video::before{content:"\f677"}.bi-person-video2::before{content:"\f678"}.bi-person-video3::before{content:"\f679"}.bi-person-workspace::before{content:"\f67a"}.bi-radioactive::before{content:"\f67b"}.bi-webcam-fill::before{content:"\f67c"}.bi-webcam::before{content:"\f67d"}.bi-yin-yang::before{content:"\f67e"}.bi-bandaid-fill::before{content:"\f680"}.bi-bandaid::before{content:"\f681"}.bi-bluetooth::before{content:"\f682"}.bi-body-text::before{content:"\f683"}.bi-boombox::before{content:"\f684"}.bi-boxes::before{content:"\f685"}.bi-dpad-fill::before{content:"\f686"}.bi-dpad::before{content:"\f687"}.bi-ear-fill::before{content:"\f688"}.bi-ear::before{content:"\f689"}.bi-envelope-check-fill::before{content:"\f68b"}.bi-envelope-check::before{content:"\f68c"}.bi-envelope-dash-fill::before{content:"\f68e"}.bi-envelope-dash::before{content:"\f68f"}.bi-envelope-exclamation-fill::before{content:"\f691"}.bi-envelope-exclamation::before{content:"\f692"}.bi-envelope-plus-fill::before{content:"\f693"}.bi-envelope-plus::before{content:"\f694"}.bi-envelope-slash-fill::before{content:"\f696"}.bi-envelope-slash::before{content:"\f697"}.bi-envelope-x-fill::before{content:"\f699"}.bi-envelope-x::before{content:"\f69a"}.bi-explicit-fill::before{content:"\f69b"}.bi-explicit::before{content:"\f69c"}.bi-git::before{content:"\f69d"}.bi-infinity::before{content:"\f69e"}.bi-list-columns-reverse::before{content:"\f69f"}.bi-list-columns::before{content:"\f6a0"}.bi-meta::before{content:"\f6a1"}.bi-nintendo-switch::before{content:"\f6a4"}.bi-pc-display-horizontal::before{content:"\f6a5"}.bi-pc-display::before{content:"\f6a6"}.bi-pc-horizontal::before{content:"\f6a7"}.bi-pc::before{content:"\f6a8"}.bi-playstation::before{content:"\f6a9"}.bi-plus-slash-minus::before{content:"\f6aa"}.bi-projector-fill::before{content:"\f6ab"}.bi-projector::before{content:"\f6ac"}.bi-qr-code-scan::before{content:"\f6ad"}.bi-qr-code::before{content:"\f6ae"}.bi-quora::before{content:"\f6af"}.bi-quote::before{content:"\f6b0"}.bi-robot::before{content:"\f6b1"}.bi-send-check-fill::before{content:"\f6b2"}.bi-send-check::before{content:"\f6b3"}.bi-send-dash-fill::before{content:"\f6b4"}.bi-send-dash::before{content:"\f6b5"}.bi-send-exclamation-fill::before{content:"\f6b7"}.bi-send-exclamation::before{content:"\f6b8"}.bi-send-fill::before{content:"\f6b9"}.bi-send-plus-fill::before{content:"\f6ba"}.bi-send-plus::before{content:"\f6bb"}.bi-send-slash-fill::before{content:"\f6bc"}.bi-send-slash::before{content:"\f6bd"}.bi-send-x-fill::before{content:"\f6be"}.bi-send-x::before{content:"\f6bf"}.bi-send::before{content:"\f6c0"}.bi-steam::before{content:"\f6c1"}.bi-terminal-dash::before{content:"\f6c3"}.bi-terminal-plus::before{content:"\f6c4"}.bi-terminal-split::before{content:"\f6c5"}.bi-ticket-detailed-fill::before{content:"\f6c6"}.bi-ticket-detailed::before{content:"\f6c7"}.bi-ticket-fill::before{content:"\f6c8"}.bi-ticket-perforated-fill::before{content:"\f6c9"}.bi-ticket-perforated::before{content:"\f6ca"}.bi-ticket::before{content:"\f6cb"}.bi-tiktok::before{content:"\f6cc"}.bi-window-dash::before{content:"\f6cd"}.bi-window-desktop::before{content:"\f6ce"}.bi-window-fullscreen::before{content:"\f6cf"}.bi-window-plus::before{content:"\f6d0"}.bi-window-split::before{content:"\f6d1"}.bi-window-stack::before{content:"\f6d2"}.bi-window-x::before{content:"\f6d3"}.bi-xbox::before{content:"\f6d4"}.bi-ethernet::before{content:"\f6d5"}.bi-hdmi-fill::before{content:"\f6d6"}.bi-hdmi::before{content:"\f6d7"}.bi-usb-c-fill::before{content:"\f6d8"}.bi-usb-c::before{content:"\f6d9"}.bi-usb-fill::before{content:"\f6da"}.bi-usb-plug-fill::before{content:"\f6db"}.bi-usb-plug::before{content:"\f6dc"}.bi-usb-symbol::before{content:"\f6dd"}.bi-usb::before{content:"\f6de"}.bi-boombox-fill::before{content:"\f6df"}.bi-displayport::before{content:"\f6e1"}.bi-gpu-card::before{content:"\f6e2"}.bi-memory::before{content:"\f6e3"}.bi-modem-fill::before{content:"\f6e4"}.bi-modem::before{content:"\f6e5"}.bi-motherboard-fill::before{content:"\f6e6"}.bi-motherboard::before{content:"\f6e7"}.bi-optical-audio-fill::before{content:"\f6e8"}.bi-optical-audio::before{content:"\f6e9"}.bi-pci-card::before{content:"\f6ea"}.bi-router-fill::before{content:"\f6eb"}.bi-router::before{content:"\f6ec"}.bi-thunderbolt-fill::before{content:"\f6ef"}.bi-thunderbolt::before{content:"\f6f0"}.bi-usb-drive-fill::before{content:"\f6f1"}.bi-usb-drive::before{content:"\f6f2"}.bi-usb-micro-fill::before{content:"\f6f3"}.bi-usb-micro::before{content:"\f6f4"}.bi-usb-mini-fill::before{content:"\f6f5"}.bi-usb-mini::before{content:"\f6f6"}.bi-cloud-haze2::before{content:"\f6f7"}.bi-device-hdd-fill::before{content:"\f6f8"}.bi-device-hdd::before{content:"\f6f9"}.bi-device-ssd-fill::before{content:"\f6fa"}.bi-device-ssd::before{content:"\f6fb"}.bi-displayport-fill::before{content:"\f6fc"}.bi-mortarboard-fill::before{content:"\f6fd"}.bi-mortarboard::before{content:"\f6fe"}.bi-terminal-x::before{content:"\f6ff"}.bi-arrow-through-heart-fill::before{content:"\f700"}.bi-arrow-through-heart::before{content:"\f701"}.bi-badge-sd-fill::before{content:"\f702"}.bi-badge-sd::before{content:"\f703"}.bi-bag-heart-fill::before{content:"\f704"}.bi-bag-heart::before{content:"\f705"}.bi-balloon-fill::before{content:"\f706"}.bi-balloon-heart-fill::before{content:"\f707"}.bi-balloon-heart::before{content:"\f708"}.bi-balloon::before{content:"\f709"}.bi-box2-fill::before{content:"\f70a"}.bi-box2-heart-fill::before{content:"\f70b"}.bi-box2-heart::before{content:"\f70c"}.bi-box2::before{content:"\f70d"}.bi-braces-asterisk::before{content:"\f70e"}.bi-calendar-heart-fill::before{content:"\f70f"}.bi-calendar-heart::before{content:"\f710"}.bi-calendar2-heart-fill::before{content:"\f711"}.bi-calendar2-heart::before{content:"\f712"}.bi-chat-heart-fill::before{content:"\f713"}.bi-chat-heart::before{content:"\f714"}.bi-chat-left-heart-fill::before{content:"\f715"}.bi-chat-left-heart::before{content:"\f716"}.bi-chat-right-heart-fill::before{content:"\f717"}.bi-chat-right-heart::before{content:"\f718"}.bi-chat-square-heart-fill::before{content:"\f719"}.bi-chat-square-heart::before{content:"\f71a"}.bi-clipboard-check-fill::before{content:"\f71b"}.bi-clipboard-data-fill::before{content:"\f71c"}.bi-clipboard-fill::before{content:"\f71d"}.bi-clipboard-heart-fill::before{content:"\f71e"}.bi-clipboard-heart::before{content:"\f71f"}.bi-clipboard-minus-fill::before{content:"\f720"}.bi-clipboard-plus-fill::before{content:"\f721"}.bi-clipboard-pulse::before{content:"\f722"}.bi-clipboard-x-fill::before{content:"\f723"}.bi-clipboard2-check-fill::before{content:"\f724"}.bi-clipboard2-check::before{content:"\f725"}.bi-clipboard2-data-fill::before{content:"\f726"}.bi-clipboard2-data::before{content:"\f727"}.bi-clipboard2-fill::before{content:"\f728"}.bi-clipboard2-heart-fill::before{content:"\f729"}.bi-clipboard2-heart::before{content:"\f72a"}.bi-clipboard2-minus-fill::before{content:"\f72b"}.bi-clipboard2-minus::before{content:"\f72c"}.bi-clipboard2-plus-fill::before{content:"\f72d"}.bi-clipboard2-plus::before{content:"\f72e"}.bi-clipboard2-pulse-fill::before{content:"\f72f"}.bi-clipboard2-pulse::before{content:"\f730"}.bi-clipboard2-x-fill::before{content:"\f731"}.bi-clipboard2-x::before{content:"\f732"}.bi-clipboard2::before{content:"\f733"}.bi-emoji-kiss-fill::before{content:"\f734"}.bi-emoji-kiss::before{content:"\f735"}.bi-envelope-heart-fill::before{content:"\f736"}.bi-envelope-heart::before{content:"\f737"}.bi-envelope-open-heart-fill::before{content:"\f738"}.bi-envelope-open-heart::before{content:"\f739"}.bi-envelope-paper-fill::before{content:"\f73a"}.bi-envelope-paper-heart-fill::before{content:"\f73b"}.bi-envelope-paper-heart::before{content:"\f73c"}.bi-envelope-paper::before{content:"\f73d"}.bi-filetype-aac::before{content:"\f73e"}.bi-filetype-ai::before{content:"\f73f"}.bi-filetype-bmp::before{content:"\f740"}.bi-filetype-cs::before{content:"\f741"}.bi-filetype-css::before{content:"\f742"}.bi-filetype-csv::before{content:"\f743"}.bi-filetype-doc::before{content:"\f744"}.bi-filetype-docx::before{content:"\f745"}.bi-filetype-exe::before{content:"\f746"}.bi-filetype-gif::before{content:"\f747"}.bi-filetype-heic::before{content:"\f748"}.bi-filetype-html::before{content:"\f749"}.bi-filetype-java::before{content:"\f74a"}.bi-filetype-jpg::before{content:"\f74b"}.bi-filetype-js::before{content:"\f74c"}.bi-filetype-jsx::before{content:"\f74d"}.bi-filetype-key::before{content:"\f74e"}.bi-filetype-m4p::before{content:"\f74f"}.bi-filetype-md::before{content:"\f750"}.bi-filetype-mdx::before{content:"\f751"}.bi-filetype-mov::before{content:"\f752"}.bi-filetype-mp3::before{content:"\f753"}.bi-filetype-mp4::before{content:"\f754"}.bi-filetype-otf::before{content:"\f755"}.bi-filetype-pdf::before{content:"\f756"}.bi-filetype-php::before{content:"\f757"}.bi-filetype-png::before{content:"\f758"}.bi-filetype-ppt::before{content:"\f75a"}.bi-filetype-psd::before{content:"\f75b"}.bi-filetype-py::before{content:"\f75c"}.bi-filetype-raw::before{content:"\f75d"}.bi-filetype-rb::before{content:"\f75e"}.bi-filetype-sass::before{content:"\f75f"}.bi-filetype-scss::before{content:"\f760"}.bi-filetype-sh::before{content:"\f761"}.bi-filetype-svg::before{content:"\f762"}.bi-filetype-tiff::before{content:"\f763"}.bi-filetype-tsx::before{content:"\f764"}.bi-filetype-ttf::before{content:"\f765"}.bi-filetype-txt::before{content:"\f766"}.bi-filetype-wav::before{content:"\f767"}.bi-filetype-woff::before{content:"\f768"}.bi-filetype-xls::before{content:"\f76a"}.bi-filetype-xml::before{content:"\f76b"}.bi-filetype-yml::before{content:"\f76c"}.bi-heart-arrow::before{content:"\f76d"}.bi-heart-pulse-fill::before{content:"\f76e"}.bi-heart-pulse::before{content:"\f76f"}.bi-heartbreak-fill::before{content:"\f770"}.bi-heartbreak::before{content:"\f771"}.bi-hearts::before{content:"\f772"}.bi-hospital-fill::before{content:"\f773"}.bi-hospital::before{content:"\f774"}.bi-house-heart-fill::before{content:"\f775"}.bi-house-heart::before{content:"\f776"}.bi-incognito::before{content:"\f777"}.bi-magnet-fill::before{content:"\f778"}.bi-magnet::before{content:"\f779"}.bi-person-heart::before{content:"\f77a"}.bi-person-hearts::before{content:"\f77b"}.bi-phone-flip::before{content:"\f77c"}.bi-plugin::before{content:"\f77d"}.bi-postage-fill::before{content:"\f77e"}.bi-postage-heart-fill::before{content:"\f77f"}.bi-postage-heart::before{content:"\f780"}.bi-postage::before{content:"\f781"}.bi-postcard-fill::before{content:"\f782"}.bi-postcard-heart-fill::before{content:"\f783"}.bi-postcard-heart::before{content:"\f784"}.bi-postcard::before{content:"\f785"}.bi-search-heart-fill::before{content:"\f786"}.bi-search-heart::before{content:"\f787"}.bi-sliders2-vertical::before{content:"\f788"}.bi-sliders2::before{content:"\f789"}.bi-trash3-fill::before{content:"\f78a"}.bi-trash3::before{content:"\f78b"}.bi-valentine::before{content:"\f78c"}.bi-valentine2::before{content:"\f78d"}.bi-wrench-adjustable-circle-fill::before{content:"\f78e"}.bi-wrench-adjustable-circle::before{content:"\f78f"}.bi-wrench-adjustable::before{content:"\f790"}.bi-filetype-json::before{content:"\f791"}.bi-filetype-pptx::before{content:"\f792"}.bi-filetype-xlsx::before{content:"\f793"}.bi-1-circle-fill::before{content:"\f796"}.bi-1-circle::before{content:"\f797"}.bi-1-square-fill::before{content:"\f798"}.bi-1-square::before{content:"\f799"}.bi-2-circle-fill::before{content:"\f79c"}.bi-2-circle::before{content:"\f79d"}.bi-2-square-fill::before{content:"\f79e"}.bi-2-square::before{content:"\f79f"}.bi-3-circle-fill::before{content:"\f7a2"}.bi-3-circle::before{content:"\f7a3"}.bi-3-square-fill::before{content:"\f7a4"}.bi-3-square::before{content:"\f7a5"}.bi-4-circle-fill::before{content:"\f7a8"}.bi-4-circle::before{content:"\f7a9"}.bi-4-square-fill::before{content:"\f7aa"}.bi-4-square::before{content:"\f7ab"}.bi-5-circle-fill::before{content:"\f7ae"}.bi-5-circle::before{content:"\f7af"}.bi-5-square-fill::before{content:"\f7b0"}.bi-5-square::before{content:"\f7b1"}.bi-6-circle-fill::before{content:"\f7b4"}.bi-6-circle::before{content:"\f7b5"}.bi-6-square-fill::before{content:"\f7b6"}.bi-6-square::before{content:"\f7b7"}.bi-7-circle-fill::before{content:"\f7ba"}.bi-7-circle::before{content:"\f7bb"}.bi-7-square-fill::before{content:"\f7bc"}.bi-7-square::before{content:"\f7bd"}.bi-8-circle-fill::before{content:"\f7c0"}.bi-8-circle::before{content:"\f7c1"}.bi-8-square-fill::before{content:"\f7c2"}.bi-8-square::before{content:"\f7c3"}.bi-9-circle-fill::before{content:"\f7c6"}.bi-9-circle::before{content:"\f7c7"}.bi-9-square-fill::before{content:"\f7c8"}.bi-9-square::before{content:"\f7c9"}.bi-airplane-engines-fill::before{content:"\f7ca"}.bi-airplane-engines::before{content:"\f7cb"}.bi-airplane-fill::before{content:"\f7cc"}.bi-airplane::before{content:"\f7cd"}.bi-alexa::before{content:"\f7ce"}.bi-alipay::before{content:"\f7cf"}.bi-android::before{content:"\f7d0"}.bi-android2::before{content:"\f7d1"}.bi-box-fill::before{content:"\f7d2"}.bi-box-seam-fill::before{content:"\f7d3"}.bi-browser-chrome::before{content:"\f7d4"}.bi-browser-edge::before{content:"\f7d5"}.bi-browser-firefox::before{content:"\f7d6"}.bi-browser-safari::before{content:"\f7d7"}.bi-c-circle-fill::before{content:"\f7da"}.bi-c-circle::before{content:"\f7db"}.bi-c-square-fill::before{content:"\f7dc"}.bi-c-square::before{content:"\f7dd"}.bi-capsule-pill::before{content:"\f7de"}.bi-capsule::before{content:"\f7df"}.bi-car-front-fill::before{content:"\f7e0"}.bi-car-front::before{content:"\f7e1"}.bi-cassette-fill::before{content:"\f7e2"}.bi-cassette::before{content:"\f7e3"}.bi-cc-circle-fill::before{content:"\f7e6"}.bi-cc-circle::before{content:"\f7e7"}.bi-cc-square-fill::before{content:"\f7e8"}.bi-cc-square::before{content:"\f7e9"}.bi-cup-hot-fill::before{content:"\f7ea"}.bi-cup-hot::before{content:"\f7eb"}.bi-currency-rupee::before{content:"\f7ec"}.bi-dropbox::before{content:"\f7ed"}.bi-escape::before{content:"\f7ee"}.bi-fast-forward-btn-fill::before{content:"\f7ef"}.bi-fast-forward-btn::before{content:"\f7f0"}.bi-fast-forward-circle-fill::before{content:"\f7f1"}.bi-fast-forward-circle::before{content:"\f7f2"}.bi-fast-forward-fill::before{content:"\f7f3"}.bi-fast-forward::before{content:"\f7f4"}.bi-filetype-sql::before{content:"\f7f5"}.bi-fire::before{content:"\f7f6"}.bi-google-play::before{content:"\f7f7"}.bi-h-circle-fill::before{content:"\f7fa"}.bi-h-circle::before{content:"\f7fb"}.bi-h-square-fill::before{content:"\f7fc"}.bi-h-square::before{content:"\f7fd"}.bi-indent::before{content:"\f7fe"}.bi-lungs-fill::before{content:"\f7ff"}.bi-lungs::before{content:"\f800"}.bi-microsoft-teams::before{content:"\f801"}.bi-p-circle-fill::before{content:"\f804"}.bi-p-circle::before{content:"\f805"}.bi-p-square-fill::before{content:"\f806"}.bi-p-square::before{content:"\f807"}.bi-pass-fill::before{content:"\f808"}.bi-pass::before{content:"\f809"}.bi-prescription::before{content:"\f80a"}.bi-prescription2::before{content:"\f80b"}.bi-r-circle-fill::before{content:"\f80e"}.bi-r-circle::before{content:"\f80f"}.bi-r-square-fill::before{content:"\f810"}.bi-r-square::before{content:"\f811"}.bi-repeat-1::before{content:"\f812"}.bi-repeat::before{content:"\f813"}.bi-rewind-btn-fill::before{content:"\f814"}.bi-rewind-btn::before{content:"\f815"}.bi-rewind-circle-fill::before{content:"\f816"}.bi-rewind-circle::before{content:"\f817"}.bi-rewind-fill::before{content:"\f818"}.bi-rewind::before{content:"\f819"}.bi-train-freight-front-fill::before{content:"\f81a"}.bi-train-freight-front::before{content:"\f81b"}.bi-train-front-fill::before{content:"\f81c"}.bi-train-front::before{content:"\f81d"}.bi-train-lightrail-front-fill::before{content:"\f81e"}.bi-train-lightrail-front::before{content:"\f81f"}.bi-truck-front-fill::before{content:"\f820"}.bi-truck-front::before{content:"\f821"}.bi-ubuntu::before{content:"\f822"}.bi-unindent::before{content:"\f823"}.bi-unity::before{content:"\f824"}.bi-universal-access-circle::before{content:"\f825"}.bi-universal-access::before{content:"\f826"}.bi-virus::before{content:"\f827"}.bi-virus2::before{content:"\f828"}.bi-wechat::before{content:"\f829"}.bi-yelp::before{content:"\f82a"}.bi-sign-stop-fill::before{content:"\f82b"}.bi-sign-stop-lights-fill::before{content:"\f82c"}.bi-sign-stop-lights::before{content:"\f82d"}.bi-sign-stop::before{content:"\f82e"}.bi-sign-turn-left-fill::before{content:"\f82f"}.bi-sign-turn-left::before{content:"\f830"}.bi-sign-turn-right-fill::before{content:"\f831"}.bi-sign-turn-right::before{content:"\f832"}.bi-sign-turn-slight-left-fill::before{content:"\f833"}.bi-sign-turn-slight-left::before{content:"\f834"}.bi-sign-turn-slight-right-fill::before{content:"\f835"}.bi-sign-turn-slight-right::before{content:"\f836"}.bi-sign-yield-fill::before{content:"\f837"}.bi-sign-yield::before{content:"\f838"}.bi-ev-station-fill::before{content:"\f839"}.bi-ev-station::before{content:"\f83a"}.bi-fuel-pump-diesel-fill::before{content:"\f83b"}.bi-fuel-pump-diesel::before{content:"\f83c"}.bi-fuel-pump-fill::before{content:"\f83d"}.bi-fuel-pump::before{content:"\f83e"}.bi-0-circle-fill::before{content:"\f83f"}.bi-0-circle::before{content:"\f840"}.bi-0-square-fill::before{content:"\f841"}.bi-0-square::before{content:"\f842"}.bi-rocket-fill::before{content:"\f843"}.bi-rocket-takeoff-fill::before{content:"\f844"}.bi-rocket-takeoff::before{content:"\f845"}.bi-rocket::before{content:"\f846"}.bi-stripe::before{content:"\f847"}.bi-subscript::before{content:"\f848"}.bi-superscript::before{content:"\f849"}.bi-trello::before{content:"\f84a"}.bi-envelope-at-fill::before{content:"\f84b"}.bi-envelope-at::before{content:"\f84c"}.bi-regex::before{content:"\f84d"}.bi-text-wrap::before{content:"\f84e"}.bi-sign-dead-end-fill::before{content:"\f84f"}.bi-sign-dead-end::before{content:"\f850"}.bi-sign-do-not-enter-fill::before{content:"\f851"}.bi-sign-do-not-enter::before{content:"\f852"}.bi-sign-intersection-fill::before{content:"\f853"}.bi-sign-intersection-side-fill::before{content:"\f854"}.bi-sign-intersection-side::before{content:"\f855"}.bi-sign-intersection-t-fill::before{content:"\f856"}.bi-sign-intersection-t::before{content:"\f857"}.bi-sign-intersection-y-fill::before{content:"\f858"}.bi-sign-intersection-y::before{content:"\f859"}.bi-sign-intersection::before{content:"\f85a"}.bi-sign-merge-left-fill::before{content:"\f85b"}.bi-sign-merge-left::before{content:"\f85c"}.bi-sign-merge-right-fill::before{content:"\f85d"}.bi-sign-merge-right::before{content:"\f85e"}.bi-sign-no-left-turn-fill::before{content:"\f85f"}.bi-sign-no-left-turn::before{content:"\f860"}.bi-sign-no-parking-fill::before{content:"\f861"}.bi-sign-no-parking::before{content:"\f862"}.bi-sign-no-right-turn-fill::before{content:"\f863"}.bi-sign-no-right-turn::before{content:"\f864"}.bi-sign-railroad-fill::before{content:"\f865"}.bi-sign-railroad::before{content:"\f866"}.bi-building-add::before{content:"\f867"}.bi-building-check::before{content:"\f868"}.bi-building-dash::before{content:"\f869"}.bi-building-down::before{content:"\f86a"}.bi-building-exclamation::before{content:"\f86b"}.bi-building-fill-add::before{content:"\f86c"}.bi-building-fill-check::before{content:"\f86d"}.bi-building-fill-dash::before{content:"\f86e"}.bi-building-fill-down::before{content:"\f86f"}.bi-building-fill-exclamation::before{content:"\f870"}.bi-building-fill-gear::before{content:"\f871"}.bi-building-fill-lock::before{content:"\f872"}.bi-building-fill-slash::before{content:"\f873"}.bi-building-fill-up::before{content:"\f874"}.bi-building-fill-x::before{content:"\f875"}.bi-building-fill::before{content:"\f876"}.bi-building-gear::before{content:"\f877"}.bi-building-lock::before{content:"\f878"}.bi-building-slash::before{content:"\f879"}.bi-building-up::before{content:"\f87a"}.bi-building-x::before{content:"\f87b"}.bi-buildings-fill::before{content:"\f87c"}.bi-buildings::before{content:"\f87d"}.bi-bus-front-fill::before{content:"\f87e"}.bi-bus-front::before{content:"\f87f"}.bi-ev-front-fill::before{content:"\f880"}.bi-ev-front::before{content:"\f881"}.bi-globe-americas::before{content:"\f882"}.bi-globe-asia-australia::before{content:"\f883"}.bi-globe-central-south-asia::before{content:"\f884"}.bi-globe-europe-africa::before{content:"\f885"}.bi-house-add-fill::before{content:"\f886"}.bi-house-add::before{content:"\f887"}.bi-house-check-fill::before{content:"\f888"}.bi-house-check::before{content:"\f889"}.bi-house-dash-fill::before{content:"\f88a"}.bi-house-dash::before{content:"\f88b"}.bi-house-down-fill::before{content:"\f88c"}.bi-house-down::before{content:"\f88d"}.bi-house-exclamation-fill::before{content:"\f88e"}.bi-house-exclamation::before{content:"\f88f"}.bi-house-gear-fill::before{content:"\f890"}.bi-house-gear::before{content:"\f891"}.bi-house-lock-fill::before{content:"\f892"}.bi-house-lock::before{content:"\f893"}.bi-house-slash-fill::before{content:"\f894"}.bi-house-slash::before{content:"\f895"}.bi-house-up-fill::before{content:"\f896"}.bi-house-up::before{content:"\f897"}.bi-house-x-fill::before{content:"\f898"}.bi-house-x::before{content:"\f899"}.bi-person-add::before{content:"\f89a"}.bi-person-down::before{content:"\f89b"}.bi-person-exclamation::before{content:"\f89c"}.bi-person-fill-add::before{content:"\f89d"}.bi-person-fill-check::before{content:"\f89e"}.bi-person-fill-dash::before{content:"\f89f"}.bi-person-fill-down::before{content:"\f8a0"}.bi-person-fill-exclamation::before{content:"\f8a1"}.bi-person-fill-gear::before{content:"\f8a2"}.bi-person-fill-lock::before{content:"\f8a3"}.bi-person-fill-slash::before{content:"\f8a4"}.bi-person-fill-up::before{content:"\f8a5"}.bi-person-fill-x::before{content:"\f8a6"}.bi-person-gear::before{content:"\f8a7"}.bi-person-lock::before{content:"\f8a8"}.bi-person-slash::before{content:"\f8a9"}.bi-person-up::before{content:"\f8aa"}.bi-scooter::before{content:"\f8ab"}.bi-taxi-front-fill::before{content:"\f8ac"}.bi-taxi-front::before{content:"\f8ad"}.bi-amd::before{content:"\f8ae"}.bi-database-add::before{content:"\f8af"}.bi-database-check::before{content:"\f8b0"}.bi-database-dash::before{content:"\f8b1"}.bi-database-down::before{content:"\f8b2"}.bi-database-exclamation::before{content:"\f8b3"}.bi-database-fill-add::before{content:"\f8b4"}.bi-database-fill-check::before{content:"\f8b5"}.bi-database-fill-dash::before{content:"\f8b6"}.bi-database-fill-down::before{content:"\f8b7"}.bi-database-fill-exclamation::before{content:"\f8b8"}.bi-database-fill-gear::before{content:"\f8b9"}.bi-database-fill-lock::before{content:"\f8ba"}.bi-database-fill-slash::before{content:"\f8bb"}.bi-database-fill-up::before{content:"\f8bc"}.bi-database-fill-x::before{content:"\f8bd"}.bi-database-fill::before{content:"\f8be"}.bi-database-gear::before{content:"\f8bf"}.bi-database-lock::before{content:"\f8c0"}.bi-database-slash::before{content:"\f8c1"}.bi-database-up::before{content:"\f8c2"}.bi-database-x::before{content:"\f8c3"}.bi-database::before{content:"\f8c4"}.bi-houses-fill::before{content:"\f8c5"}.bi-houses::before{content:"\f8c6"}.bi-nvidia::before{content:"\f8c7"}.bi-person-vcard-fill::before{content:"\f8c8"}.bi-person-vcard::before{content:"\f8c9"}.bi-sina-weibo::before{content:"\f8ca"}.bi-tencent-qq::before{content:"\f8cb"}.bi-wikipedia::before{content:"\f8cc"}.bi-alphabet-uppercase::before{content:"\f2a5"}.bi-alphabet::before{content:"\f68a"}.bi-amazon::before{content:"\f68d"}.bi-arrows-collapse-vertical::before{content:"\f690"}.bi-arrows-expand-vertical::before{content:"\f695"}.bi-arrows-vertical::before{content:"\f698"}.bi-arrows::before{content:"\f6a2"}.bi-ban-fill::before{content:"\f6a3"}.bi-ban::before{content:"\f6b6"}.bi-bing::before{content:"\f6c2"}.bi-cake::before{content:"\f6e0"}.bi-cake2::before{content:"\f6ed"}.bi-cookie::before{content:"\f6ee"}.bi-copy::before{content:"\f759"}.bi-crosshair::before{content:"\f769"}.bi-crosshair2::before{content:"\f794"}.bi-emoji-astonished-fill::before{content:"\f795"}.bi-emoji-astonished::before{content:"\f79a"}.bi-emoji-grimace-fill::before{content:"\f79b"}.bi-emoji-grimace::before{content:"\f7a0"}.bi-emoji-grin-fill::before{content:"\f7a1"}.bi-emoji-grin::before{content:"\f7a6"}.bi-emoji-surprise-fill::before{content:"\f7a7"}.bi-emoji-surprise::before{content:"\f7ac"}.bi-emoji-tear-fill::before{content:"\f7ad"}.bi-emoji-tear::before{content:"\f7b2"}.bi-envelope-arrow-down-fill::before{content:"\f7b3"}.bi-envelope-arrow-down::before{content:"\f7b8"}.bi-envelope-arrow-up-fill::before{content:"\f7b9"}.bi-envelope-arrow-up::before{content:"\f7be"}.bi-feather::before{content:"\f7bf"}.bi-feather2::before{content:"\f7c4"}.bi-floppy-fill::before{content:"\f7c5"}.bi-floppy::before{content:"\f7d8"}.bi-floppy2-fill::before{content:"\f7d9"}.bi-floppy2::before{content:"\f7e4"}.bi-gitlab::before{content:"\f7e5"}.bi-highlighter::before{content:"\f7f8"}.bi-marker-tip::before{content:"\f802"}.bi-nvme-fill::before{content:"\f803"}.bi-nvme::before{content:"\f80c"}.bi-opencollective::before{content:"\f80d"}.bi-pci-card-network::before{content:"\f8cd"}.bi-pci-card-sound::before{content:"\f8ce"}.bi-radar::before{content:"\f8cf"}.bi-send-arrow-down-fill::before{content:"\f8d0"}.bi-send-arrow-down::before{content:"\f8d1"}.bi-send-arrow-up-fill::before{content:"\f8d2"}.bi-send-arrow-up::before{content:"\f8d3"}.bi-sim-slash-fill::before{content:"\f8d4"}.bi-sim-slash::before{content:"\f8d5"}.bi-sourceforge::before{content:"\f8d6"}.bi-substack::before{content:"\f8d7"}.bi-threads-fill::before{content:"\f8d8"}.bi-threads::before{content:"\f8d9"}.bi-transparency::before{content:"\f8da"}.bi-twitter-x::before{content:"\f8db"}.bi-type-h4::before{content:"\f8dc"}.bi-type-h5::before{content:"\f8dd"}.bi-type-h6::before{content:"\f8de"}.bi-backpack-fill::before{content:"\f8df"}.bi-backpack::before{content:"\f8e0"}.bi-backpack2-fill::before{content:"\f8e1"}.bi-backpack2::before{content:"\f8e2"}.bi-backpack3-fill::before{content:"\f8e3"}.bi-backpack3::before{content:"\f8e4"}.bi-backpack4-fill::before{content:"\f8e5"}.bi-backpack4::before{content:"\f8e6"}.bi-brilliance::before{content:"\f8e7"}.bi-cake-fill::before{content:"\f8e8"}.bi-cake2-fill::before{content:"\f8e9"}.bi-duffle-fill::before{content:"\f8ea"}.bi-duffle::before{content:"\f8eb"}.bi-exposure::before{content:"\f8ec"}.bi-gender-neuter::before{content:"\f8ed"}.bi-highlights::before{content:"\f8ee"}.bi-luggage-fill::before{content:"\f8ef"}.bi-luggage::before{content:"\f8f0"}.bi-mailbox-flag::before{content:"\f8f1"}.bi-mailbox2-flag::before{content:"\f8f2"}.bi-noise-reduction::before{content:"\f8f3"}.bi-passport-fill::before{content:"\f8f4"}.bi-passport::before{content:"\f8f5"}.bi-person-arms-up::before{content:"\f8f6"}.bi-person-raised-hand::before{content:"\f8f7"}.bi-person-standing-dress::before{content:"\f8f8"}.bi-person-standing::before{content:"\f8f9"}.bi-person-walking::before{content:"\f8fa"}.bi-person-wheelchair::before{content:"\f8fb"}.bi-shadows::before{content:"\f8fc"}.bi-suitcase-fill::before{content:"\f8fd"}.bi-suitcase-lg-fill::before{content:"\f8fe"}.bi-suitcase-lg::before{content:"\f8ff"}.bi-suitcase::before{content:"\f900"}.bi-suitcase2-fill::before{content:"\f901"}.bi-suitcase2::before{content:"\f902"}.bi-vignette::before{content:"\f903"} \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.scss b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.scss index 99770d07d..ea5c018f4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.scss +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/bootstrap-icons.scss @@ -1,8 +1,15 @@ +/*! + * Bootstrap Icons v1.11.3 (https://icons.getbootstrap.com/) + * Copyright 2019-2024 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE) + */ + $bootstrap-icons-font: "bootstrap-icons" !default; $bootstrap-icons-font-dir: "./fonts" !default; -$bootstrap-icons-font-file: #{$bootstrap-icons-font-dir}/#{$bootstrap-icons-font} !default; -$bootstrap-icons-font-hash: "8d200481aa7f02a2d63a331fc782cfaf" !default; -$bootstrap-icons-font-src: url("#{$bootstrap-icons-font-file}.woff2?#{$bootstrap-icons-font-hash}") format("woff2"), url("#{$bootstrap-icons-font-file}.woff?#{$bootstrap-icons-font-hash}") format("woff") !default; +$bootstrap-icons-font-file: "#{$bootstrap-icons-font-dir}/#{$bootstrap-icons-font}" !default; +$bootstrap-icons-font-hash: "24e3eb84d0bcaf83d77f904c78ac1f47" !default; +$bootstrap-icons-font-src: url("#{$bootstrap-icons-font-file}.woff2?#{$bootstrap-icons-font-hash}") format("woff2"), + url("#{$bootstrap-icons-font-file}.woff?#{$bootstrap-icons-font-hash}") format("woff") !default; @font-face { font-display: block; @@ -447,7 +454,6 @@ $bootstrap-icons-map: ( "cloud-fog2": "\f2a2", "cloud-hail-fill": "\f2a3", "cloud-hail": "\f2a4", - "cloud-haze-1": "\f2a5", "cloud-haze-fill": "\f2a6", "cloud-haze": "\f2a7", "cloud-haze2-fill": "\f2a8", @@ -1443,21 +1449,16 @@ $bootstrap-icons-map: ( "dpad": "\f687", "ear-fill": "\f688", "ear": "\f689", - "envelope-check-1": "\f68a", "envelope-check-fill": "\f68b", "envelope-check": "\f68c", - "envelope-dash-1": "\f68d", "envelope-dash-fill": "\f68e", "envelope-dash": "\f68f", - "envelope-exclamation-1": "\f690", "envelope-exclamation-fill": "\f691", "envelope-exclamation": "\f692", "envelope-plus-fill": "\f693", "envelope-plus": "\f694", - "envelope-slash-1": "\f695", "envelope-slash-fill": "\f696", "envelope-slash": "\f697", - "envelope-x-1": "\f698", "envelope-x-fill": "\f699", "envelope-x": "\f69a", "explicit-fill": "\f69b", @@ -1467,8 +1468,6 @@ $bootstrap-icons-map: ( "list-columns-reverse": "\f69f", "list-columns": "\f6a0", "meta": "\f6a1", - "mortorboard-fill": "\f6a2", - "mortorboard": "\f6a3", "nintendo-switch": "\f6a4", "pc-display-horizontal": "\f6a5", "pc-display": "\f6a6", @@ -1487,7 +1486,6 @@ $bootstrap-icons-map: ( "send-check": "\f6b3", "send-dash-fill": "\f6b4", "send-dash": "\f6b5", - "send-exclamation-1": "\f6b6", "send-exclamation-fill": "\f6b7", "send-exclamation": "\f6b8", "send-fill": "\f6b9", @@ -1499,7 +1497,6 @@ $bootstrap-icons-map: ( "send-x": "\f6bf", "send": "\f6c0", "steam": "\f6c1", - "terminal-dash-1": "\f6c2", "terminal-dash": "\f6c3", "terminal-plus": "\f6c4", "terminal-split": "\f6c5", @@ -1529,7 +1526,6 @@ $bootstrap-icons-map: ( "usb-symbol": "\f6dd", "usb": "\f6de", "boombox-fill": "\f6df", - "displayport-1": "\f6e0", "displayport": "\f6e1", "gpu-card": "\f6e2", "memory": "\f6e3", @@ -1542,8 +1538,6 @@ $bootstrap-icons-map: ( "pci-card": "\f6ea", "router-fill": "\f6eb", "router": "\f6ec", - "ssd-fill": "\f6ed", - "ssd": "\f6ee", "thunderbolt-fill": "\f6ef", "thunderbolt": "\f6f0", "usb-drive-fill": "\f6f1", @@ -1650,7 +1644,6 @@ $bootstrap-icons-map: ( "filetype-pdf": "\f756", "filetype-php": "\f757", "filetype-png": "\f758", - "filetype-ppt-1": "\f759", "filetype-ppt": "\f75a", "filetype-psd": "\f75b", "filetype-py": "\f75c", @@ -1666,7 +1659,6 @@ $bootstrap-icons-map: ( "filetype-txt": "\f766", "filetype-wav": "\f767", "filetype-woff": "\f768", - "filetype-xls-1": "\f769", "filetype-xls": "\f76a", "filetype-xml": "\f76b", "filetype-yml": "\f76c", @@ -1709,56 +1701,38 @@ $bootstrap-icons-map: ( "filetype-json": "\f791", "filetype-pptx": "\f792", "filetype-xlsx": "\f793", - "1-circle-1": "\f794", - "1-circle-fill-1": "\f795", "1-circle-fill": "\f796", "1-circle": "\f797", "1-square-fill": "\f798", "1-square": "\f799", - "2-circle-1": "\f79a", - "2-circle-fill-1": "\f79b", "2-circle-fill": "\f79c", "2-circle": "\f79d", "2-square-fill": "\f79e", "2-square": "\f79f", - "3-circle-1": "\f7a0", - "3-circle-fill-1": "\f7a1", "3-circle-fill": "\f7a2", "3-circle": "\f7a3", "3-square-fill": "\f7a4", "3-square": "\f7a5", - "4-circle-1": "\f7a6", - "4-circle-fill-1": "\f7a7", "4-circle-fill": "\f7a8", "4-circle": "\f7a9", "4-square-fill": "\f7aa", "4-square": "\f7ab", - "5-circle-1": "\f7ac", - "5-circle-fill-1": "\f7ad", "5-circle-fill": "\f7ae", "5-circle": "\f7af", "5-square-fill": "\f7b0", "5-square": "\f7b1", - "6-circle-1": "\f7b2", - "6-circle-fill-1": "\f7b3", "6-circle-fill": "\f7b4", "6-circle": "\f7b5", "6-square-fill": "\f7b6", "6-square": "\f7b7", - "7-circle-1": "\f7b8", - "7-circle-fill-1": "\f7b9", "7-circle-fill": "\f7ba", "7-circle": "\f7bb", "7-square-fill": "\f7bc", "7-square": "\f7bd", - "8-circle-1": "\f7be", - "8-circle-fill-1": "\f7bf", "8-circle-fill": "\f7c0", "8-circle": "\f7c1", "8-square-fill": "\f7c2", "8-square": "\f7c3", - "9-circle-1": "\f7c4", - "9-circle-fill-1": "\f7c5", "9-circle-fill": "\f7c6", "9-circle": "\f7c7", "9-square-fill": "\f7c8", @@ -1777,8 +1751,6 @@ $bootstrap-icons-map: ( "browser-edge": "\f7d5", "browser-firefox": "\f7d6", "browser-safari": "\f7d7", - "c-circle-1": "\f7d8", - "c-circle-fill-1": "\f7d9", "c-circle-fill": "\f7da", "c-circle": "\f7db", "c-square-fill": "\f7dc", @@ -1789,8 +1761,6 @@ $bootstrap-icons-map: ( "car-front": "\f7e1", "cassette-fill": "\f7e2", "cassette": "\f7e3", - "cc-circle-1": "\f7e4", - "cc-circle-fill-1": "\f7e5", "cc-circle-fill": "\f7e6", "cc-circle": "\f7e7", "cc-square-fill": "\f7e8", @@ -1809,8 +1779,6 @@ $bootstrap-icons-map: ( "filetype-sql": "\f7f5", "fire": "\f7f6", "google-play": "\f7f7", - "h-circle-1": "\f7f8", - "h-circle-fill-1": "\f7f9", "h-circle-fill": "\f7fa", "h-circle": "\f7fb", "h-square-fill": "\f7fc", @@ -1819,8 +1787,6 @@ $bootstrap-icons-map: ( "lungs-fill": "\f7ff", "lungs": "\f800", "microsoft-teams": "\f801", - "p-circle-1": "\f802", - "p-circle-fill-1": "\f803", "p-circle-fill": "\f804", "p-circle": "\f805", "p-square-fill": "\f806", @@ -1829,8 +1795,6 @@ $bootstrap-icons-map: ( "pass": "\f809", "prescription": "\f80a", "prescription2": "\f80b", - "r-circle-1": "\f80c", - "r-circle-fill-1": "\f80d", "r-circle-fill": "\f80e", "r-circle": "\f80f", "r-square-fill": "\f810", @@ -1880,1859 +1844,247 @@ $bootstrap-icons-map: ( "fuel-pump-diesel": "\f83c", "fuel-pump-fill": "\f83d", "fuel-pump": "\f83e", + "0-circle-fill": "\f83f", + "0-circle": "\f840", + "0-square-fill": "\f841", + "0-square": "\f842", + "rocket-fill": "\f843", + "rocket-takeoff-fill": "\f844", + "rocket-takeoff": "\f845", + "rocket": "\f846", + "stripe": "\f847", + "subscript": "\f848", + "superscript": "\f849", + "trello": "\f84a", + "envelope-at-fill": "\f84b", + "envelope-at": "\f84c", + "regex": "\f84d", + "text-wrap": "\f84e", + "sign-dead-end-fill": "\f84f", + "sign-dead-end": "\f850", + "sign-do-not-enter-fill": "\f851", + "sign-do-not-enter": "\f852", + "sign-intersection-fill": "\f853", + "sign-intersection-side-fill": "\f854", + "sign-intersection-side": "\f855", + "sign-intersection-t-fill": "\f856", + "sign-intersection-t": "\f857", + "sign-intersection-y-fill": "\f858", + "sign-intersection-y": "\f859", + "sign-intersection": "\f85a", + "sign-merge-left-fill": "\f85b", + "sign-merge-left": "\f85c", + "sign-merge-right-fill": "\f85d", + "sign-merge-right": "\f85e", + "sign-no-left-turn-fill": "\f85f", + "sign-no-left-turn": "\f860", + "sign-no-parking-fill": "\f861", + "sign-no-parking": "\f862", + "sign-no-right-turn-fill": "\f863", + "sign-no-right-turn": "\f864", + "sign-railroad-fill": "\f865", + "sign-railroad": "\f866", + "building-add": "\f867", + "building-check": "\f868", + "building-dash": "\f869", + "building-down": "\f86a", + "building-exclamation": "\f86b", + "building-fill-add": "\f86c", + "building-fill-check": "\f86d", + "building-fill-dash": "\f86e", + "building-fill-down": "\f86f", + "building-fill-exclamation": "\f870", + "building-fill-gear": "\f871", + "building-fill-lock": "\f872", + "building-fill-slash": "\f873", + "building-fill-up": "\f874", + "building-fill-x": "\f875", + "building-fill": "\f876", + "building-gear": "\f877", + "building-lock": "\f878", + "building-slash": "\f879", + "building-up": "\f87a", + "building-x": "\f87b", + "buildings-fill": "\f87c", + "buildings": "\f87d", + "bus-front-fill": "\f87e", + "bus-front": "\f87f", + "ev-front-fill": "\f880", + "ev-front": "\f881", + "globe-americas": "\f882", + "globe-asia-australia": "\f883", + "globe-central-south-asia": "\f884", + "globe-europe-africa": "\f885", + "house-add-fill": "\f886", + "house-add": "\f887", + "house-check-fill": "\f888", + "house-check": "\f889", + "house-dash-fill": "\f88a", + "house-dash": "\f88b", + "house-down-fill": "\f88c", + "house-down": "\f88d", + "house-exclamation-fill": "\f88e", + "house-exclamation": "\f88f", + "house-gear-fill": "\f890", + "house-gear": "\f891", + "house-lock-fill": "\f892", + "house-lock": "\f893", + "house-slash-fill": "\f894", + "house-slash": "\f895", + "house-up-fill": "\f896", + "house-up": "\f897", + "house-x-fill": "\f898", + "house-x": "\f899", + "person-add": "\f89a", + "person-down": "\f89b", + "person-exclamation": "\f89c", + "person-fill-add": "\f89d", + "person-fill-check": "\f89e", + "person-fill-dash": "\f89f", + "person-fill-down": "\f8a0", + "person-fill-exclamation": "\f8a1", + "person-fill-gear": "\f8a2", + "person-fill-lock": "\f8a3", + "person-fill-slash": "\f8a4", + "person-fill-up": "\f8a5", + "person-fill-x": "\f8a6", + "person-gear": "\f8a7", + "person-lock": "\f8a8", + "person-slash": "\f8a9", + "person-up": "\f8aa", + "scooter": "\f8ab", + "taxi-front-fill": "\f8ac", + "taxi-front": "\f8ad", + "amd": "\f8ae", + "database-add": "\f8af", + "database-check": "\f8b0", + "database-dash": "\f8b1", + "database-down": "\f8b2", + "database-exclamation": "\f8b3", + "database-fill-add": "\f8b4", + "database-fill-check": "\f8b5", + "database-fill-dash": "\f8b6", + "database-fill-down": "\f8b7", + "database-fill-exclamation": "\f8b8", + "database-fill-gear": "\f8b9", + "database-fill-lock": "\f8ba", + "database-fill-slash": "\f8bb", + "database-fill-up": "\f8bc", + "database-fill-x": "\f8bd", + "database-fill": "\f8be", + "database-gear": "\f8bf", + "database-lock": "\f8c0", + "database-slash": "\f8c1", + "database-up": "\f8c2", + "database-x": "\f8c3", + "database": "\f8c4", + "houses-fill": "\f8c5", + "houses": "\f8c6", + "nvidia": "\f8c7", + "person-vcard-fill": "\f8c8", + "person-vcard": "\f8c9", + "sina-weibo": "\f8ca", + "tencent-qq": "\f8cb", + "wikipedia": "\f8cc", + "alphabet-uppercase": "\f2a5", + "alphabet": "\f68a", + "amazon": "\f68d", + "arrows-collapse-vertical": "\f690", + "arrows-expand-vertical": "\f695", + "arrows-vertical": "\f698", + "arrows": "\f6a2", + "ban-fill": "\f6a3", + "ban": "\f6b6", + "bing": "\f6c2", + "cake": "\f6e0", + "cake2": "\f6ed", + "cookie": "\f6ee", + "copy": "\f759", + "crosshair": "\f769", + "crosshair2": "\f794", + "emoji-astonished-fill": "\f795", + "emoji-astonished": "\f79a", + "emoji-grimace-fill": "\f79b", + "emoji-grimace": "\f7a0", + "emoji-grin-fill": "\f7a1", + "emoji-grin": "\f7a6", + "emoji-surprise-fill": "\f7a7", + "emoji-surprise": "\f7ac", + "emoji-tear-fill": "\f7ad", + "emoji-tear": "\f7b2", + "envelope-arrow-down-fill": "\f7b3", + "envelope-arrow-down": "\f7b8", + "envelope-arrow-up-fill": "\f7b9", + "envelope-arrow-up": "\f7be", + "feather": "\f7bf", + "feather2": "\f7c4", + "floppy-fill": "\f7c5", + "floppy": "\f7d8", + "floppy2-fill": "\f7d9", + "floppy2": "\f7e4", + "gitlab": "\f7e5", + "highlighter": "\f7f8", + "marker-tip": "\f802", + "nvme-fill": "\f803", + "nvme": "\f80c", + "opencollective": "\f80d", + "pci-card-network": "\f8cd", + "pci-card-sound": "\f8ce", + "radar": "\f8cf", + "send-arrow-down-fill": "\f8d0", + "send-arrow-down": "\f8d1", + "send-arrow-up-fill": "\f8d2", + "send-arrow-up": "\f8d3", + "sim-slash-fill": "\f8d4", + "sim-slash": "\f8d5", + "sourceforge": "\f8d6", + "substack": "\f8d7", + "threads-fill": "\f8d8", + "threads": "\f8d9", + "transparency": "\f8da", + "twitter-x": "\f8db", + "type-h4": "\f8dc", + "type-h5": "\f8dd", + "type-h6": "\f8de", + "backpack-fill": "\f8df", + "backpack": "\f8e0", + "backpack2-fill": "\f8e1", + "backpack2": "\f8e2", + "backpack3-fill": "\f8e3", + "backpack3": "\f8e4", + "backpack4-fill": "\f8e5", + "backpack4": "\f8e6", + "brilliance": "\f8e7", + "cake-fill": "\f8e8", + "cake2-fill": "\f8e9", + "duffle-fill": "\f8ea", + "duffle": "\f8eb", + "exposure": "\f8ec", + "gender-neuter": "\f8ed", + "highlights": "\f8ee", + "luggage-fill": "\f8ef", + "luggage": "\f8f0", + "mailbox-flag": "\f8f1", + "mailbox2-flag": "\f8f2", + "noise-reduction": "\f8f3", + "passport-fill": "\f8f4", + "passport": "\f8f5", + "person-arms-up": "\f8f6", + "person-raised-hand": "\f8f7", + "person-standing-dress": "\f8f8", + "person-standing": "\f8f9", + "person-walking": "\f8fa", + "person-wheelchair": "\f8fb", + "shadows": "\f8fc", + "suitcase-fill": "\f8fd", + "suitcase-lg-fill": "\f8fe", + "suitcase-lg": "\f8ff", + "suitcase": "\f900", + "suitcase2-fill": "\f901", + "suitcase2": "\f902", + "vignette": "\f903", ); -.bi-123::before { content: map-get($bootstrap-icons-map, "123"); } -.bi-alarm-fill::before { content: map-get($bootstrap-icons-map, "alarm-fill"); } -.bi-alarm::before { content: map-get($bootstrap-icons-map, "alarm"); } -.bi-align-bottom::before { content: map-get($bootstrap-icons-map, "align-bottom"); } -.bi-align-center::before { content: map-get($bootstrap-icons-map, "align-center"); } -.bi-align-end::before { content: map-get($bootstrap-icons-map, "align-end"); } -.bi-align-middle::before { content: map-get($bootstrap-icons-map, "align-middle"); } -.bi-align-start::before { content: map-get($bootstrap-icons-map, "align-start"); } -.bi-align-top::before { content: map-get($bootstrap-icons-map, "align-top"); } -.bi-alt::before { content: map-get($bootstrap-icons-map, "alt"); } -.bi-app-indicator::before { content: map-get($bootstrap-icons-map, "app-indicator"); } -.bi-app::before { content: map-get($bootstrap-icons-map, "app"); } -.bi-archive-fill::before { content: map-get($bootstrap-icons-map, "archive-fill"); } -.bi-archive::before { content: map-get($bootstrap-icons-map, "archive"); } -.bi-arrow-90deg-down::before { content: map-get($bootstrap-icons-map, "arrow-90deg-down"); } -.bi-arrow-90deg-left::before { content: map-get($bootstrap-icons-map, "arrow-90deg-left"); } -.bi-arrow-90deg-right::before { content: map-get($bootstrap-icons-map, "arrow-90deg-right"); } -.bi-arrow-90deg-up::before { content: map-get($bootstrap-icons-map, "arrow-90deg-up"); } -.bi-arrow-bar-down::before { content: map-get($bootstrap-icons-map, "arrow-bar-down"); } -.bi-arrow-bar-left::before { content: map-get($bootstrap-icons-map, "arrow-bar-left"); } -.bi-arrow-bar-right::before { content: map-get($bootstrap-icons-map, "arrow-bar-right"); } -.bi-arrow-bar-up::before { content: map-get($bootstrap-icons-map, "arrow-bar-up"); } -.bi-arrow-clockwise::before { content: map-get($bootstrap-icons-map, "arrow-clockwise"); } -.bi-arrow-counterclockwise::before { content: map-get($bootstrap-icons-map, "arrow-counterclockwise"); } -.bi-arrow-down-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-down-circle-fill"); } -.bi-arrow-down-circle::before { content: map-get($bootstrap-icons-map, "arrow-down-circle"); } -.bi-arrow-down-left-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-down-left-circle-fill"); } -.bi-arrow-down-left-circle::before { content: map-get($bootstrap-icons-map, "arrow-down-left-circle"); } -.bi-arrow-down-left-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-down-left-square-fill"); } -.bi-arrow-down-left-square::before { content: map-get($bootstrap-icons-map, "arrow-down-left-square"); } -.bi-arrow-down-left::before { content: map-get($bootstrap-icons-map, "arrow-down-left"); } -.bi-arrow-down-right-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-down-right-circle-fill"); } -.bi-arrow-down-right-circle::before { content: map-get($bootstrap-icons-map, "arrow-down-right-circle"); } -.bi-arrow-down-right-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-down-right-square-fill"); } -.bi-arrow-down-right-square::before { content: map-get($bootstrap-icons-map, "arrow-down-right-square"); } -.bi-arrow-down-right::before { content: map-get($bootstrap-icons-map, "arrow-down-right"); } -.bi-arrow-down-short::before { content: map-get($bootstrap-icons-map, "arrow-down-short"); } -.bi-arrow-down-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-down-square-fill"); } -.bi-arrow-down-square::before { content: map-get($bootstrap-icons-map, "arrow-down-square"); } -.bi-arrow-down-up::before { content: map-get($bootstrap-icons-map, "arrow-down-up"); } -.bi-arrow-down::before { content: map-get($bootstrap-icons-map, "arrow-down"); } -.bi-arrow-left-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-left-circle-fill"); } -.bi-arrow-left-circle::before { content: map-get($bootstrap-icons-map, "arrow-left-circle"); } -.bi-arrow-left-right::before { content: map-get($bootstrap-icons-map, "arrow-left-right"); } -.bi-arrow-left-short::before { content: map-get($bootstrap-icons-map, "arrow-left-short"); } -.bi-arrow-left-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-left-square-fill"); } -.bi-arrow-left-square::before { content: map-get($bootstrap-icons-map, "arrow-left-square"); } -.bi-arrow-left::before { content: map-get($bootstrap-icons-map, "arrow-left"); } -.bi-arrow-repeat::before { content: map-get($bootstrap-icons-map, "arrow-repeat"); } -.bi-arrow-return-left::before { content: map-get($bootstrap-icons-map, "arrow-return-left"); } -.bi-arrow-return-right::before { content: map-get($bootstrap-icons-map, "arrow-return-right"); } -.bi-arrow-right-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-right-circle-fill"); } -.bi-arrow-right-circle::before { content: map-get($bootstrap-icons-map, "arrow-right-circle"); } -.bi-arrow-right-short::before { content: map-get($bootstrap-icons-map, "arrow-right-short"); } -.bi-arrow-right-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-right-square-fill"); } -.bi-arrow-right-square::before { content: map-get($bootstrap-icons-map, "arrow-right-square"); } -.bi-arrow-right::before { content: map-get($bootstrap-icons-map, "arrow-right"); } -.bi-arrow-up-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-up-circle-fill"); } -.bi-arrow-up-circle::before { content: map-get($bootstrap-icons-map, "arrow-up-circle"); } -.bi-arrow-up-left-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-up-left-circle-fill"); } -.bi-arrow-up-left-circle::before { content: map-get($bootstrap-icons-map, "arrow-up-left-circle"); } -.bi-arrow-up-left-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-up-left-square-fill"); } -.bi-arrow-up-left-square::before { content: map-get($bootstrap-icons-map, "arrow-up-left-square"); } -.bi-arrow-up-left::before { content: map-get($bootstrap-icons-map, "arrow-up-left"); } -.bi-arrow-up-right-circle-fill::before { content: map-get($bootstrap-icons-map, "arrow-up-right-circle-fill"); } -.bi-arrow-up-right-circle::before { content: map-get($bootstrap-icons-map, "arrow-up-right-circle"); } -.bi-arrow-up-right-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-up-right-square-fill"); } -.bi-arrow-up-right-square::before { content: map-get($bootstrap-icons-map, "arrow-up-right-square"); } -.bi-arrow-up-right::before { content: map-get($bootstrap-icons-map, "arrow-up-right"); } -.bi-arrow-up-short::before { content: map-get($bootstrap-icons-map, "arrow-up-short"); } -.bi-arrow-up-square-fill::before { content: map-get($bootstrap-icons-map, "arrow-up-square-fill"); } -.bi-arrow-up-square::before { content: map-get($bootstrap-icons-map, "arrow-up-square"); } -.bi-arrow-up::before { content: map-get($bootstrap-icons-map, "arrow-up"); } -.bi-arrows-angle-contract::before { content: map-get($bootstrap-icons-map, "arrows-angle-contract"); } -.bi-arrows-angle-expand::before { content: map-get($bootstrap-icons-map, "arrows-angle-expand"); } -.bi-arrows-collapse::before { content: map-get($bootstrap-icons-map, "arrows-collapse"); } -.bi-arrows-expand::before { content: map-get($bootstrap-icons-map, "arrows-expand"); } -.bi-arrows-fullscreen::before { content: map-get($bootstrap-icons-map, "arrows-fullscreen"); } -.bi-arrows-move::before { content: map-get($bootstrap-icons-map, "arrows-move"); } -.bi-aspect-ratio-fill::before { content: map-get($bootstrap-icons-map, "aspect-ratio-fill"); } -.bi-aspect-ratio::before { content: map-get($bootstrap-icons-map, "aspect-ratio"); } -.bi-asterisk::before { content: map-get($bootstrap-icons-map, "asterisk"); } -.bi-at::before { content: map-get($bootstrap-icons-map, "at"); } -.bi-award-fill::before { content: map-get($bootstrap-icons-map, "award-fill"); } -.bi-award::before { content: map-get($bootstrap-icons-map, "award"); } -.bi-back::before { content: map-get($bootstrap-icons-map, "back"); } -.bi-backspace-fill::before { content: map-get($bootstrap-icons-map, "backspace-fill"); } -.bi-backspace-reverse-fill::before { content: map-get($bootstrap-icons-map, "backspace-reverse-fill"); } -.bi-backspace-reverse::before { content: map-get($bootstrap-icons-map, "backspace-reverse"); } -.bi-backspace::before { content: map-get($bootstrap-icons-map, "backspace"); } -.bi-badge-3d-fill::before { content: map-get($bootstrap-icons-map, "badge-3d-fill"); } -.bi-badge-3d::before { content: map-get($bootstrap-icons-map, "badge-3d"); } -.bi-badge-4k-fill::before { content: map-get($bootstrap-icons-map, "badge-4k-fill"); } -.bi-badge-4k::before { content: map-get($bootstrap-icons-map, "badge-4k"); } -.bi-badge-8k-fill::before { content: map-get($bootstrap-icons-map, "badge-8k-fill"); } -.bi-badge-8k::before { content: map-get($bootstrap-icons-map, "badge-8k"); } -.bi-badge-ad-fill::before { content: map-get($bootstrap-icons-map, "badge-ad-fill"); } -.bi-badge-ad::before { content: map-get($bootstrap-icons-map, "badge-ad"); } -.bi-badge-ar-fill::before { content: map-get($bootstrap-icons-map, "badge-ar-fill"); } -.bi-badge-ar::before { content: map-get($bootstrap-icons-map, "badge-ar"); } -.bi-badge-cc-fill::before { content: map-get($bootstrap-icons-map, "badge-cc-fill"); } -.bi-badge-cc::before { content: map-get($bootstrap-icons-map, "badge-cc"); } -.bi-badge-hd-fill::before { content: map-get($bootstrap-icons-map, "badge-hd-fill"); } -.bi-badge-hd::before { content: map-get($bootstrap-icons-map, "badge-hd"); } -.bi-badge-tm-fill::before { content: map-get($bootstrap-icons-map, "badge-tm-fill"); } -.bi-badge-tm::before { content: map-get($bootstrap-icons-map, "badge-tm"); } -.bi-badge-vo-fill::before { content: map-get($bootstrap-icons-map, "badge-vo-fill"); } -.bi-badge-vo::before { content: map-get($bootstrap-icons-map, "badge-vo"); } -.bi-badge-vr-fill::before { content: map-get($bootstrap-icons-map, "badge-vr-fill"); } -.bi-badge-vr::before { content: map-get($bootstrap-icons-map, "badge-vr"); } -.bi-badge-wc-fill::before { content: map-get($bootstrap-icons-map, "badge-wc-fill"); } -.bi-badge-wc::before { content: map-get($bootstrap-icons-map, "badge-wc"); } -.bi-bag-check-fill::before { content: map-get($bootstrap-icons-map, "bag-check-fill"); } -.bi-bag-check::before { content: map-get($bootstrap-icons-map, "bag-check"); } -.bi-bag-dash-fill::before { content: map-get($bootstrap-icons-map, "bag-dash-fill"); } -.bi-bag-dash::before { content: map-get($bootstrap-icons-map, "bag-dash"); } -.bi-bag-fill::before { content: map-get($bootstrap-icons-map, "bag-fill"); } -.bi-bag-plus-fill::before { content: map-get($bootstrap-icons-map, "bag-plus-fill"); } -.bi-bag-plus::before { content: map-get($bootstrap-icons-map, "bag-plus"); } -.bi-bag-x-fill::before { content: map-get($bootstrap-icons-map, "bag-x-fill"); } -.bi-bag-x::before { content: map-get($bootstrap-icons-map, "bag-x"); } -.bi-bag::before { content: map-get($bootstrap-icons-map, "bag"); } -.bi-bar-chart-fill::before { content: map-get($bootstrap-icons-map, "bar-chart-fill"); } -.bi-bar-chart-line-fill::before { content: map-get($bootstrap-icons-map, "bar-chart-line-fill"); } -.bi-bar-chart-line::before { content: map-get($bootstrap-icons-map, "bar-chart-line"); } -.bi-bar-chart-steps::before { content: map-get($bootstrap-icons-map, "bar-chart-steps"); } -.bi-bar-chart::before { content: map-get($bootstrap-icons-map, "bar-chart"); } -.bi-basket-fill::before { content: map-get($bootstrap-icons-map, "basket-fill"); } -.bi-basket::before { content: map-get($bootstrap-icons-map, "basket"); } -.bi-basket2-fill::before { content: map-get($bootstrap-icons-map, "basket2-fill"); } -.bi-basket2::before { content: map-get($bootstrap-icons-map, "basket2"); } -.bi-basket3-fill::before { content: map-get($bootstrap-icons-map, "basket3-fill"); } -.bi-basket3::before { content: map-get($bootstrap-icons-map, "basket3"); } -.bi-battery-charging::before { content: map-get($bootstrap-icons-map, "battery-charging"); } -.bi-battery-full::before { content: map-get($bootstrap-icons-map, "battery-full"); } -.bi-battery-half::before { content: map-get($bootstrap-icons-map, "battery-half"); } -.bi-battery::before { content: map-get($bootstrap-icons-map, "battery"); } -.bi-bell-fill::before { content: map-get($bootstrap-icons-map, "bell-fill"); } -.bi-bell::before { content: map-get($bootstrap-icons-map, "bell"); } -.bi-bezier::before { content: map-get($bootstrap-icons-map, "bezier"); } -.bi-bezier2::before { content: map-get($bootstrap-icons-map, "bezier2"); } -.bi-bicycle::before { content: map-get($bootstrap-icons-map, "bicycle"); } -.bi-binoculars-fill::before { content: map-get($bootstrap-icons-map, "binoculars-fill"); } -.bi-binoculars::before { content: map-get($bootstrap-icons-map, "binoculars"); } -.bi-blockquote-left::before { content: map-get($bootstrap-icons-map, "blockquote-left"); } -.bi-blockquote-right::before { content: map-get($bootstrap-icons-map, "blockquote-right"); } -.bi-book-fill::before { content: map-get($bootstrap-icons-map, "book-fill"); } -.bi-book-half::before { content: map-get($bootstrap-icons-map, "book-half"); } -.bi-book::before { content: map-get($bootstrap-icons-map, "book"); } -.bi-bookmark-check-fill::before { content: map-get($bootstrap-icons-map, "bookmark-check-fill"); } -.bi-bookmark-check::before { content: map-get($bootstrap-icons-map, "bookmark-check"); } -.bi-bookmark-dash-fill::before { content: map-get($bootstrap-icons-map, "bookmark-dash-fill"); } -.bi-bookmark-dash::before { content: map-get($bootstrap-icons-map, "bookmark-dash"); } -.bi-bookmark-fill::before { content: map-get($bootstrap-icons-map, "bookmark-fill"); } -.bi-bookmark-heart-fill::before { content: map-get($bootstrap-icons-map, "bookmark-heart-fill"); } -.bi-bookmark-heart::before { content: map-get($bootstrap-icons-map, "bookmark-heart"); } -.bi-bookmark-plus-fill::before { content: map-get($bootstrap-icons-map, "bookmark-plus-fill"); } -.bi-bookmark-plus::before { content: map-get($bootstrap-icons-map, "bookmark-plus"); } -.bi-bookmark-star-fill::before { content: map-get($bootstrap-icons-map, "bookmark-star-fill"); } -.bi-bookmark-star::before { content: map-get($bootstrap-icons-map, "bookmark-star"); } -.bi-bookmark-x-fill::before { content: map-get($bootstrap-icons-map, "bookmark-x-fill"); } -.bi-bookmark-x::before { content: map-get($bootstrap-icons-map, "bookmark-x"); } -.bi-bookmark::before { content: map-get($bootstrap-icons-map, "bookmark"); } -.bi-bookmarks-fill::before { content: map-get($bootstrap-icons-map, "bookmarks-fill"); } -.bi-bookmarks::before { content: map-get($bootstrap-icons-map, "bookmarks"); } -.bi-bookshelf::before { content: map-get($bootstrap-icons-map, "bookshelf"); } -.bi-bootstrap-fill::before { content: map-get($bootstrap-icons-map, "bootstrap-fill"); } -.bi-bootstrap-reboot::before { content: map-get($bootstrap-icons-map, "bootstrap-reboot"); } -.bi-bootstrap::before { content: map-get($bootstrap-icons-map, "bootstrap"); } -.bi-border-all::before { content: map-get($bootstrap-icons-map, "border-all"); } -.bi-border-bottom::before { content: map-get($bootstrap-icons-map, "border-bottom"); } -.bi-border-center::before { content: map-get($bootstrap-icons-map, "border-center"); } -.bi-border-inner::before { content: map-get($bootstrap-icons-map, "border-inner"); } -.bi-border-left::before { content: map-get($bootstrap-icons-map, "border-left"); } -.bi-border-middle::before { content: map-get($bootstrap-icons-map, "border-middle"); } -.bi-border-outer::before { content: map-get($bootstrap-icons-map, "border-outer"); } -.bi-border-right::before { content: map-get($bootstrap-icons-map, "border-right"); } -.bi-border-style::before { content: map-get($bootstrap-icons-map, "border-style"); } -.bi-border-top::before { content: map-get($bootstrap-icons-map, "border-top"); } -.bi-border-width::before { content: map-get($bootstrap-icons-map, "border-width"); } -.bi-border::before { content: map-get($bootstrap-icons-map, "border"); } -.bi-bounding-box-circles::before { content: map-get($bootstrap-icons-map, "bounding-box-circles"); } -.bi-bounding-box::before { content: map-get($bootstrap-icons-map, "bounding-box"); } -.bi-box-arrow-down-left::before { content: map-get($bootstrap-icons-map, "box-arrow-down-left"); } -.bi-box-arrow-down-right::before { content: map-get($bootstrap-icons-map, "box-arrow-down-right"); } -.bi-box-arrow-down::before { content: map-get($bootstrap-icons-map, "box-arrow-down"); } -.bi-box-arrow-in-down-left::before { content: map-get($bootstrap-icons-map, "box-arrow-in-down-left"); } -.bi-box-arrow-in-down-right::before { content: map-get($bootstrap-icons-map, "box-arrow-in-down-right"); } -.bi-box-arrow-in-down::before { content: map-get($bootstrap-icons-map, "box-arrow-in-down"); } -.bi-box-arrow-in-left::before { content: map-get($bootstrap-icons-map, "box-arrow-in-left"); } -.bi-box-arrow-in-right::before { content: map-get($bootstrap-icons-map, "box-arrow-in-right"); } -.bi-box-arrow-in-up-left::before { content: map-get($bootstrap-icons-map, "box-arrow-in-up-left"); } -.bi-box-arrow-in-up-right::before { content: map-get($bootstrap-icons-map, "box-arrow-in-up-right"); } -.bi-box-arrow-in-up::before { content: map-get($bootstrap-icons-map, "box-arrow-in-up"); } -.bi-box-arrow-left::before { content: map-get($bootstrap-icons-map, "box-arrow-left"); } -.bi-box-arrow-right::before { content: map-get($bootstrap-icons-map, "box-arrow-right"); } -.bi-box-arrow-up-left::before { content: map-get($bootstrap-icons-map, "box-arrow-up-left"); } -.bi-box-arrow-up-right::before { content: map-get($bootstrap-icons-map, "box-arrow-up-right"); } -.bi-box-arrow-up::before { content: map-get($bootstrap-icons-map, "box-arrow-up"); } -.bi-box-seam::before { content: map-get($bootstrap-icons-map, "box-seam"); } -.bi-box::before { content: map-get($bootstrap-icons-map, "box"); } -.bi-braces::before { content: map-get($bootstrap-icons-map, "braces"); } -.bi-bricks::before { content: map-get($bootstrap-icons-map, "bricks"); } -.bi-briefcase-fill::before { content: map-get($bootstrap-icons-map, "briefcase-fill"); } -.bi-briefcase::before { content: map-get($bootstrap-icons-map, "briefcase"); } -.bi-brightness-alt-high-fill::before { content: map-get($bootstrap-icons-map, "brightness-alt-high-fill"); } -.bi-brightness-alt-high::before { content: map-get($bootstrap-icons-map, "brightness-alt-high"); } -.bi-brightness-alt-low-fill::before { content: map-get($bootstrap-icons-map, "brightness-alt-low-fill"); } -.bi-brightness-alt-low::before { content: map-get($bootstrap-icons-map, "brightness-alt-low"); } -.bi-brightness-high-fill::before { content: map-get($bootstrap-icons-map, "brightness-high-fill"); } -.bi-brightness-high::before { content: map-get($bootstrap-icons-map, "brightness-high"); } -.bi-brightness-low-fill::before { content: map-get($bootstrap-icons-map, "brightness-low-fill"); } -.bi-brightness-low::before { content: map-get($bootstrap-icons-map, "brightness-low"); } -.bi-broadcast-pin::before { content: map-get($bootstrap-icons-map, "broadcast-pin"); } -.bi-broadcast::before { content: map-get($bootstrap-icons-map, "broadcast"); } -.bi-brush-fill::before { content: map-get($bootstrap-icons-map, "brush-fill"); } -.bi-brush::before { content: map-get($bootstrap-icons-map, "brush"); } -.bi-bucket-fill::before { content: map-get($bootstrap-icons-map, "bucket-fill"); } -.bi-bucket::before { content: map-get($bootstrap-icons-map, "bucket"); } -.bi-bug-fill::before { content: map-get($bootstrap-icons-map, "bug-fill"); } -.bi-bug::before { content: map-get($bootstrap-icons-map, "bug"); } -.bi-building::before { content: map-get($bootstrap-icons-map, "building"); } -.bi-bullseye::before { content: map-get($bootstrap-icons-map, "bullseye"); } -.bi-calculator-fill::before { content: map-get($bootstrap-icons-map, "calculator-fill"); } -.bi-calculator::before { content: map-get($bootstrap-icons-map, "calculator"); } -.bi-calendar-check-fill::before { content: map-get($bootstrap-icons-map, "calendar-check-fill"); } -.bi-calendar-check::before { content: map-get($bootstrap-icons-map, "calendar-check"); } -.bi-calendar-date-fill::before { content: map-get($bootstrap-icons-map, "calendar-date-fill"); } -.bi-calendar-date::before { content: map-get($bootstrap-icons-map, "calendar-date"); } -.bi-calendar-day-fill::before { content: map-get($bootstrap-icons-map, "calendar-day-fill"); } -.bi-calendar-day::before { content: map-get($bootstrap-icons-map, "calendar-day"); } -.bi-calendar-event-fill::before { content: map-get($bootstrap-icons-map, "calendar-event-fill"); } -.bi-calendar-event::before { content: map-get($bootstrap-icons-map, "calendar-event"); } -.bi-calendar-fill::before { content: map-get($bootstrap-icons-map, "calendar-fill"); } -.bi-calendar-minus-fill::before { content: map-get($bootstrap-icons-map, "calendar-minus-fill"); } -.bi-calendar-minus::before { content: map-get($bootstrap-icons-map, "calendar-minus"); } -.bi-calendar-month-fill::before { content: map-get($bootstrap-icons-map, "calendar-month-fill"); } -.bi-calendar-month::before { content: map-get($bootstrap-icons-map, "calendar-month"); } -.bi-calendar-plus-fill::before { content: map-get($bootstrap-icons-map, "calendar-plus-fill"); } -.bi-calendar-plus::before { content: map-get($bootstrap-icons-map, "calendar-plus"); } -.bi-calendar-range-fill::before { content: map-get($bootstrap-icons-map, "calendar-range-fill"); } -.bi-calendar-range::before { content: map-get($bootstrap-icons-map, "calendar-range"); } -.bi-calendar-week-fill::before { content: map-get($bootstrap-icons-map, "calendar-week-fill"); } -.bi-calendar-week::before { content: map-get($bootstrap-icons-map, "calendar-week"); } -.bi-calendar-x-fill::before { content: map-get($bootstrap-icons-map, "calendar-x-fill"); } -.bi-calendar-x::before { content: map-get($bootstrap-icons-map, "calendar-x"); } -.bi-calendar::before { content: map-get($bootstrap-icons-map, "calendar"); } -.bi-calendar2-check-fill::before { content: map-get($bootstrap-icons-map, "calendar2-check-fill"); } -.bi-calendar2-check::before { content: map-get($bootstrap-icons-map, "calendar2-check"); } -.bi-calendar2-date-fill::before { content: map-get($bootstrap-icons-map, "calendar2-date-fill"); } -.bi-calendar2-date::before { content: map-get($bootstrap-icons-map, "calendar2-date"); } -.bi-calendar2-day-fill::before { content: map-get($bootstrap-icons-map, "calendar2-day-fill"); } -.bi-calendar2-day::before { content: map-get($bootstrap-icons-map, "calendar2-day"); } -.bi-calendar2-event-fill::before { content: map-get($bootstrap-icons-map, "calendar2-event-fill"); } -.bi-calendar2-event::before { content: map-get($bootstrap-icons-map, "calendar2-event"); } -.bi-calendar2-fill::before { content: map-get($bootstrap-icons-map, "calendar2-fill"); } -.bi-calendar2-minus-fill::before { content: map-get($bootstrap-icons-map, "calendar2-minus-fill"); } -.bi-calendar2-minus::before { content: map-get($bootstrap-icons-map, "calendar2-minus"); } -.bi-calendar2-month-fill::before { content: map-get($bootstrap-icons-map, "calendar2-month-fill"); } -.bi-calendar2-month::before { content: map-get($bootstrap-icons-map, "calendar2-month"); } -.bi-calendar2-plus-fill::before { content: map-get($bootstrap-icons-map, "calendar2-plus-fill"); } -.bi-calendar2-plus::before { content: map-get($bootstrap-icons-map, "calendar2-plus"); } -.bi-calendar2-range-fill::before { content: map-get($bootstrap-icons-map, "calendar2-range-fill"); } -.bi-calendar2-range::before { content: map-get($bootstrap-icons-map, "calendar2-range"); } -.bi-calendar2-week-fill::before { content: map-get($bootstrap-icons-map, "calendar2-week-fill"); } -.bi-calendar2-week::before { content: map-get($bootstrap-icons-map, "calendar2-week"); } -.bi-calendar2-x-fill::before { content: map-get($bootstrap-icons-map, "calendar2-x-fill"); } -.bi-calendar2-x::before { content: map-get($bootstrap-icons-map, "calendar2-x"); } -.bi-calendar2::before { content: map-get($bootstrap-icons-map, "calendar2"); } -.bi-calendar3-event-fill::before { content: map-get($bootstrap-icons-map, "calendar3-event-fill"); } -.bi-calendar3-event::before { content: map-get($bootstrap-icons-map, "calendar3-event"); } -.bi-calendar3-fill::before { content: map-get($bootstrap-icons-map, "calendar3-fill"); } -.bi-calendar3-range-fill::before { content: map-get($bootstrap-icons-map, "calendar3-range-fill"); } -.bi-calendar3-range::before { content: map-get($bootstrap-icons-map, "calendar3-range"); } -.bi-calendar3-week-fill::before { content: map-get($bootstrap-icons-map, "calendar3-week-fill"); } -.bi-calendar3-week::before { content: map-get($bootstrap-icons-map, "calendar3-week"); } -.bi-calendar3::before { content: map-get($bootstrap-icons-map, "calendar3"); } -.bi-calendar4-event::before { content: map-get($bootstrap-icons-map, "calendar4-event"); } -.bi-calendar4-range::before { content: map-get($bootstrap-icons-map, "calendar4-range"); } -.bi-calendar4-week::before { content: map-get($bootstrap-icons-map, "calendar4-week"); } -.bi-calendar4::before { content: map-get($bootstrap-icons-map, "calendar4"); } -.bi-camera-fill::before { content: map-get($bootstrap-icons-map, "camera-fill"); } -.bi-camera-reels-fill::before { content: map-get($bootstrap-icons-map, "camera-reels-fill"); } -.bi-camera-reels::before { content: map-get($bootstrap-icons-map, "camera-reels"); } -.bi-camera-video-fill::before { content: map-get($bootstrap-icons-map, "camera-video-fill"); } -.bi-camera-video-off-fill::before { content: map-get($bootstrap-icons-map, "camera-video-off-fill"); } -.bi-camera-video-off::before { content: map-get($bootstrap-icons-map, "camera-video-off"); } -.bi-camera-video::before { content: map-get($bootstrap-icons-map, "camera-video"); } -.bi-camera::before { content: map-get($bootstrap-icons-map, "camera"); } -.bi-camera2::before { content: map-get($bootstrap-icons-map, "camera2"); } -.bi-capslock-fill::before { content: map-get($bootstrap-icons-map, "capslock-fill"); } -.bi-capslock::before { content: map-get($bootstrap-icons-map, "capslock"); } -.bi-card-checklist::before { content: map-get($bootstrap-icons-map, "card-checklist"); } -.bi-card-heading::before { content: map-get($bootstrap-icons-map, "card-heading"); } -.bi-card-image::before { content: map-get($bootstrap-icons-map, "card-image"); } -.bi-card-list::before { content: map-get($bootstrap-icons-map, "card-list"); } -.bi-card-text::before { content: map-get($bootstrap-icons-map, "card-text"); } -.bi-caret-down-fill::before { content: map-get($bootstrap-icons-map, "caret-down-fill"); } -.bi-caret-down-square-fill::before { content: map-get($bootstrap-icons-map, "caret-down-square-fill"); } -.bi-caret-down-square::before { content: map-get($bootstrap-icons-map, "caret-down-square"); } -.bi-caret-down::before { content: map-get($bootstrap-icons-map, "caret-down"); } -.bi-caret-left-fill::before { content: map-get($bootstrap-icons-map, "caret-left-fill"); } -.bi-caret-left-square-fill::before { content: map-get($bootstrap-icons-map, "caret-left-square-fill"); } -.bi-caret-left-square::before { content: map-get($bootstrap-icons-map, "caret-left-square"); } -.bi-caret-left::before { content: map-get($bootstrap-icons-map, "caret-left"); } -.bi-caret-right-fill::before { content: map-get($bootstrap-icons-map, "caret-right-fill"); } -.bi-caret-right-square-fill::before { content: map-get($bootstrap-icons-map, "caret-right-square-fill"); } -.bi-caret-right-square::before { content: map-get($bootstrap-icons-map, "caret-right-square"); } -.bi-caret-right::before { content: map-get($bootstrap-icons-map, "caret-right"); } -.bi-caret-up-fill::before { content: map-get($bootstrap-icons-map, "caret-up-fill"); } -.bi-caret-up-square-fill::before { content: map-get($bootstrap-icons-map, "caret-up-square-fill"); } -.bi-caret-up-square::before { content: map-get($bootstrap-icons-map, "caret-up-square"); } -.bi-caret-up::before { content: map-get($bootstrap-icons-map, "caret-up"); } -.bi-cart-check-fill::before { content: map-get($bootstrap-icons-map, "cart-check-fill"); } -.bi-cart-check::before { content: map-get($bootstrap-icons-map, "cart-check"); } -.bi-cart-dash-fill::before { content: map-get($bootstrap-icons-map, "cart-dash-fill"); } -.bi-cart-dash::before { content: map-get($bootstrap-icons-map, "cart-dash"); } -.bi-cart-fill::before { content: map-get($bootstrap-icons-map, "cart-fill"); } -.bi-cart-plus-fill::before { content: map-get($bootstrap-icons-map, "cart-plus-fill"); } -.bi-cart-plus::before { content: map-get($bootstrap-icons-map, "cart-plus"); } -.bi-cart-x-fill::before { content: map-get($bootstrap-icons-map, "cart-x-fill"); } -.bi-cart-x::before { content: map-get($bootstrap-icons-map, "cart-x"); } -.bi-cart::before { content: map-get($bootstrap-icons-map, "cart"); } -.bi-cart2::before { content: map-get($bootstrap-icons-map, "cart2"); } -.bi-cart3::before { content: map-get($bootstrap-icons-map, "cart3"); } -.bi-cart4::before { content: map-get($bootstrap-icons-map, "cart4"); } -.bi-cash-stack::before { content: map-get($bootstrap-icons-map, "cash-stack"); } -.bi-cash::before { content: map-get($bootstrap-icons-map, "cash"); } -.bi-cast::before { content: map-get($bootstrap-icons-map, "cast"); } -.bi-chat-dots-fill::before { content: map-get($bootstrap-icons-map, "chat-dots-fill"); } -.bi-chat-dots::before { content: map-get($bootstrap-icons-map, "chat-dots"); } -.bi-chat-fill::before { content: map-get($bootstrap-icons-map, "chat-fill"); } -.bi-chat-left-dots-fill::before { content: map-get($bootstrap-icons-map, "chat-left-dots-fill"); } -.bi-chat-left-dots::before { content: map-get($bootstrap-icons-map, "chat-left-dots"); } -.bi-chat-left-fill::before { content: map-get($bootstrap-icons-map, "chat-left-fill"); } -.bi-chat-left-quote-fill::before { content: map-get($bootstrap-icons-map, "chat-left-quote-fill"); } -.bi-chat-left-quote::before { content: map-get($bootstrap-icons-map, "chat-left-quote"); } -.bi-chat-left-text-fill::before { content: map-get($bootstrap-icons-map, "chat-left-text-fill"); } -.bi-chat-left-text::before { content: map-get($bootstrap-icons-map, "chat-left-text"); } -.bi-chat-left::before { content: map-get($bootstrap-icons-map, "chat-left"); } -.bi-chat-quote-fill::before { content: map-get($bootstrap-icons-map, "chat-quote-fill"); } -.bi-chat-quote::before { content: map-get($bootstrap-icons-map, "chat-quote"); } -.bi-chat-right-dots-fill::before { content: map-get($bootstrap-icons-map, "chat-right-dots-fill"); } -.bi-chat-right-dots::before { content: map-get($bootstrap-icons-map, "chat-right-dots"); } -.bi-chat-right-fill::before { content: map-get($bootstrap-icons-map, "chat-right-fill"); } -.bi-chat-right-quote-fill::before { content: map-get($bootstrap-icons-map, "chat-right-quote-fill"); } -.bi-chat-right-quote::before { content: map-get($bootstrap-icons-map, "chat-right-quote"); } -.bi-chat-right-text-fill::before { content: map-get($bootstrap-icons-map, "chat-right-text-fill"); } -.bi-chat-right-text::before { content: map-get($bootstrap-icons-map, "chat-right-text"); } -.bi-chat-right::before { content: map-get($bootstrap-icons-map, "chat-right"); } -.bi-chat-square-dots-fill::before { content: map-get($bootstrap-icons-map, "chat-square-dots-fill"); } -.bi-chat-square-dots::before { content: map-get($bootstrap-icons-map, "chat-square-dots"); } -.bi-chat-square-fill::before { content: map-get($bootstrap-icons-map, "chat-square-fill"); } -.bi-chat-square-quote-fill::before { content: map-get($bootstrap-icons-map, "chat-square-quote-fill"); } -.bi-chat-square-quote::before { content: map-get($bootstrap-icons-map, "chat-square-quote"); } -.bi-chat-square-text-fill::before { content: map-get($bootstrap-icons-map, "chat-square-text-fill"); } -.bi-chat-square-text::before { content: map-get($bootstrap-icons-map, "chat-square-text"); } -.bi-chat-square::before { content: map-get($bootstrap-icons-map, "chat-square"); } -.bi-chat-text-fill::before { content: map-get($bootstrap-icons-map, "chat-text-fill"); } -.bi-chat-text::before { content: map-get($bootstrap-icons-map, "chat-text"); } -.bi-chat::before { content: map-get($bootstrap-icons-map, "chat"); } -.bi-check-all::before { content: map-get($bootstrap-icons-map, "check-all"); } -.bi-check-circle-fill::before { content: map-get($bootstrap-icons-map, "check-circle-fill"); } -.bi-check-circle::before { content: map-get($bootstrap-icons-map, "check-circle"); } -.bi-check-square-fill::before { content: map-get($bootstrap-icons-map, "check-square-fill"); } -.bi-check-square::before { content: map-get($bootstrap-icons-map, "check-square"); } -.bi-check::before { content: map-get($bootstrap-icons-map, "check"); } -.bi-check2-all::before { content: map-get($bootstrap-icons-map, "check2-all"); } -.bi-check2-circle::before { content: map-get($bootstrap-icons-map, "check2-circle"); } -.bi-check2-square::before { content: map-get($bootstrap-icons-map, "check2-square"); } -.bi-check2::before { content: map-get($bootstrap-icons-map, "check2"); } -.bi-chevron-bar-contract::before { content: map-get($bootstrap-icons-map, "chevron-bar-contract"); } -.bi-chevron-bar-down::before { content: map-get($bootstrap-icons-map, "chevron-bar-down"); } -.bi-chevron-bar-expand::before { content: map-get($bootstrap-icons-map, "chevron-bar-expand"); } -.bi-chevron-bar-left::before { content: map-get($bootstrap-icons-map, "chevron-bar-left"); } -.bi-chevron-bar-right::before { content: map-get($bootstrap-icons-map, "chevron-bar-right"); } -.bi-chevron-bar-up::before { content: map-get($bootstrap-icons-map, "chevron-bar-up"); } -.bi-chevron-compact-down::before { content: map-get($bootstrap-icons-map, "chevron-compact-down"); } -.bi-chevron-compact-left::before { content: map-get($bootstrap-icons-map, "chevron-compact-left"); } -.bi-chevron-compact-right::before { content: map-get($bootstrap-icons-map, "chevron-compact-right"); } -.bi-chevron-compact-up::before { content: map-get($bootstrap-icons-map, "chevron-compact-up"); } -.bi-chevron-contract::before { content: map-get($bootstrap-icons-map, "chevron-contract"); } -.bi-chevron-double-down::before { content: map-get($bootstrap-icons-map, "chevron-double-down"); } -.bi-chevron-double-left::before { content: map-get($bootstrap-icons-map, "chevron-double-left"); } -.bi-chevron-double-right::before { content: map-get($bootstrap-icons-map, "chevron-double-right"); } -.bi-chevron-double-up::before { content: map-get($bootstrap-icons-map, "chevron-double-up"); } -.bi-chevron-down::before { content: map-get($bootstrap-icons-map, "chevron-down"); } -.bi-chevron-expand::before { content: map-get($bootstrap-icons-map, "chevron-expand"); } -.bi-chevron-left::before { content: map-get($bootstrap-icons-map, "chevron-left"); } -.bi-chevron-right::before { content: map-get($bootstrap-icons-map, "chevron-right"); } -.bi-chevron-up::before { content: map-get($bootstrap-icons-map, "chevron-up"); } -.bi-circle-fill::before { content: map-get($bootstrap-icons-map, "circle-fill"); } -.bi-circle-half::before { content: map-get($bootstrap-icons-map, "circle-half"); } -.bi-circle-square::before { content: map-get($bootstrap-icons-map, "circle-square"); } -.bi-circle::before { content: map-get($bootstrap-icons-map, "circle"); } -.bi-clipboard-check::before { content: map-get($bootstrap-icons-map, "clipboard-check"); } -.bi-clipboard-data::before { content: map-get($bootstrap-icons-map, "clipboard-data"); } -.bi-clipboard-minus::before { content: map-get($bootstrap-icons-map, "clipboard-minus"); } -.bi-clipboard-plus::before { content: map-get($bootstrap-icons-map, "clipboard-plus"); } -.bi-clipboard-x::before { content: map-get($bootstrap-icons-map, "clipboard-x"); } -.bi-clipboard::before { content: map-get($bootstrap-icons-map, "clipboard"); } -.bi-clock-fill::before { content: map-get($bootstrap-icons-map, "clock-fill"); } -.bi-clock-history::before { content: map-get($bootstrap-icons-map, "clock-history"); } -.bi-clock::before { content: map-get($bootstrap-icons-map, "clock"); } -.bi-cloud-arrow-down-fill::before { content: map-get($bootstrap-icons-map, "cloud-arrow-down-fill"); } -.bi-cloud-arrow-down::before { content: map-get($bootstrap-icons-map, "cloud-arrow-down"); } -.bi-cloud-arrow-up-fill::before { content: map-get($bootstrap-icons-map, "cloud-arrow-up-fill"); } -.bi-cloud-arrow-up::before { content: map-get($bootstrap-icons-map, "cloud-arrow-up"); } -.bi-cloud-check-fill::before { content: map-get($bootstrap-icons-map, "cloud-check-fill"); } -.bi-cloud-check::before { content: map-get($bootstrap-icons-map, "cloud-check"); } -.bi-cloud-download-fill::before { content: map-get($bootstrap-icons-map, "cloud-download-fill"); } -.bi-cloud-download::before { content: map-get($bootstrap-icons-map, "cloud-download"); } -.bi-cloud-drizzle-fill::before { content: map-get($bootstrap-icons-map, "cloud-drizzle-fill"); } -.bi-cloud-drizzle::before { content: map-get($bootstrap-icons-map, "cloud-drizzle"); } -.bi-cloud-fill::before { content: map-get($bootstrap-icons-map, "cloud-fill"); } -.bi-cloud-fog-fill::before { content: map-get($bootstrap-icons-map, "cloud-fog-fill"); } -.bi-cloud-fog::before { content: map-get($bootstrap-icons-map, "cloud-fog"); } -.bi-cloud-fog2-fill::before { content: map-get($bootstrap-icons-map, "cloud-fog2-fill"); } -.bi-cloud-fog2::before { content: map-get($bootstrap-icons-map, "cloud-fog2"); } -.bi-cloud-hail-fill::before { content: map-get($bootstrap-icons-map, "cloud-hail-fill"); } -.bi-cloud-hail::before { content: map-get($bootstrap-icons-map, "cloud-hail"); } -.bi-cloud-haze-1::before { content: map-get($bootstrap-icons-map, "cloud-haze-1"); } -.bi-cloud-haze-fill::before { content: map-get($bootstrap-icons-map, "cloud-haze-fill"); } -.bi-cloud-haze::before { content: map-get($bootstrap-icons-map, "cloud-haze"); } -.bi-cloud-haze2-fill::before { content: map-get($bootstrap-icons-map, "cloud-haze2-fill"); } -.bi-cloud-lightning-fill::before { content: map-get($bootstrap-icons-map, "cloud-lightning-fill"); } -.bi-cloud-lightning-rain-fill::before { content: map-get($bootstrap-icons-map, "cloud-lightning-rain-fill"); } -.bi-cloud-lightning-rain::before { content: map-get($bootstrap-icons-map, "cloud-lightning-rain"); } -.bi-cloud-lightning::before { content: map-get($bootstrap-icons-map, "cloud-lightning"); } -.bi-cloud-minus-fill::before { content: map-get($bootstrap-icons-map, "cloud-minus-fill"); } -.bi-cloud-minus::before { content: map-get($bootstrap-icons-map, "cloud-minus"); } -.bi-cloud-moon-fill::before { content: map-get($bootstrap-icons-map, "cloud-moon-fill"); } -.bi-cloud-moon::before { content: map-get($bootstrap-icons-map, "cloud-moon"); } -.bi-cloud-plus-fill::before { content: map-get($bootstrap-icons-map, "cloud-plus-fill"); } -.bi-cloud-plus::before { content: map-get($bootstrap-icons-map, "cloud-plus"); } -.bi-cloud-rain-fill::before { content: map-get($bootstrap-icons-map, "cloud-rain-fill"); } -.bi-cloud-rain-heavy-fill::before { content: map-get($bootstrap-icons-map, "cloud-rain-heavy-fill"); } -.bi-cloud-rain-heavy::before { content: map-get($bootstrap-icons-map, "cloud-rain-heavy"); } -.bi-cloud-rain::before { content: map-get($bootstrap-icons-map, "cloud-rain"); } -.bi-cloud-slash-fill::before { content: map-get($bootstrap-icons-map, "cloud-slash-fill"); } -.bi-cloud-slash::before { content: map-get($bootstrap-icons-map, "cloud-slash"); } -.bi-cloud-sleet-fill::before { content: map-get($bootstrap-icons-map, "cloud-sleet-fill"); } -.bi-cloud-sleet::before { content: map-get($bootstrap-icons-map, "cloud-sleet"); } -.bi-cloud-snow-fill::before { content: map-get($bootstrap-icons-map, "cloud-snow-fill"); } -.bi-cloud-snow::before { content: map-get($bootstrap-icons-map, "cloud-snow"); } -.bi-cloud-sun-fill::before { content: map-get($bootstrap-icons-map, "cloud-sun-fill"); } -.bi-cloud-sun::before { content: map-get($bootstrap-icons-map, "cloud-sun"); } -.bi-cloud-upload-fill::before { content: map-get($bootstrap-icons-map, "cloud-upload-fill"); } -.bi-cloud-upload::before { content: map-get($bootstrap-icons-map, "cloud-upload"); } -.bi-cloud::before { content: map-get($bootstrap-icons-map, "cloud"); } -.bi-clouds-fill::before { content: map-get($bootstrap-icons-map, "clouds-fill"); } -.bi-clouds::before { content: map-get($bootstrap-icons-map, "clouds"); } -.bi-cloudy-fill::before { content: map-get($bootstrap-icons-map, "cloudy-fill"); } -.bi-cloudy::before { content: map-get($bootstrap-icons-map, "cloudy"); } -.bi-code-slash::before { content: map-get($bootstrap-icons-map, "code-slash"); } -.bi-code-square::before { content: map-get($bootstrap-icons-map, "code-square"); } -.bi-code::before { content: map-get($bootstrap-icons-map, "code"); } -.bi-collection-fill::before { content: map-get($bootstrap-icons-map, "collection-fill"); } -.bi-collection-play-fill::before { content: map-get($bootstrap-icons-map, "collection-play-fill"); } -.bi-collection-play::before { content: map-get($bootstrap-icons-map, "collection-play"); } -.bi-collection::before { content: map-get($bootstrap-icons-map, "collection"); } -.bi-columns-gap::before { content: map-get($bootstrap-icons-map, "columns-gap"); } -.bi-columns::before { content: map-get($bootstrap-icons-map, "columns"); } -.bi-command::before { content: map-get($bootstrap-icons-map, "command"); } -.bi-compass-fill::before { content: map-get($bootstrap-icons-map, "compass-fill"); } -.bi-compass::before { content: map-get($bootstrap-icons-map, "compass"); } -.bi-cone-striped::before { content: map-get($bootstrap-icons-map, "cone-striped"); } -.bi-cone::before { content: map-get($bootstrap-icons-map, "cone"); } -.bi-controller::before { content: map-get($bootstrap-icons-map, "controller"); } -.bi-cpu-fill::before { content: map-get($bootstrap-icons-map, "cpu-fill"); } -.bi-cpu::before { content: map-get($bootstrap-icons-map, "cpu"); } -.bi-credit-card-2-back-fill::before { content: map-get($bootstrap-icons-map, "credit-card-2-back-fill"); } -.bi-credit-card-2-back::before { content: map-get($bootstrap-icons-map, "credit-card-2-back"); } -.bi-credit-card-2-front-fill::before { content: map-get($bootstrap-icons-map, "credit-card-2-front-fill"); } -.bi-credit-card-2-front::before { content: map-get($bootstrap-icons-map, "credit-card-2-front"); } -.bi-credit-card-fill::before { content: map-get($bootstrap-icons-map, "credit-card-fill"); } -.bi-credit-card::before { content: map-get($bootstrap-icons-map, "credit-card"); } -.bi-crop::before { content: map-get($bootstrap-icons-map, "crop"); } -.bi-cup-fill::before { content: map-get($bootstrap-icons-map, "cup-fill"); } -.bi-cup-straw::before { content: map-get($bootstrap-icons-map, "cup-straw"); } -.bi-cup::before { content: map-get($bootstrap-icons-map, "cup"); } -.bi-cursor-fill::before { content: map-get($bootstrap-icons-map, "cursor-fill"); } -.bi-cursor-text::before { content: map-get($bootstrap-icons-map, "cursor-text"); } -.bi-cursor::before { content: map-get($bootstrap-icons-map, "cursor"); } -.bi-dash-circle-dotted::before { content: map-get($bootstrap-icons-map, "dash-circle-dotted"); } -.bi-dash-circle-fill::before { content: map-get($bootstrap-icons-map, "dash-circle-fill"); } -.bi-dash-circle::before { content: map-get($bootstrap-icons-map, "dash-circle"); } -.bi-dash-square-dotted::before { content: map-get($bootstrap-icons-map, "dash-square-dotted"); } -.bi-dash-square-fill::before { content: map-get($bootstrap-icons-map, "dash-square-fill"); } -.bi-dash-square::before { content: map-get($bootstrap-icons-map, "dash-square"); } -.bi-dash::before { content: map-get($bootstrap-icons-map, "dash"); } -.bi-diagram-2-fill::before { content: map-get($bootstrap-icons-map, "diagram-2-fill"); } -.bi-diagram-2::before { content: map-get($bootstrap-icons-map, "diagram-2"); } -.bi-diagram-3-fill::before { content: map-get($bootstrap-icons-map, "diagram-3-fill"); } -.bi-diagram-3::before { content: map-get($bootstrap-icons-map, "diagram-3"); } -.bi-diamond-fill::before { content: map-get($bootstrap-icons-map, "diamond-fill"); } -.bi-diamond-half::before { content: map-get($bootstrap-icons-map, "diamond-half"); } -.bi-diamond::before { content: map-get($bootstrap-icons-map, "diamond"); } -.bi-dice-1-fill::before { content: map-get($bootstrap-icons-map, "dice-1-fill"); } -.bi-dice-1::before { content: map-get($bootstrap-icons-map, "dice-1"); } -.bi-dice-2-fill::before { content: map-get($bootstrap-icons-map, "dice-2-fill"); } -.bi-dice-2::before { content: map-get($bootstrap-icons-map, "dice-2"); } -.bi-dice-3-fill::before { content: map-get($bootstrap-icons-map, "dice-3-fill"); } -.bi-dice-3::before { content: map-get($bootstrap-icons-map, "dice-3"); } -.bi-dice-4-fill::before { content: map-get($bootstrap-icons-map, "dice-4-fill"); } -.bi-dice-4::before { content: map-get($bootstrap-icons-map, "dice-4"); } -.bi-dice-5-fill::before { content: map-get($bootstrap-icons-map, "dice-5-fill"); } -.bi-dice-5::before { content: map-get($bootstrap-icons-map, "dice-5"); } -.bi-dice-6-fill::before { content: map-get($bootstrap-icons-map, "dice-6-fill"); } -.bi-dice-6::before { content: map-get($bootstrap-icons-map, "dice-6"); } -.bi-disc-fill::before { content: map-get($bootstrap-icons-map, "disc-fill"); } -.bi-disc::before { content: map-get($bootstrap-icons-map, "disc"); } -.bi-discord::before { content: map-get($bootstrap-icons-map, "discord"); } -.bi-display-fill::before { content: map-get($bootstrap-icons-map, "display-fill"); } -.bi-display::before { content: map-get($bootstrap-icons-map, "display"); } -.bi-distribute-horizontal::before { content: map-get($bootstrap-icons-map, "distribute-horizontal"); } -.bi-distribute-vertical::before { content: map-get($bootstrap-icons-map, "distribute-vertical"); } -.bi-door-closed-fill::before { content: map-get($bootstrap-icons-map, "door-closed-fill"); } -.bi-door-closed::before { content: map-get($bootstrap-icons-map, "door-closed"); } -.bi-door-open-fill::before { content: map-get($bootstrap-icons-map, "door-open-fill"); } -.bi-door-open::before { content: map-get($bootstrap-icons-map, "door-open"); } -.bi-dot::before { content: map-get($bootstrap-icons-map, "dot"); } -.bi-download::before { content: map-get($bootstrap-icons-map, "download"); } -.bi-droplet-fill::before { content: map-get($bootstrap-icons-map, "droplet-fill"); } -.bi-droplet-half::before { content: map-get($bootstrap-icons-map, "droplet-half"); } -.bi-droplet::before { content: map-get($bootstrap-icons-map, "droplet"); } -.bi-earbuds::before { content: map-get($bootstrap-icons-map, "earbuds"); } -.bi-easel-fill::before { content: map-get($bootstrap-icons-map, "easel-fill"); } -.bi-easel::before { content: map-get($bootstrap-icons-map, "easel"); } -.bi-egg-fill::before { content: map-get($bootstrap-icons-map, "egg-fill"); } -.bi-egg-fried::before { content: map-get($bootstrap-icons-map, "egg-fried"); } -.bi-egg::before { content: map-get($bootstrap-icons-map, "egg"); } -.bi-eject-fill::before { content: map-get($bootstrap-icons-map, "eject-fill"); } -.bi-eject::before { content: map-get($bootstrap-icons-map, "eject"); } -.bi-emoji-angry-fill::before { content: map-get($bootstrap-icons-map, "emoji-angry-fill"); } -.bi-emoji-angry::before { content: map-get($bootstrap-icons-map, "emoji-angry"); } -.bi-emoji-dizzy-fill::before { content: map-get($bootstrap-icons-map, "emoji-dizzy-fill"); } -.bi-emoji-dizzy::before { content: map-get($bootstrap-icons-map, "emoji-dizzy"); } -.bi-emoji-expressionless-fill::before { content: map-get($bootstrap-icons-map, "emoji-expressionless-fill"); } -.bi-emoji-expressionless::before { content: map-get($bootstrap-icons-map, "emoji-expressionless"); } -.bi-emoji-frown-fill::before { content: map-get($bootstrap-icons-map, "emoji-frown-fill"); } -.bi-emoji-frown::before { content: map-get($bootstrap-icons-map, "emoji-frown"); } -.bi-emoji-heart-eyes-fill::before { content: map-get($bootstrap-icons-map, "emoji-heart-eyes-fill"); } -.bi-emoji-heart-eyes::before { content: map-get($bootstrap-icons-map, "emoji-heart-eyes"); } -.bi-emoji-laughing-fill::before { content: map-get($bootstrap-icons-map, "emoji-laughing-fill"); } -.bi-emoji-laughing::before { content: map-get($bootstrap-icons-map, "emoji-laughing"); } -.bi-emoji-neutral-fill::before { content: map-get($bootstrap-icons-map, "emoji-neutral-fill"); } -.bi-emoji-neutral::before { content: map-get($bootstrap-icons-map, "emoji-neutral"); } -.bi-emoji-smile-fill::before { content: map-get($bootstrap-icons-map, "emoji-smile-fill"); } -.bi-emoji-smile-upside-down-fill::before { content: map-get($bootstrap-icons-map, "emoji-smile-upside-down-fill"); } -.bi-emoji-smile-upside-down::before { content: map-get($bootstrap-icons-map, "emoji-smile-upside-down"); } -.bi-emoji-smile::before { content: map-get($bootstrap-icons-map, "emoji-smile"); } -.bi-emoji-sunglasses-fill::before { content: map-get($bootstrap-icons-map, "emoji-sunglasses-fill"); } -.bi-emoji-sunglasses::before { content: map-get($bootstrap-icons-map, "emoji-sunglasses"); } -.bi-emoji-wink-fill::before { content: map-get($bootstrap-icons-map, "emoji-wink-fill"); } -.bi-emoji-wink::before { content: map-get($bootstrap-icons-map, "emoji-wink"); } -.bi-envelope-fill::before { content: map-get($bootstrap-icons-map, "envelope-fill"); } -.bi-envelope-open-fill::before { content: map-get($bootstrap-icons-map, "envelope-open-fill"); } -.bi-envelope-open::before { content: map-get($bootstrap-icons-map, "envelope-open"); } -.bi-envelope::before { content: map-get($bootstrap-icons-map, "envelope"); } -.bi-eraser-fill::before { content: map-get($bootstrap-icons-map, "eraser-fill"); } -.bi-eraser::before { content: map-get($bootstrap-icons-map, "eraser"); } -.bi-exclamation-circle-fill::before { content: map-get($bootstrap-icons-map, "exclamation-circle-fill"); } -.bi-exclamation-circle::before { content: map-get($bootstrap-icons-map, "exclamation-circle"); } -.bi-exclamation-diamond-fill::before { content: map-get($bootstrap-icons-map, "exclamation-diamond-fill"); } -.bi-exclamation-diamond::before { content: map-get($bootstrap-icons-map, "exclamation-diamond"); } -.bi-exclamation-octagon-fill::before { content: map-get($bootstrap-icons-map, "exclamation-octagon-fill"); } -.bi-exclamation-octagon::before { content: map-get($bootstrap-icons-map, "exclamation-octagon"); } -.bi-exclamation-square-fill::before { content: map-get($bootstrap-icons-map, "exclamation-square-fill"); } -.bi-exclamation-square::before { content: map-get($bootstrap-icons-map, "exclamation-square"); } -.bi-exclamation-triangle-fill::before { content: map-get($bootstrap-icons-map, "exclamation-triangle-fill"); } -.bi-exclamation-triangle::before { content: map-get($bootstrap-icons-map, "exclamation-triangle"); } -.bi-exclamation::before { content: map-get($bootstrap-icons-map, "exclamation"); } -.bi-exclude::before { content: map-get($bootstrap-icons-map, "exclude"); } -.bi-eye-fill::before { content: map-get($bootstrap-icons-map, "eye-fill"); } -.bi-eye-slash-fill::before { content: map-get($bootstrap-icons-map, "eye-slash-fill"); } -.bi-eye-slash::before { content: map-get($bootstrap-icons-map, "eye-slash"); } -.bi-eye::before { content: map-get($bootstrap-icons-map, "eye"); } -.bi-eyedropper::before { content: map-get($bootstrap-icons-map, "eyedropper"); } -.bi-eyeglasses::before { content: map-get($bootstrap-icons-map, "eyeglasses"); } -.bi-facebook::before { content: map-get($bootstrap-icons-map, "facebook"); } -.bi-file-arrow-down-fill::before { content: map-get($bootstrap-icons-map, "file-arrow-down-fill"); } -.bi-file-arrow-down::before { content: map-get($bootstrap-icons-map, "file-arrow-down"); } -.bi-file-arrow-up-fill::before { content: map-get($bootstrap-icons-map, "file-arrow-up-fill"); } -.bi-file-arrow-up::before { content: map-get($bootstrap-icons-map, "file-arrow-up"); } -.bi-file-bar-graph-fill::before { content: map-get($bootstrap-icons-map, "file-bar-graph-fill"); } -.bi-file-bar-graph::before { content: map-get($bootstrap-icons-map, "file-bar-graph"); } -.bi-file-binary-fill::before { content: map-get($bootstrap-icons-map, "file-binary-fill"); } -.bi-file-binary::before { content: map-get($bootstrap-icons-map, "file-binary"); } -.bi-file-break-fill::before { content: map-get($bootstrap-icons-map, "file-break-fill"); } -.bi-file-break::before { content: map-get($bootstrap-icons-map, "file-break"); } -.bi-file-check-fill::before { content: map-get($bootstrap-icons-map, "file-check-fill"); } -.bi-file-check::before { content: map-get($bootstrap-icons-map, "file-check"); } -.bi-file-code-fill::before { content: map-get($bootstrap-icons-map, "file-code-fill"); } -.bi-file-code::before { content: map-get($bootstrap-icons-map, "file-code"); } -.bi-file-diff-fill::before { content: map-get($bootstrap-icons-map, "file-diff-fill"); } -.bi-file-diff::before { content: map-get($bootstrap-icons-map, "file-diff"); } -.bi-file-earmark-arrow-down-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-arrow-down-fill"); } -.bi-file-earmark-arrow-down::before { content: map-get($bootstrap-icons-map, "file-earmark-arrow-down"); } -.bi-file-earmark-arrow-up-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-arrow-up-fill"); } -.bi-file-earmark-arrow-up::before { content: map-get($bootstrap-icons-map, "file-earmark-arrow-up"); } -.bi-file-earmark-bar-graph-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-bar-graph-fill"); } -.bi-file-earmark-bar-graph::before { content: map-get($bootstrap-icons-map, "file-earmark-bar-graph"); } -.bi-file-earmark-binary-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-binary-fill"); } -.bi-file-earmark-binary::before { content: map-get($bootstrap-icons-map, "file-earmark-binary"); } -.bi-file-earmark-break-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-break-fill"); } -.bi-file-earmark-break::before { content: map-get($bootstrap-icons-map, "file-earmark-break"); } -.bi-file-earmark-check-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-check-fill"); } -.bi-file-earmark-check::before { content: map-get($bootstrap-icons-map, "file-earmark-check"); } -.bi-file-earmark-code-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-code-fill"); } -.bi-file-earmark-code::before { content: map-get($bootstrap-icons-map, "file-earmark-code"); } -.bi-file-earmark-diff-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-diff-fill"); } -.bi-file-earmark-diff::before { content: map-get($bootstrap-icons-map, "file-earmark-diff"); } -.bi-file-earmark-easel-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-easel-fill"); } -.bi-file-earmark-easel::before { content: map-get($bootstrap-icons-map, "file-earmark-easel"); } -.bi-file-earmark-excel-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-excel-fill"); } -.bi-file-earmark-excel::before { content: map-get($bootstrap-icons-map, "file-earmark-excel"); } -.bi-file-earmark-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-fill"); } -.bi-file-earmark-font-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-font-fill"); } -.bi-file-earmark-font::before { content: map-get($bootstrap-icons-map, "file-earmark-font"); } -.bi-file-earmark-image-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-image-fill"); } -.bi-file-earmark-image::before { content: map-get($bootstrap-icons-map, "file-earmark-image"); } -.bi-file-earmark-lock-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-lock-fill"); } -.bi-file-earmark-lock::before { content: map-get($bootstrap-icons-map, "file-earmark-lock"); } -.bi-file-earmark-lock2-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-lock2-fill"); } -.bi-file-earmark-lock2::before { content: map-get($bootstrap-icons-map, "file-earmark-lock2"); } -.bi-file-earmark-medical-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-medical-fill"); } -.bi-file-earmark-medical::before { content: map-get($bootstrap-icons-map, "file-earmark-medical"); } -.bi-file-earmark-minus-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-minus-fill"); } -.bi-file-earmark-minus::before { content: map-get($bootstrap-icons-map, "file-earmark-minus"); } -.bi-file-earmark-music-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-music-fill"); } -.bi-file-earmark-music::before { content: map-get($bootstrap-icons-map, "file-earmark-music"); } -.bi-file-earmark-person-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-person-fill"); } -.bi-file-earmark-person::before { content: map-get($bootstrap-icons-map, "file-earmark-person"); } -.bi-file-earmark-play-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-play-fill"); } -.bi-file-earmark-play::before { content: map-get($bootstrap-icons-map, "file-earmark-play"); } -.bi-file-earmark-plus-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-plus-fill"); } -.bi-file-earmark-plus::before { content: map-get($bootstrap-icons-map, "file-earmark-plus"); } -.bi-file-earmark-post-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-post-fill"); } -.bi-file-earmark-post::before { content: map-get($bootstrap-icons-map, "file-earmark-post"); } -.bi-file-earmark-ppt-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-ppt-fill"); } -.bi-file-earmark-ppt::before { content: map-get($bootstrap-icons-map, "file-earmark-ppt"); } -.bi-file-earmark-richtext-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-richtext-fill"); } -.bi-file-earmark-richtext::before { content: map-get($bootstrap-icons-map, "file-earmark-richtext"); } -.bi-file-earmark-ruled-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-ruled-fill"); } -.bi-file-earmark-ruled::before { content: map-get($bootstrap-icons-map, "file-earmark-ruled"); } -.bi-file-earmark-slides-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-slides-fill"); } -.bi-file-earmark-slides::before { content: map-get($bootstrap-icons-map, "file-earmark-slides"); } -.bi-file-earmark-spreadsheet-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-spreadsheet-fill"); } -.bi-file-earmark-spreadsheet::before { content: map-get($bootstrap-icons-map, "file-earmark-spreadsheet"); } -.bi-file-earmark-text-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-text-fill"); } -.bi-file-earmark-text::before { content: map-get($bootstrap-icons-map, "file-earmark-text"); } -.bi-file-earmark-word-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-word-fill"); } -.bi-file-earmark-word::before { content: map-get($bootstrap-icons-map, "file-earmark-word"); } -.bi-file-earmark-x-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-x-fill"); } -.bi-file-earmark-x::before { content: map-get($bootstrap-icons-map, "file-earmark-x"); } -.bi-file-earmark-zip-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-zip-fill"); } -.bi-file-earmark-zip::before { content: map-get($bootstrap-icons-map, "file-earmark-zip"); } -.bi-file-earmark::before { content: map-get($bootstrap-icons-map, "file-earmark"); } -.bi-file-easel-fill::before { content: map-get($bootstrap-icons-map, "file-easel-fill"); } -.bi-file-easel::before { content: map-get($bootstrap-icons-map, "file-easel"); } -.bi-file-excel-fill::before { content: map-get($bootstrap-icons-map, "file-excel-fill"); } -.bi-file-excel::before { content: map-get($bootstrap-icons-map, "file-excel"); } -.bi-file-fill::before { content: map-get($bootstrap-icons-map, "file-fill"); } -.bi-file-font-fill::before { content: map-get($bootstrap-icons-map, "file-font-fill"); } -.bi-file-font::before { content: map-get($bootstrap-icons-map, "file-font"); } -.bi-file-image-fill::before { content: map-get($bootstrap-icons-map, "file-image-fill"); } -.bi-file-image::before { content: map-get($bootstrap-icons-map, "file-image"); } -.bi-file-lock-fill::before { content: map-get($bootstrap-icons-map, "file-lock-fill"); } -.bi-file-lock::before { content: map-get($bootstrap-icons-map, "file-lock"); } -.bi-file-lock2-fill::before { content: map-get($bootstrap-icons-map, "file-lock2-fill"); } -.bi-file-lock2::before { content: map-get($bootstrap-icons-map, "file-lock2"); } -.bi-file-medical-fill::before { content: map-get($bootstrap-icons-map, "file-medical-fill"); } -.bi-file-medical::before { content: map-get($bootstrap-icons-map, "file-medical"); } -.bi-file-minus-fill::before { content: map-get($bootstrap-icons-map, "file-minus-fill"); } -.bi-file-minus::before { content: map-get($bootstrap-icons-map, "file-minus"); } -.bi-file-music-fill::before { content: map-get($bootstrap-icons-map, "file-music-fill"); } -.bi-file-music::before { content: map-get($bootstrap-icons-map, "file-music"); } -.bi-file-person-fill::before { content: map-get($bootstrap-icons-map, "file-person-fill"); } -.bi-file-person::before { content: map-get($bootstrap-icons-map, "file-person"); } -.bi-file-play-fill::before { content: map-get($bootstrap-icons-map, "file-play-fill"); } -.bi-file-play::before { content: map-get($bootstrap-icons-map, "file-play"); } -.bi-file-plus-fill::before { content: map-get($bootstrap-icons-map, "file-plus-fill"); } -.bi-file-plus::before { content: map-get($bootstrap-icons-map, "file-plus"); } -.bi-file-post-fill::before { content: map-get($bootstrap-icons-map, "file-post-fill"); } -.bi-file-post::before { content: map-get($bootstrap-icons-map, "file-post"); } -.bi-file-ppt-fill::before { content: map-get($bootstrap-icons-map, "file-ppt-fill"); } -.bi-file-ppt::before { content: map-get($bootstrap-icons-map, "file-ppt"); } -.bi-file-richtext-fill::before { content: map-get($bootstrap-icons-map, "file-richtext-fill"); } -.bi-file-richtext::before { content: map-get($bootstrap-icons-map, "file-richtext"); } -.bi-file-ruled-fill::before { content: map-get($bootstrap-icons-map, "file-ruled-fill"); } -.bi-file-ruled::before { content: map-get($bootstrap-icons-map, "file-ruled"); } -.bi-file-slides-fill::before { content: map-get($bootstrap-icons-map, "file-slides-fill"); } -.bi-file-slides::before { content: map-get($bootstrap-icons-map, "file-slides"); } -.bi-file-spreadsheet-fill::before { content: map-get($bootstrap-icons-map, "file-spreadsheet-fill"); } -.bi-file-spreadsheet::before { content: map-get($bootstrap-icons-map, "file-spreadsheet"); } -.bi-file-text-fill::before { content: map-get($bootstrap-icons-map, "file-text-fill"); } -.bi-file-text::before { content: map-get($bootstrap-icons-map, "file-text"); } -.bi-file-word-fill::before { content: map-get($bootstrap-icons-map, "file-word-fill"); } -.bi-file-word::before { content: map-get($bootstrap-icons-map, "file-word"); } -.bi-file-x-fill::before { content: map-get($bootstrap-icons-map, "file-x-fill"); } -.bi-file-x::before { content: map-get($bootstrap-icons-map, "file-x"); } -.bi-file-zip-fill::before { content: map-get($bootstrap-icons-map, "file-zip-fill"); } -.bi-file-zip::before { content: map-get($bootstrap-icons-map, "file-zip"); } -.bi-file::before { content: map-get($bootstrap-icons-map, "file"); } -.bi-files-alt::before { content: map-get($bootstrap-icons-map, "files-alt"); } -.bi-files::before { content: map-get($bootstrap-icons-map, "files"); } -.bi-film::before { content: map-get($bootstrap-icons-map, "film"); } -.bi-filter-circle-fill::before { content: map-get($bootstrap-icons-map, "filter-circle-fill"); } -.bi-filter-circle::before { content: map-get($bootstrap-icons-map, "filter-circle"); } -.bi-filter-left::before { content: map-get($bootstrap-icons-map, "filter-left"); } -.bi-filter-right::before { content: map-get($bootstrap-icons-map, "filter-right"); } -.bi-filter-square-fill::before { content: map-get($bootstrap-icons-map, "filter-square-fill"); } -.bi-filter-square::before { content: map-get($bootstrap-icons-map, "filter-square"); } -.bi-filter::before { content: map-get($bootstrap-icons-map, "filter"); } -.bi-flag-fill::before { content: map-get($bootstrap-icons-map, "flag-fill"); } -.bi-flag::before { content: map-get($bootstrap-icons-map, "flag"); } -.bi-flower1::before { content: map-get($bootstrap-icons-map, "flower1"); } -.bi-flower2::before { content: map-get($bootstrap-icons-map, "flower2"); } -.bi-flower3::before { content: map-get($bootstrap-icons-map, "flower3"); } -.bi-folder-check::before { content: map-get($bootstrap-icons-map, "folder-check"); } -.bi-folder-fill::before { content: map-get($bootstrap-icons-map, "folder-fill"); } -.bi-folder-minus::before { content: map-get($bootstrap-icons-map, "folder-minus"); } -.bi-folder-plus::before { content: map-get($bootstrap-icons-map, "folder-plus"); } -.bi-folder-symlink-fill::before { content: map-get($bootstrap-icons-map, "folder-symlink-fill"); } -.bi-folder-symlink::before { content: map-get($bootstrap-icons-map, "folder-symlink"); } -.bi-folder-x::before { content: map-get($bootstrap-icons-map, "folder-x"); } -.bi-folder::before { content: map-get($bootstrap-icons-map, "folder"); } -.bi-folder2-open::before { content: map-get($bootstrap-icons-map, "folder2-open"); } -.bi-folder2::before { content: map-get($bootstrap-icons-map, "folder2"); } -.bi-fonts::before { content: map-get($bootstrap-icons-map, "fonts"); } -.bi-forward-fill::before { content: map-get($bootstrap-icons-map, "forward-fill"); } -.bi-forward::before { content: map-get($bootstrap-icons-map, "forward"); } -.bi-front::before { content: map-get($bootstrap-icons-map, "front"); } -.bi-fullscreen-exit::before { content: map-get($bootstrap-icons-map, "fullscreen-exit"); } -.bi-fullscreen::before { content: map-get($bootstrap-icons-map, "fullscreen"); } -.bi-funnel-fill::before { content: map-get($bootstrap-icons-map, "funnel-fill"); } -.bi-funnel::before { content: map-get($bootstrap-icons-map, "funnel"); } -.bi-gear-fill::before { content: map-get($bootstrap-icons-map, "gear-fill"); } -.bi-gear-wide-connected::before { content: map-get($bootstrap-icons-map, "gear-wide-connected"); } -.bi-gear-wide::before { content: map-get($bootstrap-icons-map, "gear-wide"); } -.bi-gear::before { content: map-get($bootstrap-icons-map, "gear"); } -.bi-gem::before { content: map-get($bootstrap-icons-map, "gem"); } -.bi-geo-alt-fill::before { content: map-get($bootstrap-icons-map, "geo-alt-fill"); } -.bi-geo-alt::before { content: map-get($bootstrap-icons-map, "geo-alt"); } -.bi-geo-fill::before { content: map-get($bootstrap-icons-map, "geo-fill"); } -.bi-geo::before { content: map-get($bootstrap-icons-map, "geo"); } -.bi-gift-fill::before { content: map-get($bootstrap-icons-map, "gift-fill"); } -.bi-gift::before { content: map-get($bootstrap-icons-map, "gift"); } -.bi-github::before { content: map-get($bootstrap-icons-map, "github"); } -.bi-globe::before { content: map-get($bootstrap-icons-map, "globe"); } -.bi-globe2::before { content: map-get($bootstrap-icons-map, "globe2"); } -.bi-google::before { content: map-get($bootstrap-icons-map, "google"); } -.bi-graph-down::before { content: map-get($bootstrap-icons-map, "graph-down"); } -.bi-graph-up::before { content: map-get($bootstrap-icons-map, "graph-up"); } -.bi-grid-1x2-fill::before { content: map-get($bootstrap-icons-map, "grid-1x2-fill"); } -.bi-grid-1x2::before { content: map-get($bootstrap-icons-map, "grid-1x2"); } -.bi-grid-3x2-gap-fill::before { content: map-get($bootstrap-icons-map, "grid-3x2-gap-fill"); } -.bi-grid-3x2-gap::before { content: map-get($bootstrap-icons-map, "grid-3x2-gap"); } -.bi-grid-3x2::before { content: map-get($bootstrap-icons-map, "grid-3x2"); } -.bi-grid-3x3-gap-fill::before { content: map-get($bootstrap-icons-map, "grid-3x3-gap-fill"); } -.bi-grid-3x3-gap::before { content: map-get($bootstrap-icons-map, "grid-3x3-gap"); } -.bi-grid-3x3::before { content: map-get($bootstrap-icons-map, "grid-3x3"); } -.bi-grid-fill::before { content: map-get($bootstrap-icons-map, "grid-fill"); } -.bi-grid::before { content: map-get($bootstrap-icons-map, "grid"); } -.bi-grip-horizontal::before { content: map-get($bootstrap-icons-map, "grip-horizontal"); } -.bi-grip-vertical::before { content: map-get($bootstrap-icons-map, "grip-vertical"); } -.bi-hammer::before { content: map-get($bootstrap-icons-map, "hammer"); } -.bi-hand-index-fill::before { content: map-get($bootstrap-icons-map, "hand-index-fill"); } -.bi-hand-index-thumb-fill::before { content: map-get($bootstrap-icons-map, "hand-index-thumb-fill"); } -.bi-hand-index-thumb::before { content: map-get($bootstrap-icons-map, "hand-index-thumb"); } -.bi-hand-index::before { content: map-get($bootstrap-icons-map, "hand-index"); } -.bi-hand-thumbs-down-fill::before { content: map-get($bootstrap-icons-map, "hand-thumbs-down-fill"); } -.bi-hand-thumbs-down::before { content: map-get($bootstrap-icons-map, "hand-thumbs-down"); } -.bi-hand-thumbs-up-fill::before { content: map-get($bootstrap-icons-map, "hand-thumbs-up-fill"); } -.bi-hand-thumbs-up::before { content: map-get($bootstrap-icons-map, "hand-thumbs-up"); } -.bi-handbag-fill::before { content: map-get($bootstrap-icons-map, "handbag-fill"); } -.bi-handbag::before { content: map-get($bootstrap-icons-map, "handbag"); } -.bi-hash::before { content: map-get($bootstrap-icons-map, "hash"); } -.bi-hdd-fill::before { content: map-get($bootstrap-icons-map, "hdd-fill"); } -.bi-hdd-network-fill::before { content: map-get($bootstrap-icons-map, "hdd-network-fill"); } -.bi-hdd-network::before { content: map-get($bootstrap-icons-map, "hdd-network"); } -.bi-hdd-rack-fill::before { content: map-get($bootstrap-icons-map, "hdd-rack-fill"); } -.bi-hdd-rack::before { content: map-get($bootstrap-icons-map, "hdd-rack"); } -.bi-hdd-stack-fill::before { content: map-get($bootstrap-icons-map, "hdd-stack-fill"); } -.bi-hdd-stack::before { content: map-get($bootstrap-icons-map, "hdd-stack"); } -.bi-hdd::before { content: map-get($bootstrap-icons-map, "hdd"); } -.bi-headphones::before { content: map-get($bootstrap-icons-map, "headphones"); } -.bi-headset::before { content: map-get($bootstrap-icons-map, "headset"); } -.bi-heart-fill::before { content: map-get($bootstrap-icons-map, "heart-fill"); } -.bi-heart-half::before { content: map-get($bootstrap-icons-map, "heart-half"); } -.bi-heart::before { content: map-get($bootstrap-icons-map, "heart"); } -.bi-heptagon-fill::before { content: map-get($bootstrap-icons-map, "heptagon-fill"); } -.bi-heptagon-half::before { content: map-get($bootstrap-icons-map, "heptagon-half"); } -.bi-heptagon::before { content: map-get($bootstrap-icons-map, "heptagon"); } -.bi-hexagon-fill::before { content: map-get($bootstrap-icons-map, "hexagon-fill"); } -.bi-hexagon-half::before { content: map-get($bootstrap-icons-map, "hexagon-half"); } -.bi-hexagon::before { content: map-get($bootstrap-icons-map, "hexagon"); } -.bi-hourglass-bottom::before { content: map-get($bootstrap-icons-map, "hourglass-bottom"); } -.bi-hourglass-split::before { content: map-get($bootstrap-icons-map, "hourglass-split"); } -.bi-hourglass-top::before { content: map-get($bootstrap-icons-map, "hourglass-top"); } -.bi-hourglass::before { content: map-get($bootstrap-icons-map, "hourglass"); } -.bi-house-door-fill::before { content: map-get($bootstrap-icons-map, "house-door-fill"); } -.bi-house-door::before { content: map-get($bootstrap-icons-map, "house-door"); } -.bi-house-fill::before { content: map-get($bootstrap-icons-map, "house-fill"); } -.bi-house::before { content: map-get($bootstrap-icons-map, "house"); } -.bi-hr::before { content: map-get($bootstrap-icons-map, "hr"); } -.bi-hurricane::before { content: map-get($bootstrap-icons-map, "hurricane"); } -.bi-image-alt::before { content: map-get($bootstrap-icons-map, "image-alt"); } -.bi-image-fill::before { content: map-get($bootstrap-icons-map, "image-fill"); } -.bi-image::before { content: map-get($bootstrap-icons-map, "image"); } -.bi-images::before { content: map-get($bootstrap-icons-map, "images"); } -.bi-inbox-fill::before { content: map-get($bootstrap-icons-map, "inbox-fill"); } -.bi-inbox::before { content: map-get($bootstrap-icons-map, "inbox"); } -.bi-inboxes-fill::before { content: map-get($bootstrap-icons-map, "inboxes-fill"); } -.bi-inboxes::before { content: map-get($bootstrap-icons-map, "inboxes"); } -.bi-info-circle-fill::before { content: map-get($bootstrap-icons-map, "info-circle-fill"); } -.bi-info-circle::before { content: map-get($bootstrap-icons-map, "info-circle"); } -.bi-info-square-fill::before { content: map-get($bootstrap-icons-map, "info-square-fill"); } -.bi-info-square::before { content: map-get($bootstrap-icons-map, "info-square"); } -.bi-info::before { content: map-get($bootstrap-icons-map, "info"); } -.bi-input-cursor-text::before { content: map-get($bootstrap-icons-map, "input-cursor-text"); } -.bi-input-cursor::before { content: map-get($bootstrap-icons-map, "input-cursor"); } -.bi-instagram::before { content: map-get($bootstrap-icons-map, "instagram"); } -.bi-intersect::before { content: map-get($bootstrap-icons-map, "intersect"); } -.bi-journal-album::before { content: map-get($bootstrap-icons-map, "journal-album"); } -.bi-journal-arrow-down::before { content: map-get($bootstrap-icons-map, "journal-arrow-down"); } -.bi-journal-arrow-up::before { content: map-get($bootstrap-icons-map, "journal-arrow-up"); } -.bi-journal-bookmark-fill::before { content: map-get($bootstrap-icons-map, "journal-bookmark-fill"); } -.bi-journal-bookmark::before { content: map-get($bootstrap-icons-map, "journal-bookmark"); } -.bi-journal-check::before { content: map-get($bootstrap-icons-map, "journal-check"); } -.bi-journal-code::before { content: map-get($bootstrap-icons-map, "journal-code"); } -.bi-journal-medical::before { content: map-get($bootstrap-icons-map, "journal-medical"); } -.bi-journal-minus::before { content: map-get($bootstrap-icons-map, "journal-minus"); } -.bi-journal-plus::before { content: map-get($bootstrap-icons-map, "journal-plus"); } -.bi-journal-richtext::before { content: map-get($bootstrap-icons-map, "journal-richtext"); } -.bi-journal-text::before { content: map-get($bootstrap-icons-map, "journal-text"); } -.bi-journal-x::before { content: map-get($bootstrap-icons-map, "journal-x"); } -.bi-journal::before { content: map-get($bootstrap-icons-map, "journal"); } -.bi-journals::before { content: map-get($bootstrap-icons-map, "journals"); } -.bi-joystick::before { content: map-get($bootstrap-icons-map, "joystick"); } -.bi-justify-left::before { content: map-get($bootstrap-icons-map, "justify-left"); } -.bi-justify-right::before { content: map-get($bootstrap-icons-map, "justify-right"); } -.bi-justify::before { content: map-get($bootstrap-icons-map, "justify"); } -.bi-kanban-fill::before { content: map-get($bootstrap-icons-map, "kanban-fill"); } -.bi-kanban::before { content: map-get($bootstrap-icons-map, "kanban"); } -.bi-key-fill::before { content: map-get($bootstrap-icons-map, "key-fill"); } -.bi-key::before { content: map-get($bootstrap-icons-map, "key"); } -.bi-keyboard-fill::before { content: map-get($bootstrap-icons-map, "keyboard-fill"); } -.bi-keyboard::before { content: map-get($bootstrap-icons-map, "keyboard"); } -.bi-ladder::before { content: map-get($bootstrap-icons-map, "ladder"); } -.bi-lamp-fill::before { content: map-get($bootstrap-icons-map, "lamp-fill"); } -.bi-lamp::before { content: map-get($bootstrap-icons-map, "lamp"); } -.bi-laptop-fill::before { content: map-get($bootstrap-icons-map, "laptop-fill"); } -.bi-laptop::before { content: map-get($bootstrap-icons-map, "laptop"); } -.bi-layer-backward::before { content: map-get($bootstrap-icons-map, "layer-backward"); } -.bi-layer-forward::before { content: map-get($bootstrap-icons-map, "layer-forward"); } -.bi-layers-fill::before { content: map-get($bootstrap-icons-map, "layers-fill"); } -.bi-layers-half::before { content: map-get($bootstrap-icons-map, "layers-half"); } -.bi-layers::before { content: map-get($bootstrap-icons-map, "layers"); } -.bi-layout-sidebar-inset-reverse::before { content: map-get($bootstrap-icons-map, "layout-sidebar-inset-reverse"); } -.bi-layout-sidebar-inset::before { content: map-get($bootstrap-icons-map, "layout-sidebar-inset"); } -.bi-layout-sidebar-reverse::before { content: map-get($bootstrap-icons-map, "layout-sidebar-reverse"); } -.bi-layout-sidebar::before { content: map-get($bootstrap-icons-map, "layout-sidebar"); } -.bi-layout-split::before { content: map-get($bootstrap-icons-map, "layout-split"); } -.bi-layout-text-sidebar-reverse::before { content: map-get($bootstrap-icons-map, "layout-text-sidebar-reverse"); } -.bi-layout-text-sidebar::before { content: map-get($bootstrap-icons-map, "layout-text-sidebar"); } -.bi-layout-text-window-reverse::before { content: map-get($bootstrap-icons-map, "layout-text-window-reverse"); } -.bi-layout-text-window::before { content: map-get($bootstrap-icons-map, "layout-text-window"); } -.bi-layout-three-columns::before { content: map-get($bootstrap-icons-map, "layout-three-columns"); } -.bi-layout-wtf::before { content: map-get($bootstrap-icons-map, "layout-wtf"); } -.bi-life-preserver::before { content: map-get($bootstrap-icons-map, "life-preserver"); } -.bi-lightbulb-fill::before { content: map-get($bootstrap-icons-map, "lightbulb-fill"); } -.bi-lightbulb-off-fill::before { content: map-get($bootstrap-icons-map, "lightbulb-off-fill"); } -.bi-lightbulb-off::before { content: map-get($bootstrap-icons-map, "lightbulb-off"); } -.bi-lightbulb::before { content: map-get($bootstrap-icons-map, "lightbulb"); } -.bi-lightning-charge-fill::before { content: map-get($bootstrap-icons-map, "lightning-charge-fill"); } -.bi-lightning-charge::before { content: map-get($bootstrap-icons-map, "lightning-charge"); } -.bi-lightning-fill::before { content: map-get($bootstrap-icons-map, "lightning-fill"); } -.bi-lightning::before { content: map-get($bootstrap-icons-map, "lightning"); } -.bi-link-45deg::before { content: map-get($bootstrap-icons-map, "link-45deg"); } -.bi-link::before { content: map-get($bootstrap-icons-map, "link"); } -.bi-linkedin::before { content: map-get($bootstrap-icons-map, "linkedin"); } -.bi-list-check::before { content: map-get($bootstrap-icons-map, "list-check"); } -.bi-list-nested::before { content: map-get($bootstrap-icons-map, "list-nested"); } -.bi-list-ol::before { content: map-get($bootstrap-icons-map, "list-ol"); } -.bi-list-stars::before { content: map-get($bootstrap-icons-map, "list-stars"); } -.bi-list-task::before { content: map-get($bootstrap-icons-map, "list-task"); } -.bi-list-ul::before { content: map-get($bootstrap-icons-map, "list-ul"); } -.bi-list::before { content: map-get($bootstrap-icons-map, "list"); } -.bi-lock-fill::before { content: map-get($bootstrap-icons-map, "lock-fill"); } -.bi-lock::before { content: map-get($bootstrap-icons-map, "lock"); } -.bi-mailbox::before { content: map-get($bootstrap-icons-map, "mailbox"); } -.bi-mailbox2::before { content: map-get($bootstrap-icons-map, "mailbox2"); } -.bi-map-fill::before { content: map-get($bootstrap-icons-map, "map-fill"); } -.bi-map::before { content: map-get($bootstrap-icons-map, "map"); } -.bi-markdown-fill::before { content: map-get($bootstrap-icons-map, "markdown-fill"); } -.bi-markdown::before { content: map-get($bootstrap-icons-map, "markdown"); } -.bi-mask::before { content: map-get($bootstrap-icons-map, "mask"); } -.bi-megaphone-fill::before { content: map-get($bootstrap-icons-map, "megaphone-fill"); } -.bi-megaphone::before { content: map-get($bootstrap-icons-map, "megaphone"); } -.bi-menu-app-fill::before { content: map-get($bootstrap-icons-map, "menu-app-fill"); } -.bi-menu-app::before { content: map-get($bootstrap-icons-map, "menu-app"); } -.bi-menu-button-fill::before { content: map-get($bootstrap-icons-map, "menu-button-fill"); } -.bi-menu-button-wide-fill::before { content: map-get($bootstrap-icons-map, "menu-button-wide-fill"); } -.bi-menu-button-wide::before { content: map-get($bootstrap-icons-map, "menu-button-wide"); } -.bi-menu-button::before { content: map-get($bootstrap-icons-map, "menu-button"); } -.bi-menu-down::before { content: map-get($bootstrap-icons-map, "menu-down"); } -.bi-menu-up::before { content: map-get($bootstrap-icons-map, "menu-up"); } -.bi-mic-fill::before { content: map-get($bootstrap-icons-map, "mic-fill"); } -.bi-mic-mute-fill::before { content: map-get($bootstrap-icons-map, "mic-mute-fill"); } -.bi-mic-mute::before { content: map-get($bootstrap-icons-map, "mic-mute"); } -.bi-mic::before { content: map-get($bootstrap-icons-map, "mic"); } -.bi-minecart-loaded::before { content: map-get($bootstrap-icons-map, "minecart-loaded"); } -.bi-minecart::before { content: map-get($bootstrap-icons-map, "minecart"); } -.bi-moisture::before { content: map-get($bootstrap-icons-map, "moisture"); } -.bi-moon-fill::before { content: map-get($bootstrap-icons-map, "moon-fill"); } -.bi-moon-stars-fill::before { content: map-get($bootstrap-icons-map, "moon-stars-fill"); } -.bi-moon-stars::before { content: map-get($bootstrap-icons-map, "moon-stars"); } -.bi-moon::before { content: map-get($bootstrap-icons-map, "moon"); } -.bi-mouse-fill::before { content: map-get($bootstrap-icons-map, "mouse-fill"); } -.bi-mouse::before { content: map-get($bootstrap-icons-map, "mouse"); } -.bi-mouse2-fill::before { content: map-get($bootstrap-icons-map, "mouse2-fill"); } -.bi-mouse2::before { content: map-get($bootstrap-icons-map, "mouse2"); } -.bi-mouse3-fill::before { content: map-get($bootstrap-icons-map, "mouse3-fill"); } -.bi-mouse3::before { content: map-get($bootstrap-icons-map, "mouse3"); } -.bi-music-note-beamed::before { content: map-get($bootstrap-icons-map, "music-note-beamed"); } -.bi-music-note-list::before { content: map-get($bootstrap-icons-map, "music-note-list"); } -.bi-music-note::before { content: map-get($bootstrap-icons-map, "music-note"); } -.bi-music-player-fill::before { content: map-get($bootstrap-icons-map, "music-player-fill"); } -.bi-music-player::before { content: map-get($bootstrap-icons-map, "music-player"); } -.bi-newspaper::before { content: map-get($bootstrap-icons-map, "newspaper"); } -.bi-node-minus-fill::before { content: map-get($bootstrap-icons-map, "node-minus-fill"); } -.bi-node-minus::before { content: map-get($bootstrap-icons-map, "node-minus"); } -.bi-node-plus-fill::before { content: map-get($bootstrap-icons-map, "node-plus-fill"); } -.bi-node-plus::before { content: map-get($bootstrap-icons-map, "node-plus"); } -.bi-nut-fill::before { content: map-get($bootstrap-icons-map, "nut-fill"); } -.bi-nut::before { content: map-get($bootstrap-icons-map, "nut"); } -.bi-octagon-fill::before { content: map-get($bootstrap-icons-map, "octagon-fill"); } -.bi-octagon-half::before { content: map-get($bootstrap-icons-map, "octagon-half"); } -.bi-octagon::before { content: map-get($bootstrap-icons-map, "octagon"); } -.bi-option::before { content: map-get($bootstrap-icons-map, "option"); } -.bi-outlet::before { content: map-get($bootstrap-icons-map, "outlet"); } -.bi-paint-bucket::before { content: map-get($bootstrap-icons-map, "paint-bucket"); } -.bi-palette-fill::before { content: map-get($bootstrap-icons-map, "palette-fill"); } -.bi-palette::before { content: map-get($bootstrap-icons-map, "palette"); } -.bi-palette2::before { content: map-get($bootstrap-icons-map, "palette2"); } -.bi-paperclip::before { content: map-get($bootstrap-icons-map, "paperclip"); } -.bi-paragraph::before { content: map-get($bootstrap-icons-map, "paragraph"); } -.bi-patch-check-fill::before { content: map-get($bootstrap-icons-map, "patch-check-fill"); } -.bi-patch-check::before { content: map-get($bootstrap-icons-map, "patch-check"); } -.bi-patch-exclamation-fill::before { content: map-get($bootstrap-icons-map, "patch-exclamation-fill"); } -.bi-patch-exclamation::before { content: map-get($bootstrap-icons-map, "patch-exclamation"); } -.bi-patch-minus-fill::before { content: map-get($bootstrap-icons-map, "patch-minus-fill"); } -.bi-patch-minus::before { content: map-get($bootstrap-icons-map, "patch-minus"); } -.bi-patch-plus-fill::before { content: map-get($bootstrap-icons-map, "patch-plus-fill"); } -.bi-patch-plus::before { content: map-get($bootstrap-icons-map, "patch-plus"); } -.bi-patch-question-fill::before { content: map-get($bootstrap-icons-map, "patch-question-fill"); } -.bi-patch-question::before { content: map-get($bootstrap-icons-map, "patch-question"); } -.bi-pause-btn-fill::before { content: map-get($bootstrap-icons-map, "pause-btn-fill"); } -.bi-pause-btn::before { content: map-get($bootstrap-icons-map, "pause-btn"); } -.bi-pause-circle-fill::before { content: map-get($bootstrap-icons-map, "pause-circle-fill"); } -.bi-pause-circle::before { content: map-get($bootstrap-icons-map, "pause-circle"); } -.bi-pause-fill::before { content: map-get($bootstrap-icons-map, "pause-fill"); } -.bi-pause::before { content: map-get($bootstrap-icons-map, "pause"); } -.bi-peace-fill::before { content: map-get($bootstrap-icons-map, "peace-fill"); } -.bi-peace::before { content: map-get($bootstrap-icons-map, "peace"); } -.bi-pen-fill::before { content: map-get($bootstrap-icons-map, "pen-fill"); } -.bi-pen::before { content: map-get($bootstrap-icons-map, "pen"); } -.bi-pencil-fill::before { content: map-get($bootstrap-icons-map, "pencil-fill"); } -.bi-pencil-square::before { content: map-get($bootstrap-icons-map, "pencil-square"); } -.bi-pencil::before { content: map-get($bootstrap-icons-map, "pencil"); } -.bi-pentagon-fill::before { content: map-get($bootstrap-icons-map, "pentagon-fill"); } -.bi-pentagon-half::before { content: map-get($bootstrap-icons-map, "pentagon-half"); } -.bi-pentagon::before { content: map-get($bootstrap-icons-map, "pentagon"); } -.bi-people-fill::before { content: map-get($bootstrap-icons-map, "people-fill"); } -.bi-people::before { content: map-get($bootstrap-icons-map, "people"); } -.bi-percent::before { content: map-get($bootstrap-icons-map, "percent"); } -.bi-person-badge-fill::before { content: map-get($bootstrap-icons-map, "person-badge-fill"); } -.bi-person-badge::before { content: map-get($bootstrap-icons-map, "person-badge"); } -.bi-person-bounding-box::before { content: map-get($bootstrap-icons-map, "person-bounding-box"); } -.bi-person-check-fill::before { content: map-get($bootstrap-icons-map, "person-check-fill"); } -.bi-person-check::before { content: map-get($bootstrap-icons-map, "person-check"); } -.bi-person-circle::before { content: map-get($bootstrap-icons-map, "person-circle"); } -.bi-person-dash-fill::before { content: map-get($bootstrap-icons-map, "person-dash-fill"); } -.bi-person-dash::before { content: map-get($bootstrap-icons-map, "person-dash"); } -.bi-person-fill::before { content: map-get($bootstrap-icons-map, "person-fill"); } -.bi-person-lines-fill::before { content: map-get($bootstrap-icons-map, "person-lines-fill"); } -.bi-person-plus-fill::before { content: map-get($bootstrap-icons-map, "person-plus-fill"); } -.bi-person-plus::before { content: map-get($bootstrap-icons-map, "person-plus"); } -.bi-person-square::before { content: map-get($bootstrap-icons-map, "person-square"); } -.bi-person-x-fill::before { content: map-get($bootstrap-icons-map, "person-x-fill"); } -.bi-person-x::before { content: map-get($bootstrap-icons-map, "person-x"); } -.bi-person::before { content: map-get($bootstrap-icons-map, "person"); } -.bi-phone-fill::before { content: map-get($bootstrap-icons-map, "phone-fill"); } -.bi-phone-landscape-fill::before { content: map-get($bootstrap-icons-map, "phone-landscape-fill"); } -.bi-phone-landscape::before { content: map-get($bootstrap-icons-map, "phone-landscape"); } -.bi-phone-vibrate-fill::before { content: map-get($bootstrap-icons-map, "phone-vibrate-fill"); } -.bi-phone-vibrate::before { content: map-get($bootstrap-icons-map, "phone-vibrate"); } -.bi-phone::before { content: map-get($bootstrap-icons-map, "phone"); } -.bi-pie-chart-fill::before { content: map-get($bootstrap-icons-map, "pie-chart-fill"); } -.bi-pie-chart::before { content: map-get($bootstrap-icons-map, "pie-chart"); } -.bi-pin-angle-fill::before { content: map-get($bootstrap-icons-map, "pin-angle-fill"); } -.bi-pin-angle::before { content: map-get($bootstrap-icons-map, "pin-angle"); } -.bi-pin-fill::before { content: map-get($bootstrap-icons-map, "pin-fill"); } -.bi-pin::before { content: map-get($bootstrap-icons-map, "pin"); } -.bi-pip-fill::before { content: map-get($bootstrap-icons-map, "pip-fill"); } -.bi-pip::before { content: map-get($bootstrap-icons-map, "pip"); } -.bi-play-btn-fill::before { content: map-get($bootstrap-icons-map, "play-btn-fill"); } -.bi-play-btn::before { content: map-get($bootstrap-icons-map, "play-btn"); } -.bi-play-circle-fill::before { content: map-get($bootstrap-icons-map, "play-circle-fill"); } -.bi-play-circle::before { content: map-get($bootstrap-icons-map, "play-circle"); } -.bi-play-fill::before { content: map-get($bootstrap-icons-map, "play-fill"); } -.bi-play::before { content: map-get($bootstrap-icons-map, "play"); } -.bi-plug-fill::before { content: map-get($bootstrap-icons-map, "plug-fill"); } -.bi-plug::before { content: map-get($bootstrap-icons-map, "plug"); } -.bi-plus-circle-dotted::before { content: map-get($bootstrap-icons-map, "plus-circle-dotted"); } -.bi-plus-circle-fill::before { content: map-get($bootstrap-icons-map, "plus-circle-fill"); } -.bi-plus-circle::before { content: map-get($bootstrap-icons-map, "plus-circle"); } -.bi-plus-square-dotted::before { content: map-get($bootstrap-icons-map, "plus-square-dotted"); } -.bi-plus-square-fill::before { content: map-get($bootstrap-icons-map, "plus-square-fill"); } -.bi-plus-square::before { content: map-get($bootstrap-icons-map, "plus-square"); } -.bi-plus::before { content: map-get($bootstrap-icons-map, "plus"); } -.bi-power::before { content: map-get($bootstrap-icons-map, "power"); } -.bi-printer-fill::before { content: map-get($bootstrap-icons-map, "printer-fill"); } -.bi-printer::before { content: map-get($bootstrap-icons-map, "printer"); } -.bi-puzzle-fill::before { content: map-get($bootstrap-icons-map, "puzzle-fill"); } -.bi-puzzle::before { content: map-get($bootstrap-icons-map, "puzzle"); } -.bi-question-circle-fill::before { content: map-get($bootstrap-icons-map, "question-circle-fill"); } -.bi-question-circle::before { content: map-get($bootstrap-icons-map, "question-circle"); } -.bi-question-diamond-fill::before { content: map-get($bootstrap-icons-map, "question-diamond-fill"); } -.bi-question-diamond::before { content: map-get($bootstrap-icons-map, "question-diamond"); } -.bi-question-octagon-fill::before { content: map-get($bootstrap-icons-map, "question-octagon-fill"); } -.bi-question-octagon::before { content: map-get($bootstrap-icons-map, "question-octagon"); } -.bi-question-square-fill::before { content: map-get($bootstrap-icons-map, "question-square-fill"); } -.bi-question-square::before { content: map-get($bootstrap-icons-map, "question-square"); } -.bi-question::before { content: map-get($bootstrap-icons-map, "question"); } -.bi-rainbow::before { content: map-get($bootstrap-icons-map, "rainbow"); } -.bi-receipt-cutoff::before { content: map-get($bootstrap-icons-map, "receipt-cutoff"); } -.bi-receipt::before { content: map-get($bootstrap-icons-map, "receipt"); } -.bi-reception-0::before { content: map-get($bootstrap-icons-map, "reception-0"); } -.bi-reception-1::before { content: map-get($bootstrap-icons-map, "reception-1"); } -.bi-reception-2::before { content: map-get($bootstrap-icons-map, "reception-2"); } -.bi-reception-3::before { content: map-get($bootstrap-icons-map, "reception-3"); } -.bi-reception-4::before { content: map-get($bootstrap-icons-map, "reception-4"); } -.bi-record-btn-fill::before { content: map-get($bootstrap-icons-map, "record-btn-fill"); } -.bi-record-btn::before { content: map-get($bootstrap-icons-map, "record-btn"); } -.bi-record-circle-fill::before { content: map-get($bootstrap-icons-map, "record-circle-fill"); } -.bi-record-circle::before { content: map-get($bootstrap-icons-map, "record-circle"); } -.bi-record-fill::before { content: map-get($bootstrap-icons-map, "record-fill"); } -.bi-record::before { content: map-get($bootstrap-icons-map, "record"); } -.bi-record2-fill::before { content: map-get($bootstrap-icons-map, "record2-fill"); } -.bi-record2::before { content: map-get($bootstrap-icons-map, "record2"); } -.bi-reply-all-fill::before { content: map-get($bootstrap-icons-map, "reply-all-fill"); } -.bi-reply-all::before { content: map-get($bootstrap-icons-map, "reply-all"); } -.bi-reply-fill::before { content: map-get($bootstrap-icons-map, "reply-fill"); } -.bi-reply::before { content: map-get($bootstrap-icons-map, "reply"); } -.bi-rss-fill::before { content: map-get($bootstrap-icons-map, "rss-fill"); } -.bi-rss::before { content: map-get($bootstrap-icons-map, "rss"); } -.bi-rulers::before { content: map-get($bootstrap-icons-map, "rulers"); } -.bi-save-fill::before { content: map-get($bootstrap-icons-map, "save-fill"); } -.bi-save::before { content: map-get($bootstrap-icons-map, "save"); } -.bi-save2-fill::before { content: map-get($bootstrap-icons-map, "save2-fill"); } -.bi-save2::before { content: map-get($bootstrap-icons-map, "save2"); } -.bi-scissors::before { content: map-get($bootstrap-icons-map, "scissors"); } -.bi-screwdriver::before { content: map-get($bootstrap-icons-map, "screwdriver"); } -.bi-search::before { content: map-get($bootstrap-icons-map, "search"); } -.bi-segmented-nav::before { content: map-get($bootstrap-icons-map, "segmented-nav"); } -.bi-server::before { content: map-get($bootstrap-icons-map, "server"); } -.bi-share-fill::before { content: map-get($bootstrap-icons-map, "share-fill"); } -.bi-share::before { content: map-get($bootstrap-icons-map, "share"); } -.bi-shield-check::before { content: map-get($bootstrap-icons-map, "shield-check"); } -.bi-shield-exclamation::before { content: map-get($bootstrap-icons-map, "shield-exclamation"); } -.bi-shield-fill-check::before { content: map-get($bootstrap-icons-map, "shield-fill-check"); } -.bi-shield-fill-exclamation::before { content: map-get($bootstrap-icons-map, "shield-fill-exclamation"); } -.bi-shield-fill-minus::before { content: map-get($bootstrap-icons-map, "shield-fill-minus"); } -.bi-shield-fill-plus::before { content: map-get($bootstrap-icons-map, "shield-fill-plus"); } -.bi-shield-fill-x::before { content: map-get($bootstrap-icons-map, "shield-fill-x"); } -.bi-shield-fill::before { content: map-get($bootstrap-icons-map, "shield-fill"); } -.bi-shield-lock-fill::before { content: map-get($bootstrap-icons-map, "shield-lock-fill"); } -.bi-shield-lock::before { content: map-get($bootstrap-icons-map, "shield-lock"); } -.bi-shield-minus::before { content: map-get($bootstrap-icons-map, "shield-minus"); } -.bi-shield-plus::before { content: map-get($bootstrap-icons-map, "shield-plus"); } -.bi-shield-shaded::before { content: map-get($bootstrap-icons-map, "shield-shaded"); } -.bi-shield-slash-fill::before { content: map-get($bootstrap-icons-map, "shield-slash-fill"); } -.bi-shield-slash::before { content: map-get($bootstrap-icons-map, "shield-slash"); } -.bi-shield-x::before { content: map-get($bootstrap-icons-map, "shield-x"); } -.bi-shield::before { content: map-get($bootstrap-icons-map, "shield"); } -.bi-shift-fill::before { content: map-get($bootstrap-icons-map, "shift-fill"); } -.bi-shift::before { content: map-get($bootstrap-icons-map, "shift"); } -.bi-shop-window::before { content: map-get($bootstrap-icons-map, "shop-window"); } -.bi-shop::before { content: map-get($bootstrap-icons-map, "shop"); } -.bi-shuffle::before { content: map-get($bootstrap-icons-map, "shuffle"); } -.bi-signpost-2-fill::before { content: map-get($bootstrap-icons-map, "signpost-2-fill"); } -.bi-signpost-2::before { content: map-get($bootstrap-icons-map, "signpost-2"); } -.bi-signpost-fill::before { content: map-get($bootstrap-icons-map, "signpost-fill"); } -.bi-signpost-split-fill::before { content: map-get($bootstrap-icons-map, "signpost-split-fill"); } -.bi-signpost-split::before { content: map-get($bootstrap-icons-map, "signpost-split"); } -.bi-signpost::before { content: map-get($bootstrap-icons-map, "signpost"); } -.bi-sim-fill::before { content: map-get($bootstrap-icons-map, "sim-fill"); } -.bi-sim::before { content: map-get($bootstrap-icons-map, "sim"); } -.bi-skip-backward-btn-fill::before { content: map-get($bootstrap-icons-map, "skip-backward-btn-fill"); } -.bi-skip-backward-btn::before { content: map-get($bootstrap-icons-map, "skip-backward-btn"); } -.bi-skip-backward-circle-fill::before { content: map-get($bootstrap-icons-map, "skip-backward-circle-fill"); } -.bi-skip-backward-circle::before { content: map-get($bootstrap-icons-map, "skip-backward-circle"); } -.bi-skip-backward-fill::before { content: map-get($bootstrap-icons-map, "skip-backward-fill"); } -.bi-skip-backward::before { content: map-get($bootstrap-icons-map, "skip-backward"); } -.bi-skip-end-btn-fill::before { content: map-get($bootstrap-icons-map, "skip-end-btn-fill"); } -.bi-skip-end-btn::before { content: map-get($bootstrap-icons-map, "skip-end-btn"); } -.bi-skip-end-circle-fill::before { content: map-get($bootstrap-icons-map, "skip-end-circle-fill"); } -.bi-skip-end-circle::before { content: map-get($bootstrap-icons-map, "skip-end-circle"); } -.bi-skip-end-fill::before { content: map-get($bootstrap-icons-map, "skip-end-fill"); } -.bi-skip-end::before { content: map-get($bootstrap-icons-map, "skip-end"); } -.bi-skip-forward-btn-fill::before { content: map-get($bootstrap-icons-map, "skip-forward-btn-fill"); } -.bi-skip-forward-btn::before { content: map-get($bootstrap-icons-map, "skip-forward-btn"); } -.bi-skip-forward-circle-fill::before { content: map-get($bootstrap-icons-map, "skip-forward-circle-fill"); } -.bi-skip-forward-circle::before { content: map-get($bootstrap-icons-map, "skip-forward-circle"); } -.bi-skip-forward-fill::before { content: map-get($bootstrap-icons-map, "skip-forward-fill"); } -.bi-skip-forward::before { content: map-get($bootstrap-icons-map, "skip-forward"); } -.bi-skip-start-btn-fill::before { content: map-get($bootstrap-icons-map, "skip-start-btn-fill"); } -.bi-skip-start-btn::before { content: map-get($bootstrap-icons-map, "skip-start-btn"); } -.bi-skip-start-circle-fill::before { content: map-get($bootstrap-icons-map, "skip-start-circle-fill"); } -.bi-skip-start-circle::before { content: map-get($bootstrap-icons-map, "skip-start-circle"); } -.bi-skip-start-fill::before { content: map-get($bootstrap-icons-map, "skip-start-fill"); } -.bi-skip-start::before { content: map-get($bootstrap-icons-map, "skip-start"); } -.bi-slack::before { content: map-get($bootstrap-icons-map, "slack"); } -.bi-slash-circle-fill::before { content: map-get($bootstrap-icons-map, "slash-circle-fill"); } -.bi-slash-circle::before { content: map-get($bootstrap-icons-map, "slash-circle"); } -.bi-slash-square-fill::before { content: map-get($bootstrap-icons-map, "slash-square-fill"); } -.bi-slash-square::before { content: map-get($bootstrap-icons-map, "slash-square"); } -.bi-slash::before { content: map-get($bootstrap-icons-map, "slash"); } -.bi-sliders::before { content: map-get($bootstrap-icons-map, "sliders"); } -.bi-smartwatch::before { content: map-get($bootstrap-icons-map, "smartwatch"); } -.bi-snow::before { content: map-get($bootstrap-icons-map, "snow"); } -.bi-snow2::before { content: map-get($bootstrap-icons-map, "snow2"); } -.bi-snow3::before { content: map-get($bootstrap-icons-map, "snow3"); } -.bi-sort-alpha-down-alt::before { content: map-get($bootstrap-icons-map, "sort-alpha-down-alt"); } -.bi-sort-alpha-down::before { content: map-get($bootstrap-icons-map, "sort-alpha-down"); } -.bi-sort-alpha-up-alt::before { content: map-get($bootstrap-icons-map, "sort-alpha-up-alt"); } -.bi-sort-alpha-up::before { content: map-get($bootstrap-icons-map, "sort-alpha-up"); } -.bi-sort-down-alt::before { content: map-get($bootstrap-icons-map, "sort-down-alt"); } -.bi-sort-down::before { content: map-get($bootstrap-icons-map, "sort-down"); } -.bi-sort-numeric-down-alt::before { content: map-get($bootstrap-icons-map, "sort-numeric-down-alt"); } -.bi-sort-numeric-down::before { content: map-get($bootstrap-icons-map, "sort-numeric-down"); } -.bi-sort-numeric-up-alt::before { content: map-get($bootstrap-icons-map, "sort-numeric-up-alt"); } -.bi-sort-numeric-up::before { content: map-get($bootstrap-icons-map, "sort-numeric-up"); } -.bi-sort-up-alt::before { content: map-get($bootstrap-icons-map, "sort-up-alt"); } -.bi-sort-up::before { content: map-get($bootstrap-icons-map, "sort-up"); } -.bi-soundwave::before { content: map-get($bootstrap-icons-map, "soundwave"); } -.bi-speaker-fill::before { content: map-get($bootstrap-icons-map, "speaker-fill"); } -.bi-speaker::before { content: map-get($bootstrap-icons-map, "speaker"); } -.bi-speedometer::before { content: map-get($bootstrap-icons-map, "speedometer"); } -.bi-speedometer2::before { content: map-get($bootstrap-icons-map, "speedometer2"); } -.bi-spellcheck::before { content: map-get($bootstrap-icons-map, "spellcheck"); } -.bi-square-fill::before { content: map-get($bootstrap-icons-map, "square-fill"); } -.bi-square-half::before { content: map-get($bootstrap-icons-map, "square-half"); } -.bi-square::before { content: map-get($bootstrap-icons-map, "square"); } -.bi-stack::before { content: map-get($bootstrap-icons-map, "stack"); } -.bi-star-fill::before { content: map-get($bootstrap-icons-map, "star-fill"); } -.bi-star-half::before { content: map-get($bootstrap-icons-map, "star-half"); } -.bi-star::before { content: map-get($bootstrap-icons-map, "star"); } -.bi-stars::before { content: map-get($bootstrap-icons-map, "stars"); } -.bi-stickies-fill::before { content: map-get($bootstrap-icons-map, "stickies-fill"); } -.bi-stickies::before { content: map-get($bootstrap-icons-map, "stickies"); } -.bi-sticky-fill::before { content: map-get($bootstrap-icons-map, "sticky-fill"); } -.bi-sticky::before { content: map-get($bootstrap-icons-map, "sticky"); } -.bi-stop-btn-fill::before { content: map-get($bootstrap-icons-map, "stop-btn-fill"); } -.bi-stop-btn::before { content: map-get($bootstrap-icons-map, "stop-btn"); } -.bi-stop-circle-fill::before { content: map-get($bootstrap-icons-map, "stop-circle-fill"); } -.bi-stop-circle::before { content: map-get($bootstrap-icons-map, "stop-circle"); } -.bi-stop-fill::before { content: map-get($bootstrap-icons-map, "stop-fill"); } -.bi-stop::before { content: map-get($bootstrap-icons-map, "stop"); } -.bi-stoplights-fill::before { content: map-get($bootstrap-icons-map, "stoplights-fill"); } -.bi-stoplights::before { content: map-get($bootstrap-icons-map, "stoplights"); } -.bi-stopwatch-fill::before { content: map-get($bootstrap-icons-map, "stopwatch-fill"); } -.bi-stopwatch::before { content: map-get($bootstrap-icons-map, "stopwatch"); } -.bi-subtract::before { content: map-get($bootstrap-icons-map, "subtract"); } -.bi-suit-club-fill::before { content: map-get($bootstrap-icons-map, "suit-club-fill"); } -.bi-suit-club::before { content: map-get($bootstrap-icons-map, "suit-club"); } -.bi-suit-diamond-fill::before { content: map-get($bootstrap-icons-map, "suit-diamond-fill"); } -.bi-suit-diamond::before { content: map-get($bootstrap-icons-map, "suit-diamond"); } -.bi-suit-heart-fill::before { content: map-get($bootstrap-icons-map, "suit-heart-fill"); } -.bi-suit-heart::before { content: map-get($bootstrap-icons-map, "suit-heart"); } -.bi-suit-spade-fill::before { content: map-get($bootstrap-icons-map, "suit-spade-fill"); } -.bi-suit-spade::before { content: map-get($bootstrap-icons-map, "suit-spade"); } -.bi-sun-fill::before { content: map-get($bootstrap-icons-map, "sun-fill"); } -.bi-sun::before { content: map-get($bootstrap-icons-map, "sun"); } -.bi-sunglasses::before { content: map-get($bootstrap-icons-map, "sunglasses"); } -.bi-sunrise-fill::before { content: map-get($bootstrap-icons-map, "sunrise-fill"); } -.bi-sunrise::before { content: map-get($bootstrap-icons-map, "sunrise"); } -.bi-sunset-fill::before { content: map-get($bootstrap-icons-map, "sunset-fill"); } -.bi-sunset::before { content: map-get($bootstrap-icons-map, "sunset"); } -.bi-symmetry-horizontal::before { content: map-get($bootstrap-icons-map, "symmetry-horizontal"); } -.bi-symmetry-vertical::before { content: map-get($bootstrap-icons-map, "symmetry-vertical"); } -.bi-table::before { content: map-get($bootstrap-icons-map, "table"); } -.bi-tablet-fill::before { content: map-get($bootstrap-icons-map, "tablet-fill"); } -.bi-tablet-landscape-fill::before { content: map-get($bootstrap-icons-map, "tablet-landscape-fill"); } -.bi-tablet-landscape::before { content: map-get($bootstrap-icons-map, "tablet-landscape"); } -.bi-tablet::before { content: map-get($bootstrap-icons-map, "tablet"); } -.bi-tag-fill::before { content: map-get($bootstrap-icons-map, "tag-fill"); } -.bi-tag::before { content: map-get($bootstrap-icons-map, "tag"); } -.bi-tags-fill::before { content: map-get($bootstrap-icons-map, "tags-fill"); } -.bi-tags::before { content: map-get($bootstrap-icons-map, "tags"); } -.bi-telegram::before { content: map-get($bootstrap-icons-map, "telegram"); } -.bi-telephone-fill::before { content: map-get($bootstrap-icons-map, "telephone-fill"); } -.bi-telephone-forward-fill::before { content: map-get($bootstrap-icons-map, "telephone-forward-fill"); } -.bi-telephone-forward::before { content: map-get($bootstrap-icons-map, "telephone-forward"); } -.bi-telephone-inbound-fill::before { content: map-get($bootstrap-icons-map, "telephone-inbound-fill"); } -.bi-telephone-inbound::before { content: map-get($bootstrap-icons-map, "telephone-inbound"); } -.bi-telephone-minus-fill::before { content: map-get($bootstrap-icons-map, "telephone-minus-fill"); } -.bi-telephone-minus::before { content: map-get($bootstrap-icons-map, "telephone-minus"); } -.bi-telephone-outbound-fill::before { content: map-get($bootstrap-icons-map, "telephone-outbound-fill"); } -.bi-telephone-outbound::before { content: map-get($bootstrap-icons-map, "telephone-outbound"); } -.bi-telephone-plus-fill::before { content: map-get($bootstrap-icons-map, "telephone-plus-fill"); } -.bi-telephone-plus::before { content: map-get($bootstrap-icons-map, "telephone-plus"); } -.bi-telephone-x-fill::before { content: map-get($bootstrap-icons-map, "telephone-x-fill"); } -.bi-telephone-x::before { content: map-get($bootstrap-icons-map, "telephone-x"); } -.bi-telephone::before { content: map-get($bootstrap-icons-map, "telephone"); } -.bi-terminal-fill::before { content: map-get($bootstrap-icons-map, "terminal-fill"); } -.bi-terminal::before { content: map-get($bootstrap-icons-map, "terminal"); } -.bi-text-center::before { content: map-get($bootstrap-icons-map, "text-center"); } -.bi-text-indent-left::before { content: map-get($bootstrap-icons-map, "text-indent-left"); } -.bi-text-indent-right::before { content: map-get($bootstrap-icons-map, "text-indent-right"); } -.bi-text-left::before { content: map-get($bootstrap-icons-map, "text-left"); } -.bi-text-paragraph::before { content: map-get($bootstrap-icons-map, "text-paragraph"); } -.bi-text-right::before { content: map-get($bootstrap-icons-map, "text-right"); } -.bi-textarea-resize::before { content: map-get($bootstrap-icons-map, "textarea-resize"); } -.bi-textarea-t::before { content: map-get($bootstrap-icons-map, "textarea-t"); } -.bi-textarea::before { content: map-get($bootstrap-icons-map, "textarea"); } -.bi-thermometer-half::before { content: map-get($bootstrap-icons-map, "thermometer-half"); } -.bi-thermometer-high::before { content: map-get($bootstrap-icons-map, "thermometer-high"); } -.bi-thermometer-low::before { content: map-get($bootstrap-icons-map, "thermometer-low"); } -.bi-thermometer-snow::before { content: map-get($bootstrap-icons-map, "thermometer-snow"); } -.bi-thermometer-sun::before { content: map-get($bootstrap-icons-map, "thermometer-sun"); } -.bi-thermometer::before { content: map-get($bootstrap-icons-map, "thermometer"); } -.bi-three-dots-vertical::before { content: map-get($bootstrap-icons-map, "three-dots-vertical"); } -.bi-three-dots::before { content: map-get($bootstrap-icons-map, "three-dots"); } -.bi-toggle-off::before { content: map-get($bootstrap-icons-map, "toggle-off"); } -.bi-toggle-on::before { content: map-get($bootstrap-icons-map, "toggle-on"); } -.bi-toggle2-off::before { content: map-get($bootstrap-icons-map, "toggle2-off"); } -.bi-toggle2-on::before { content: map-get($bootstrap-icons-map, "toggle2-on"); } -.bi-toggles::before { content: map-get($bootstrap-icons-map, "toggles"); } -.bi-toggles2::before { content: map-get($bootstrap-icons-map, "toggles2"); } -.bi-tools::before { content: map-get($bootstrap-icons-map, "tools"); } -.bi-tornado::before { content: map-get($bootstrap-icons-map, "tornado"); } -.bi-trash-fill::before { content: map-get($bootstrap-icons-map, "trash-fill"); } -.bi-trash::before { content: map-get($bootstrap-icons-map, "trash"); } -.bi-trash2-fill::before { content: map-get($bootstrap-icons-map, "trash2-fill"); } -.bi-trash2::before { content: map-get($bootstrap-icons-map, "trash2"); } -.bi-tree-fill::before { content: map-get($bootstrap-icons-map, "tree-fill"); } -.bi-tree::before { content: map-get($bootstrap-icons-map, "tree"); } -.bi-triangle-fill::before { content: map-get($bootstrap-icons-map, "triangle-fill"); } -.bi-triangle-half::before { content: map-get($bootstrap-icons-map, "triangle-half"); } -.bi-triangle::before { content: map-get($bootstrap-icons-map, "triangle"); } -.bi-trophy-fill::before { content: map-get($bootstrap-icons-map, "trophy-fill"); } -.bi-trophy::before { content: map-get($bootstrap-icons-map, "trophy"); } -.bi-tropical-storm::before { content: map-get($bootstrap-icons-map, "tropical-storm"); } -.bi-truck-flatbed::before { content: map-get($bootstrap-icons-map, "truck-flatbed"); } -.bi-truck::before { content: map-get($bootstrap-icons-map, "truck"); } -.bi-tsunami::before { content: map-get($bootstrap-icons-map, "tsunami"); } -.bi-tv-fill::before { content: map-get($bootstrap-icons-map, "tv-fill"); } -.bi-tv::before { content: map-get($bootstrap-icons-map, "tv"); } -.bi-twitch::before { content: map-get($bootstrap-icons-map, "twitch"); } -.bi-twitter::before { content: map-get($bootstrap-icons-map, "twitter"); } -.bi-type-bold::before { content: map-get($bootstrap-icons-map, "type-bold"); } -.bi-type-h1::before { content: map-get($bootstrap-icons-map, "type-h1"); } -.bi-type-h2::before { content: map-get($bootstrap-icons-map, "type-h2"); } -.bi-type-h3::before { content: map-get($bootstrap-icons-map, "type-h3"); } -.bi-type-italic::before { content: map-get($bootstrap-icons-map, "type-italic"); } -.bi-type-strikethrough::before { content: map-get($bootstrap-icons-map, "type-strikethrough"); } -.bi-type-underline::before { content: map-get($bootstrap-icons-map, "type-underline"); } -.bi-type::before { content: map-get($bootstrap-icons-map, "type"); } -.bi-ui-checks-grid::before { content: map-get($bootstrap-icons-map, "ui-checks-grid"); } -.bi-ui-checks::before { content: map-get($bootstrap-icons-map, "ui-checks"); } -.bi-ui-radios-grid::before { content: map-get($bootstrap-icons-map, "ui-radios-grid"); } -.bi-ui-radios::before { content: map-get($bootstrap-icons-map, "ui-radios"); } -.bi-umbrella-fill::before { content: map-get($bootstrap-icons-map, "umbrella-fill"); } -.bi-umbrella::before { content: map-get($bootstrap-icons-map, "umbrella"); } -.bi-union::before { content: map-get($bootstrap-icons-map, "union"); } -.bi-unlock-fill::before { content: map-get($bootstrap-icons-map, "unlock-fill"); } -.bi-unlock::before { content: map-get($bootstrap-icons-map, "unlock"); } -.bi-upc-scan::before { content: map-get($bootstrap-icons-map, "upc-scan"); } -.bi-upc::before { content: map-get($bootstrap-icons-map, "upc"); } -.bi-upload::before { content: map-get($bootstrap-icons-map, "upload"); } -.bi-vector-pen::before { content: map-get($bootstrap-icons-map, "vector-pen"); } -.bi-view-list::before { content: map-get($bootstrap-icons-map, "view-list"); } -.bi-view-stacked::before { content: map-get($bootstrap-icons-map, "view-stacked"); } -.bi-vinyl-fill::before { content: map-get($bootstrap-icons-map, "vinyl-fill"); } -.bi-vinyl::before { content: map-get($bootstrap-icons-map, "vinyl"); } -.bi-voicemail::before { content: map-get($bootstrap-icons-map, "voicemail"); } -.bi-volume-down-fill::before { content: map-get($bootstrap-icons-map, "volume-down-fill"); } -.bi-volume-down::before { content: map-get($bootstrap-icons-map, "volume-down"); } -.bi-volume-mute-fill::before { content: map-get($bootstrap-icons-map, "volume-mute-fill"); } -.bi-volume-mute::before { content: map-get($bootstrap-icons-map, "volume-mute"); } -.bi-volume-off-fill::before { content: map-get($bootstrap-icons-map, "volume-off-fill"); } -.bi-volume-off::before { content: map-get($bootstrap-icons-map, "volume-off"); } -.bi-volume-up-fill::before { content: map-get($bootstrap-icons-map, "volume-up-fill"); } -.bi-volume-up::before { content: map-get($bootstrap-icons-map, "volume-up"); } -.bi-vr::before { content: map-get($bootstrap-icons-map, "vr"); } -.bi-wallet-fill::before { content: map-get($bootstrap-icons-map, "wallet-fill"); } -.bi-wallet::before { content: map-get($bootstrap-icons-map, "wallet"); } -.bi-wallet2::before { content: map-get($bootstrap-icons-map, "wallet2"); } -.bi-watch::before { content: map-get($bootstrap-icons-map, "watch"); } -.bi-water::before { content: map-get($bootstrap-icons-map, "water"); } -.bi-whatsapp::before { content: map-get($bootstrap-icons-map, "whatsapp"); } -.bi-wifi-1::before { content: map-get($bootstrap-icons-map, "wifi-1"); } -.bi-wifi-2::before { content: map-get($bootstrap-icons-map, "wifi-2"); } -.bi-wifi-off::before { content: map-get($bootstrap-icons-map, "wifi-off"); } -.bi-wifi::before { content: map-get($bootstrap-icons-map, "wifi"); } -.bi-wind::before { content: map-get($bootstrap-icons-map, "wind"); } -.bi-window-dock::before { content: map-get($bootstrap-icons-map, "window-dock"); } -.bi-window-sidebar::before { content: map-get($bootstrap-icons-map, "window-sidebar"); } -.bi-window::before { content: map-get($bootstrap-icons-map, "window"); } -.bi-wrench::before { content: map-get($bootstrap-icons-map, "wrench"); } -.bi-x-circle-fill::before { content: map-get($bootstrap-icons-map, "x-circle-fill"); } -.bi-x-circle::before { content: map-get($bootstrap-icons-map, "x-circle"); } -.bi-x-diamond-fill::before { content: map-get($bootstrap-icons-map, "x-diamond-fill"); } -.bi-x-diamond::before { content: map-get($bootstrap-icons-map, "x-diamond"); } -.bi-x-octagon-fill::before { content: map-get($bootstrap-icons-map, "x-octagon-fill"); } -.bi-x-octagon::before { content: map-get($bootstrap-icons-map, "x-octagon"); } -.bi-x-square-fill::before { content: map-get($bootstrap-icons-map, "x-square-fill"); } -.bi-x-square::before { content: map-get($bootstrap-icons-map, "x-square"); } -.bi-x::before { content: map-get($bootstrap-icons-map, "x"); } -.bi-youtube::before { content: map-get($bootstrap-icons-map, "youtube"); } -.bi-zoom-in::before { content: map-get($bootstrap-icons-map, "zoom-in"); } -.bi-zoom-out::before { content: map-get($bootstrap-icons-map, "zoom-out"); } -.bi-bank::before { content: map-get($bootstrap-icons-map, "bank"); } -.bi-bank2::before { content: map-get($bootstrap-icons-map, "bank2"); } -.bi-bell-slash-fill::before { content: map-get($bootstrap-icons-map, "bell-slash-fill"); } -.bi-bell-slash::before { content: map-get($bootstrap-icons-map, "bell-slash"); } -.bi-cash-coin::before { content: map-get($bootstrap-icons-map, "cash-coin"); } -.bi-check-lg::before { content: map-get($bootstrap-icons-map, "check-lg"); } -.bi-coin::before { content: map-get($bootstrap-icons-map, "coin"); } -.bi-currency-bitcoin::before { content: map-get($bootstrap-icons-map, "currency-bitcoin"); } -.bi-currency-dollar::before { content: map-get($bootstrap-icons-map, "currency-dollar"); } -.bi-currency-euro::before { content: map-get($bootstrap-icons-map, "currency-euro"); } -.bi-currency-exchange::before { content: map-get($bootstrap-icons-map, "currency-exchange"); } -.bi-currency-pound::before { content: map-get($bootstrap-icons-map, "currency-pound"); } -.bi-currency-yen::before { content: map-get($bootstrap-icons-map, "currency-yen"); } -.bi-dash-lg::before { content: map-get($bootstrap-icons-map, "dash-lg"); } -.bi-exclamation-lg::before { content: map-get($bootstrap-icons-map, "exclamation-lg"); } -.bi-file-earmark-pdf-fill::before { content: map-get($bootstrap-icons-map, "file-earmark-pdf-fill"); } -.bi-file-earmark-pdf::before { content: map-get($bootstrap-icons-map, "file-earmark-pdf"); } -.bi-file-pdf-fill::before { content: map-get($bootstrap-icons-map, "file-pdf-fill"); } -.bi-file-pdf::before { content: map-get($bootstrap-icons-map, "file-pdf"); } -.bi-gender-ambiguous::before { content: map-get($bootstrap-icons-map, "gender-ambiguous"); } -.bi-gender-female::before { content: map-get($bootstrap-icons-map, "gender-female"); } -.bi-gender-male::before { content: map-get($bootstrap-icons-map, "gender-male"); } -.bi-gender-trans::before { content: map-get($bootstrap-icons-map, "gender-trans"); } -.bi-headset-vr::before { content: map-get($bootstrap-icons-map, "headset-vr"); } -.bi-info-lg::before { content: map-get($bootstrap-icons-map, "info-lg"); } -.bi-mastodon::before { content: map-get($bootstrap-icons-map, "mastodon"); } -.bi-messenger::before { content: map-get($bootstrap-icons-map, "messenger"); } -.bi-piggy-bank-fill::before { content: map-get($bootstrap-icons-map, "piggy-bank-fill"); } -.bi-piggy-bank::before { content: map-get($bootstrap-icons-map, "piggy-bank"); } -.bi-pin-map-fill::before { content: map-get($bootstrap-icons-map, "pin-map-fill"); } -.bi-pin-map::before { content: map-get($bootstrap-icons-map, "pin-map"); } -.bi-plus-lg::before { content: map-get($bootstrap-icons-map, "plus-lg"); } -.bi-question-lg::before { content: map-get($bootstrap-icons-map, "question-lg"); } -.bi-recycle::before { content: map-get($bootstrap-icons-map, "recycle"); } -.bi-reddit::before { content: map-get($bootstrap-icons-map, "reddit"); } -.bi-safe-fill::before { content: map-get($bootstrap-icons-map, "safe-fill"); } -.bi-safe2-fill::before { content: map-get($bootstrap-icons-map, "safe2-fill"); } -.bi-safe2::before { content: map-get($bootstrap-icons-map, "safe2"); } -.bi-sd-card-fill::before { content: map-get($bootstrap-icons-map, "sd-card-fill"); } -.bi-sd-card::before { content: map-get($bootstrap-icons-map, "sd-card"); } -.bi-skype::before { content: map-get($bootstrap-icons-map, "skype"); } -.bi-slash-lg::before { content: map-get($bootstrap-icons-map, "slash-lg"); } -.bi-translate::before { content: map-get($bootstrap-icons-map, "translate"); } -.bi-x-lg::before { content: map-get($bootstrap-icons-map, "x-lg"); } -.bi-safe::before { content: map-get($bootstrap-icons-map, "safe"); } -.bi-apple::before { content: map-get($bootstrap-icons-map, "apple"); } -.bi-microsoft::before { content: map-get($bootstrap-icons-map, "microsoft"); } -.bi-windows::before { content: map-get($bootstrap-icons-map, "windows"); } -.bi-behance::before { content: map-get($bootstrap-icons-map, "behance"); } -.bi-dribbble::before { content: map-get($bootstrap-icons-map, "dribbble"); } -.bi-line::before { content: map-get($bootstrap-icons-map, "line"); } -.bi-medium::before { content: map-get($bootstrap-icons-map, "medium"); } -.bi-paypal::before { content: map-get($bootstrap-icons-map, "paypal"); } -.bi-pinterest::before { content: map-get($bootstrap-icons-map, "pinterest"); } -.bi-signal::before { content: map-get($bootstrap-icons-map, "signal"); } -.bi-snapchat::before { content: map-get($bootstrap-icons-map, "snapchat"); } -.bi-spotify::before { content: map-get($bootstrap-icons-map, "spotify"); } -.bi-stack-overflow::before { content: map-get($bootstrap-icons-map, "stack-overflow"); } -.bi-strava::before { content: map-get($bootstrap-icons-map, "strava"); } -.bi-wordpress::before { content: map-get($bootstrap-icons-map, "wordpress"); } -.bi-vimeo::before { content: map-get($bootstrap-icons-map, "vimeo"); } -.bi-activity::before { content: map-get($bootstrap-icons-map, "activity"); } -.bi-easel2-fill::before { content: map-get($bootstrap-icons-map, "easel2-fill"); } -.bi-easel2::before { content: map-get($bootstrap-icons-map, "easel2"); } -.bi-easel3-fill::before { content: map-get($bootstrap-icons-map, "easel3-fill"); } -.bi-easel3::before { content: map-get($bootstrap-icons-map, "easel3"); } -.bi-fan::before { content: map-get($bootstrap-icons-map, "fan"); } -.bi-fingerprint::before { content: map-get($bootstrap-icons-map, "fingerprint"); } -.bi-graph-down-arrow::before { content: map-get($bootstrap-icons-map, "graph-down-arrow"); } -.bi-graph-up-arrow::before { content: map-get($bootstrap-icons-map, "graph-up-arrow"); } -.bi-hypnotize::before { content: map-get($bootstrap-icons-map, "hypnotize"); } -.bi-magic::before { content: map-get($bootstrap-icons-map, "magic"); } -.bi-person-rolodex::before { content: map-get($bootstrap-icons-map, "person-rolodex"); } -.bi-person-video::before { content: map-get($bootstrap-icons-map, "person-video"); } -.bi-person-video2::before { content: map-get($bootstrap-icons-map, "person-video2"); } -.bi-person-video3::before { content: map-get($bootstrap-icons-map, "person-video3"); } -.bi-person-workspace::before { content: map-get($bootstrap-icons-map, "person-workspace"); } -.bi-radioactive::before { content: map-get($bootstrap-icons-map, "radioactive"); } -.bi-webcam-fill::before { content: map-get($bootstrap-icons-map, "webcam-fill"); } -.bi-webcam::before { content: map-get($bootstrap-icons-map, "webcam"); } -.bi-yin-yang::before { content: map-get($bootstrap-icons-map, "yin-yang"); } -.bi-bandaid-fill::before { content: map-get($bootstrap-icons-map, "bandaid-fill"); } -.bi-bandaid::before { content: map-get($bootstrap-icons-map, "bandaid"); } -.bi-bluetooth::before { content: map-get($bootstrap-icons-map, "bluetooth"); } -.bi-body-text::before { content: map-get($bootstrap-icons-map, "body-text"); } -.bi-boombox::before { content: map-get($bootstrap-icons-map, "boombox"); } -.bi-boxes::before { content: map-get($bootstrap-icons-map, "boxes"); } -.bi-dpad-fill::before { content: map-get($bootstrap-icons-map, "dpad-fill"); } -.bi-dpad::before { content: map-get($bootstrap-icons-map, "dpad"); } -.bi-ear-fill::before { content: map-get($bootstrap-icons-map, "ear-fill"); } -.bi-ear::before { content: map-get($bootstrap-icons-map, "ear"); } -.bi-envelope-check-1::before { content: map-get($bootstrap-icons-map, "envelope-check-1"); } -.bi-envelope-check-fill::before { content: map-get($bootstrap-icons-map, "envelope-check-fill"); } -.bi-envelope-check::before { content: map-get($bootstrap-icons-map, "envelope-check"); } -.bi-envelope-dash-1::before { content: map-get($bootstrap-icons-map, "envelope-dash-1"); } -.bi-envelope-dash-fill::before { content: map-get($bootstrap-icons-map, "envelope-dash-fill"); } -.bi-envelope-dash::before { content: map-get($bootstrap-icons-map, "envelope-dash"); } -.bi-envelope-exclamation-1::before { content: map-get($bootstrap-icons-map, "envelope-exclamation-1"); } -.bi-envelope-exclamation-fill::before { content: map-get($bootstrap-icons-map, "envelope-exclamation-fill"); } -.bi-envelope-exclamation::before { content: map-get($bootstrap-icons-map, "envelope-exclamation"); } -.bi-envelope-plus-fill::before { content: map-get($bootstrap-icons-map, "envelope-plus-fill"); } -.bi-envelope-plus::before { content: map-get($bootstrap-icons-map, "envelope-plus"); } -.bi-envelope-slash-1::before { content: map-get($bootstrap-icons-map, "envelope-slash-1"); } -.bi-envelope-slash-fill::before { content: map-get($bootstrap-icons-map, "envelope-slash-fill"); } -.bi-envelope-slash::before { content: map-get($bootstrap-icons-map, "envelope-slash"); } -.bi-envelope-x-1::before { content: map-get($bootstrap-icons-map, "envelope-x-1"); } -.bi-envelope-x-fill::before { content: map-get($bootstrap-icons-map, "envelope-x-fill"); } -.bi-envelope-x::before { content: map-get($bootstrap-icons-map, "envelope-x"); } -.bi-explicit-fill::before { content: map-get($bootstrap-icons-map, "explicit-fill"); } -.bi-explicit::before { content: map-get($bootstrap-icons-map, "explicit"); } -.bi-git::before { content: map-get($bootstrap-icons-map, "git"); } -.bi-infinity::before { content: map-get($bootstrap-icons-map, "infinity"); } -.bi-list-columns-reverse::before { content: map-get($bootstrap-icons-map, "list-columns-reverse"); } -.bi-list-columns::before { content: map-get($bootstrap-icons-map, "list-columns"); } -.bi-meta::before { content: map-get($bootstrap-icons-map, "meta"); } -.bi-mortorboard-fill::before { content: map-get($bootstrap-icons-map, "mortorboard-fill"); } -.bi-mortorboard::before { content: map-get($bootstrap-icons-map, "mortorboard"); } -.bi-nintendo-switch::before { content: map-get($bootstrap-icons-map, "nintendo-switch"); } -.bi-pc-display-horizontal::before { content: map-get($bootstrap-icons-map, "pc-display-horizontal"); } -.bi-pc-display::before { content: map-get($bootstrap-icons-map, "pc-display"); } -.bi-pc-horizontal::before { content: map-get($bootstrap-icons-map, "pc-horizontal"); } -.bi-pc::before { content: map-get($bootstrap-icons-map, "pc"); } -.bi-playstation::before { content: map-get($bootstrap-icons-map, "playstation"); } -.bi-plus-slash-minus::before { content: map-get($bootstrap-icons-map, "plus-slash-minus"); } -.bi-projector-fill::before { content: map-get($bootstrap-icons-map, "projector-fill"); } -.bi-projector::before { content: map-get($bootstrap-icons-map, "projector"); } -.bi-qr-code-scan::before { content: map-get($bootstrap-icons-map, "qr-code-scan"); } -.bi-qr-code::before { content: map-get($bootstrap-icons-map, "qr-code"); } -.bi-quora::before { content: map-get($bootstrap-icons-map, "quora"); } -.bi-quote::before { content: map-get($bootstrap-icons-map, "quote"); } -.bi-robot::before { content: map-get($bootstrap-icons-map, "robot"); } -.bi-send-check-fill::before { content: map-get($bootstrap-icons-map, "send-check-fill"); } -.bi-send-check::before { content: map-get($bootstrap-icons-map, "send-check"); } -.bi-send-dash-fill::before { content: map-get($bootstrap-icons-map, "send-dash-fill"); } -.bi-send-dash::before { content: map-get($bootstrap-icons-map, "send-dash"); } -.bi-send-exclamation-1::before { content: map-get($bootstrap-icons-map, "send-exclamation-1"); } -.bi-send-exclamation-fill::before { content: map-get($bootstrap-icons-map, "send-exclamation-fill"); } -.bi-send-exclamation::before { content: map-get($bootstrap-icons-map, "send-exclamation"); } -.bi-send-fill::before { content: map-get($bootstrap-icons-map, "send-fill"); } -.bi-send-plus-fill::before { content: map-get($bootstrap-icons-map, "send-plus-fill"); } -.bi-send-plus::before { content: map-get($bootstrap-icons-map, "send-plus"); } -.bi-send-slash-fill::before { content: map-get($bootstrap-icons-map, "send-slash-fill"); } -.bi-send-slash::before { content: map-get($bootstrap-icons-map, "send-slash"); } -.bi-send-x-fill::before { content: map-get($bootstrap-icons-map, "send-x-fill"); } -.bi-send-x::before { content: map-get($bootstrap-icons-map, "send-x"); } -.bi-send::before { content: map-get($bootstrap-icons-map, "send"); } -.bi-steam::before { content: map-get($bootstrap-icons-map, "steam"); } -.bi-terminal-dash-1::before { content: map-get($bootstrap-icons-map, "terminal-dash-1"); } -.bi-terminal-dash::before { content: map-get($bootstrap-icons-map, "terminal-dash"); } -.bi-terminal-plus::before { content: map-get($bootstrap-icons-map, "terminal-plus"); } -.bi-terminal-split::before { content: map-get($bootstrap-icons-map, "terminal-split"); } -.bi-ticket-detailed-fill::before { content: map-get($bootstrap-icons-map, "ticket-detailed-fill"); } -.bi-ticket-detailed::before { content: map-get($bootstrap-icons-map, "ticket-detailed"); } -.bi-ticket-fill::before { content: map-get($bootstrap-icons-map, "ticket-fill"); } -.bi-ticket-perforated-fill::before { content: map-get($bootstrap-icons-map, "ticket-perforated-fill"); } -.bi-ticket-perforated::before { content: map-get($bootstrap-icons-map, "ticket-perforated"); } -.bi-ticket::before { content: map-get($bootstrap-icons-map, "ticket"); } -.bi-tiktok::before { content: map-get($bootstrap-icons-map, "tiktok"); } -.bi-window-dash::before { content: map-get($bootstrap-icons-map, "window-dash"); } -.bi-window-desktop::before { content: map-get($bootstrap-icons-map, "window-desktop"); } -.bi-window-fullscreen::before { content: map-get($bootstrap-icons-map, "window-fullscreen"); } -.bi-window-plus::before { content: map-get($bootstrap-icons-map, "window-plus"); } -.bi-window-split::before { content: map-get($bootstrap-icons-map, "window-split"); } -.bi-window-stack::before { content: map-get($bootstrap-icons-map, "window-stack"); } -.bi-window-x::before { content: map-get($bootstrap-icons-map, "window-x"); } -.bi-xbox::before { content: map-get($bootstrap-icons-map, "xbox"); } -.bi-ethernet::before { content: map-get($bootstrap-icons-map, "ethernet"); } -.bi-hdmi-fill::before { content: map-get($bootstrap-icons-map, "hdmi-fill"); } -.bi-hdmi::before { content: map-get($bootstrap-icons-map, "hdmi"); } -.bi-usb-c-fill::before { content: map-get($bootstrap-icons-map, "usb-c-fill"); } -.bi-usb-c::before { content: map-get($bootstrap-icons-map, "usb-c"); } -.bi-usb-fill::before { content: map-get($bootstrap-icons-map, "usb-fill"); } -.bi-usb-plug-fill::before { content: map-get($bootstrap-icons-map, "usb-plug-fill"); } -.bi-usb-plug::before { content: map-get($bootstrap-icons-map, "usb-plug"); } -.bi-usb-symbol::before { content: map-get($bootstrap-icons-map, "usb-symbol"); } -.bi-usb::before { content: map-get($bootstrap-icons-map, "usb"); } -.bi-boombox-fill::before { content: map-get($bootstrap-icons-map, "boombox-fill"); } -.bi-displayport-1::before { content: map-get($bootstrap-icons-map, "displayport-1"); } -.bi-displayport::before { content: map-get($bootstrap-icons-map, "displayport"); } -.bi-gpu-card::before { content: map-get($bootstrap-icons-map, "gpu-card"); } -.bi-memory::before { content: map-get($bootstrap-icons-map, "memory"); } -.bi-modem-fill::before { content: map-get($bootstrap-icons-map, "modem-fill"); } -.bi-modem::before { content: map-get($bootstrap-icons-map, "modem"); } -.bi-motherboard-fill::before { content: map-get($bootstrap-icons-map, "motherboard-fill"); } -.bi-motherboard::before { content: map-get($bootstrap-icons-map, "motherboard"); } -.bi-optical-audio-fill::before { content: map-get($bootstrap-icons-map, "optical-audio-fill"); } -.bi-optical-audio::before { content: map-get($bootstrap-icons-map, "optical-audio"); } -.bi-pci-card::before { content: map-get($bootstrap-icons-map, "pci-card"); } -.bi-router-fill::before { content: map-get($bootstrap-icons-map, "router-fill"); } -.bi-router::before { content: map-get($bootstrap-icons-map, "router"); } -.bi-ssd-fill::before { content: map-get($bootstrap-icons-map, "ssd-fill"); } -.bi-ssd::before { content: map-get($bootstrap-icons-map, "ssd"); } -.bi-thunderbolt-fill::before { content: map-get($bootstrap-icons-map, "thunderbolt-fill"); } -.bi-thunderbolt::before { content: map-get($bootstrap-icons-map, "thunderbolt"); } -.bi-usb-drive-fill::before { content: map-get($bootstrap-icons-map, "usb-drive-fill"); } -.bi-usb-drive::before { content: map-get($bootstrap-icons-map, "usb-drive"); } -.bi-usb-micro-fill::before { content: map-get($bootstrap-icons-map, "usb-micro-fill"); } -.bi-usb-micro::before { content: map-get($bootstrap-icons-map, "usb-micro"); } -.bi-usb-mini-fill::before { content: map-get($bootstrap-icons-map, "usb-mini-fill"); } -.bi-usb-mini::before { content: map-get($bootstrap-icons-map, "usb-mini"); } -.bi-cloud-haze2::before { content: map-get($bootstrap-icons-map, "cloud-haze2"); } -.bi-device-hdd-fill::before { content: map-get($bootstrap-icons-map, "device-hdd-fill"); } -.bi-device-hdd::before { content: map-get($bootstrap-icons-map, "device-hdd"); } -.bi-device-ssd-fill::before { content: map-get($bootstrap-icons-map, "device-ssd-fill"); } -.bi-device-ssd::before { content: map-get($bootstrap-icons-map, "device-ssd"); } -.bi-displayport-fill::before { content: map-get($bootstrap-icons-map, "displayport-fill"); } -.bi-mortarboard-fill::before { content: map-get($bootstrap-icons-map, "mortarboard-fill"); } -.bi-mortarboard::before { content: map-get($bootstrap-icons-map, "mortarboard"); } -.bi-terminal-x::before { content: map-get($bootstrap-icons-map, "terminal-x"); } -.bi-arrow-through-heart-fill::before { content: map-get($bootstrap-icons-map, "arrow-through-heart-fill"); } -.bi-arrow-through-heart::before { content: map-get($bootstrap-icons-map, "arrow-through-heart"); } -.bi-badge-sd-fill::before { content: map-get($bootstrap-icons-map, "badge-sd-fill"); } -.bi-badge-sd::before { content: map-get($bootstrap-icons-map, "badge-sd"); } -.bi-bag-heart-fill::before { content: map-get($bootstrap-icons-map, "bag-heart-fill"); } -.bi-bag-heart::before { content: map-get($bootstrap-icons-map, "bag-heart"); } -.bi-balloon-fill::before { content: map-get($bootstrap-icons-map, "balloon-fill"); } -.bi-balloon-heart-fill::before { content: map-get($bootstrap-icons-map, "balloon-heart-fill"); } -.bi-balloon-heart::before { content: map-get($bootstrap-icons-map, "balloon-heart"); } -.bi-balloon::before { content: map-get($bootstrap-icons-map, "balloon"); } -.bi-box2-fill::before { content: map-get($bootstrap-icons-map, "box2-fill"); } -.bi-box2-heart-fill::before { content: map-get($bootstrap-icons-map, "box2-heart-fill"); } -.bi-box2-heart::before { content: map-get($bootstrap-icons-map, "box2-heart"); } -.bi-box2::before { content: map-get($bootstrap-icons-map, "box2"); } -.bi-braces-asterisk::before { content: map-get($bootstrap-icons-map, "braces-asterisk"); } -.bi-calendar-heart-fill::before { content: map-get($bootstrap-icons-map, "calendar-heart-fill"); } -.bi-calendar-heart::before { content: map-get($bootstrap-icons-map, "calendar-heart"); } -.bi-calendar2-heart-fill::before { content: map-get($bootstrap-icons-map, "calendar2-heart-fill"); } -.bi-calendar2-heart::before { content: map-get($bootstrap-icons-map, "calendar2-heart"); } -.bi-chat-heart-fill::before { content: map-get($bootstrap-icons-map, "chat-heart-fill"); } -.bi-chat-heart::before { content: map-get($bootstrap-icons-map, "chat-heart"); } -.bi-chat-left-heart-fill::before { content: map-get($bootstrap-icons-map, "chat-left-heart-fill"); } -.bi-chat-left-heart::before { content: map-get($bootstrap-icons-map, "chat-left-heart"); } -.bi-chat-right-heart-fill::before { content: map-get($bootstrap-icons-map, "chat-right-heart-fill"); } -.bi-chat-right-heart::before { content: map-get($bootstrap-icons-map, "chat-right-heart"); } -.bi-chat-square-heart-fill::before { content: map-get($bootstrap-icons-map, "chat-square-heart-fill"); } -.bi-chat-square-heart::before { content: map-get($bootstrap-icons-map, "chat-square-heart"); } -.bi-clipboard-check-fill::before { content: map-get($bootstrap-icons-map, "clipboard-check-fill"); } -.bi-clipboard-data-fill::before { content: map-get($bootstrap-icons-map, "clipboard-data-fill"); } -.bi-clipboard-fill::before { content: map-get($bootstrap-icons-map, "clipboard-fill"); } -.bi-clipboard-heart-fill::before { content: map-get($bootstrap-icons-map, "clipboard-heart-fill"); } -.bi-clipboard-heart::before { content: map-get($bootstrap-icons-map, "clipboard-heart"); } -.bi-clipboard-minus-fill::before { content: map-get($bootstrap-icons-map, "clipboard-minus-fill"); } -.bi-clipboard-plus-fill::before { content: map-get($bootstrap-icons-map, "clipboard-plus-fill"); } -.bi-clipboard-pulse::before { content: map-get($bootstrap-icons-map, "clipboard-pulse"); } -.bi-clipboard-x-fill::before { content: map-get($bootstrap-icons-map, "clipboard-x-fill"); } -.bi-clipboard2-check-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-check-fill"); } -.bi-clipboard2-check::before { content: map-get($bootstrap-icons-map, "clipboard2-check"); } -.bi-clipboard2-data-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-data-fill"); } -.bi-clipboard2-data::before { content: map-get($bootstrap-icons-map, "clipboard2-data"); } -.bi-clipboard2-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-fill"); } -.bi-clipboard2-heart-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-heart-fill"); } -.bi-clipboard2-heart::before { content: map-get($bootstrap-icons-map, "clipboard2-heart"); } -.bi-clipboard2-minus-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-minus-fill"); } -.bi-clipboard2-minus::before { content: map-get($bootstrap-icons-map, "clipboard2-minus"); } -.bi-clipboard2-plus-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-plus-fill"); } -.bi-clipboard2-plus::before { content: map-get($bootstrap-icons-map, "clipboard2-plus"); } -.bi-clipboard2-pulse-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-pulse-fill"); } -.bi-clipboard2-pulse::before { content: map-get($bootstrap-icons-map, "clipboard2-pulse"); } -.bi-clipboard2-x-fill::before { content: map-get($bootstrap-icons-map, "clipboard2-x-fill"); } -.bi-clipboard2-x::before { content: map-get($bootstrap-icons-map, "clipboard2-x"); } -.bi-clipboard2::before { content: map-get($bootstrap-icons-map, "clipboard2"); } -.bi-emoji-kiss-fill::before { content: map-get($bootstrap-icons-map, "emoji-kiss-fill"); } -.bi-emoji-kiss::before { content: map-get($bootstrap-icons-map, "emoji-kiss"); } -.bi-envelope-heart-fill::before { content: map-get($bootstrap-icons-map, "envelope-heart-fill"); } -.bi-envelope-heart::before { content: map-get($bootstrap-icons-map, "envelope-heart"); } -.bi-envelope-open-heart-fill::before { content: map-get($bootstrap-icons-map, "envelope-open-heart-fill"); } -.bi-envelope-open-heart::before { content: map-get($bootstrap-icons-map, "envelope-open-heart"); } -.bi-envelope-paper-fill::before { content: map-get($bootstrap-icons-map, "envelope-paper-fill"); } -.bi-envelope-paper-heart-fill::before { content: map-get($bootstrap-icons-map, "envelope-paper-heart-fill"); } -.bi-envelope-paper-heart::before { content: map-get($bootstrap-icons-map, "envelope-paper-heart"); } -.bi-envelope-paper::before { content: map-get($bootstrap-icons-map, "envelope-paper"); } -.bi-filetype-aac::before { content: map-get($bootstrap-icons-map, "filetype-aac"); } -.bi-filetype-ai::before { content: map-get($bootstrap-icons-map, "filetype-ai"); } -.bi-filetype-bmp::before { content: map-get($bootstrap-icons-map, "filetype-bmp"); } -.bi-filetype-cs::before { content: map-get($bootstrap-icons-map, "filetype-cs"); } -.bi-filetype-css::before { content: map-get($bootstrap-icons-map, "filetype-css"); } -.bi-filetype-csv::before { content: map-get($bootstrap-icons-map, "filetype-csv"); } -.bi-filetype-doc::before { content: map-get($bootstrap-icons-map, "filetype-doc"); } -.bi-filetype-docx::before { content: map-get($bootstrap-icons-map, "filetype-docx"); } -.bi-filetype-exe::before { content: map-get($bootstrap-icons-map, "filetype-exe"); } -.bi-filetype-gif::before { content: map-get($bootstrap-icons-map, "filetype-gif"); } -.bi-filetype-heic::before { content: map-get($bootstrap-icons-map, "filetype-heic"); } -.bi-filetype-html::before { content: map-get($bootstrap-icons-map, "filetype-html"); } -.bi-filetype-java::before { content: map-get($bootstrap-icons-map, "filetype-java"); } -.bi-filetype-jpg::before { content: map-get($bootstrap-icons-map, "filetype-jpg"); } -.bi-filetype-js::before { content: map-get($bootstrap-icons-map, "filetype-js"); } -.bi-filetype-jsx::before { content: map-get($bootstrap-icons-map, "filetype-jsx"); } -.bi-filetype-key::before { content: map-get($bootstrap-icons-map, "filetype-key"); } -.bi-filetype-m4p::before { content: map-get($bootstrap-icons-map, "filetype-m4p"); } -.bi-filetype-md::before { content: map-get($bootstrap-icons-map, "filetype-md"); } -.bi-filetype-mdx::before { content: map-get($bootstrap-icons-map, "filetype-mdx"); } -.bi-filetype-mov::before { content: map-get($bootstrap-icons-map, "filetype-mov"); } -.bi-filetype-mp3::before { content: map-get($bootstrap-icons-map, "filetype-mp3"); } -.bi-filetype-mp4::before { content: map-get($bootstrap-icons-map, "filetype-mp4"); } -.bi-filetype-otf::before { content: map-get($bootstrap-icons-map, "filetype-otf"); } -.bi-filetype-pdf::before { content: map-get($bootstrap-icons-map, "filetype-pdf"); } -.bi-filetype-php::before { content: map-get($bootstrap-icons-map, "filetype-php"); } -.bi-filetype-png::before { content: map-get($bootstrap-icons-map, "filetype-png"); } -.bi-filetype-ppt-1::before { content: map-get($bootstrap-icons-map, "filetype-ppt-1"); } -.bi-filetype-ppt::before { content: map-get($bootstrap-icons-map, "filetype-ppt"); } -.bi-filetype-psd::before { content: map-get($bootstrap-icons-map, "filetype-psd"); } -.bi-filetype-py::before { content: map-get($bootstrap-icons-map, "filetype-py"); } -.bi-filetype-raw::before { content: map-get($bootstrap-icons-map, "filetype-raw"); } -.bi-filetype-rb::before { content: map-get($bootstrap-icons-map, "filetype-rb"); } -.bi-filetype-sass::before { content: map-get($bootstrap-icons-map, "filetype-sass"); } -.bi-filetype-scss::before { content: map-get($bootstrap-icons-map, "filetype-scss"); } -.bi-filetype-sh::before { content: map-get($bootstrap-icons-map, "filetype-sh"); } -.bi-filetype-svg::before { content: map-get($bootstrap-icons-map, "filetype-svg"); } -.bi-filetype-tiff::before { content: map-get($bootstrap-icons-map, "filetype-tiff"); } -.bi-filetype-tsx::before { content: map-get($bootstrap-icons-map, "filetype-tsx"); } -.bi-filetype-ttf::before { content: map-get($bootstrap-icons-map, "filetype-ttf"); } -.bi-filetype-txt::before { content: map-get($bootstrap-icons-map, "filetype-txt"); } -.bi-filetype-wav::before { content: map-get($bootstrap-icons-map, "filetype-wav"); } -.bi-filetype-woff::before { content: map-get($bootstrap-icons-map, "filetype-woff"); } -.bi-filetype-xls-1::before { content: map-get($bootstrap-icons-map, "filetype-xls-1"); } -.bi-filetype-xls::before { content: map-get($bootstrap-icons-map, "filetype-xls"); } -.bi-filetype-xml::before { content: map-get($bootstrap-icons-map, "filetype-xml"); } -.bi-filetype-yml::before { content: map-get($bootstrap-icons-map, "filetype-yml"); } -.bi-heart-arrow::before { content: map-get($bootstrap-icons-map, "heart-arrow"); } -.bi-heart-pulse-fill::before { content: map-get($bootstrap-icons-map, "heart-pulse-fill"); } -.bi-heart-pulse::before { content: map-get($bootstrap-icons-map, "heart-pulse"); } -.bi-heartbreak-fill::before { content: map-get($bootstrap-icons-map, "heartbreak-fill"); } -.bi-heartbreak::before { content: map-get($bootstrap-icons-map, "heartbreak"); } -.bi-hearts::before { content: map-get($bootstrap-icons-map, "hearts"); } -.bi-hospital-fill::before { content: map-get($bootstrap-icons-map, "hospital-fill"); } -.bi-hospital::before { content: map-get($bootstrap-icons-map, "hospital"); } -.bi-house-heart-fill::before { content: map-get($bootstrap-icons-map, "house-heart-fill"); } -.bi-house-heart::before { content: map-get($bootstrap-icons-map, "house-heart"); } -.bi-incognito::before { content: map-get($bootstrap-icons-map, "incognito"); } -.bi-magnet-fill::before { content: map-get($bootstrap-icons-map, "magnet-fill"); } -.bi-magnet::before { content: map-get($bootstrap-icons-map, "magnet"); } -.bi-person-heart::before { content: map-get($bootstrap-icons-map, "person-heart"); } -.bi-person-hearts::before { content: map-get($bootstrap-icons-map, "person-hearts"); } -.bi-phone-flip::before { content: map-get($bootstrap-icons-map, "phone-flip"); } -.bi-plugin::before { content: map-get($bootstrap-icons-map, "plugin"); } -.bi-postage-fill::before { content: map-get($bootstrap-icons-map, "postage-fill"); } -.bi-postage-heart-fill::before { content: map-get($bootstrap-icons-map, "postage-heart-fill"); } -.bi-postage-heart::before { content: map-get($bootstrap-icons-map, "postage-heart"); } -.bi-postage::before { content: map-get($bootstrap-icons-map, "postage"); } -.bi-postcard-fill::before { content: map-get($bootstrap-icons-map, "postcard-fill"); } -.bi-postcard-heart-fill::before { content: map-get($bootstrap-icons-map, "postcard-heart-fill"); } -.bi-postcard-heart::before { content: map-get($bootstrap-icons-map, "postcard-heart"); } -.bi-postcard::before { content: map-get($bootstrap-icons-map, "postcard"); } -.bi-search-heart-fill::before { content: map-get($bootstrap-icons-map, "search-heart-fill"); } -.bi-search-heart::before { content: map-get($bootstrap-icons-map, "search-heart"); } -.bi-sliders2-vertical::before { content: map-get($bootstrap-icons-map, "sliders2-vertical"); } -.bi-sliders2::before { content: map-get($bootstrap-icons-map, "sliders2"); } -.bi-trash3-fill::before { content: map-get($bootstrap-icons-map, "trash3-fill"); } -.bi-trash3::before { content: map-get($bootstrap-icons-map, "trash3"); } -.bi-valentine::before { content: map-get($bootstrap-icons-map, "valentine"); } -.bi-valentine2::before { content: map-get($bootstrap-icons-map, "valentine2"); } -.bi-wrench-adjustable-circle-fill::before { content: map-get($bootstrap-icons-map, "wrench-adjustable-circle-fill"); } -.bi-wrench-adjustable-circle::before { content: map-get($bootstrap-icons-map, "wrench-adjustable-circle"); } -.bi-wrench-adjustable::before { content: map-get($bootstrap-icons-map, "wrench-adjustable"); } -.bi-filetype-json::before { content: map-get($bootstrap-icons-map, "filetype-json"); } -.bi-filetype-pptx::before { content: map-get($bootstrap-icons-map, "filetype-pptx"); } -.bi-filetype-xlsx::before { content: map-get($bootstrap-icons-map, "filetype-xlsx"); } -.bi-1-circle-1::before { content: map-get($bootstrap-icons-map, "1-circle-1"); } -.bi-1-circle-fill-1::before { content: map-get($bootstrap-icons-map, "1-circle-fill-1"); } -.bi-1-circle-fill::before { content: map-get($bootstrap-icons-map, "1-circle-fill"); } -.bi-1-circle::before { content: map-get($bootstrap-icons-map, "1-circle"); } -.bi-1-square-fill::before { content: map-get($bootstrap-icons-map, "1-square-fill"); } -.bi-1-square::before { content: map-get($bootstrap-icons-map, "1-square"); } -.bi-2-circle-1::before { content: map-get($bootstrap-icons-map, "2-circle-1"); } -.bi-2-circle-fill-1::before { content: map-get($bootstrap-icons-map, "2-circle-fill-1"); } -.bi-2-circle-fill::before { content: map-get($bootstrap-icons-map, "2-circle-fill"); } -.bi-2-circle::before { content: map-get($bootstrap-icons-map, "2-circle"); } -.bi-2-square-fill::before { content: map-get($bootstrap-icons-map, "2-square-fill"); } -.bi-2-square::before { content: map-get($bootstrap-icons-map, "2-square"); } -.bi-3-circle-1::before { content: map-get($bootstrap-icons-map, "3-circle-1"); } -.bi-3-circle-fill-1::before { content: map-get($bootstrap-icons-map, "3-circle-fill-1"); } -.bi-3-circle-fill::before { content: map-get($bootstrap-icons-map, "3-circle-fill"); } -.bi-3-circle::before { content: map-get($bootstrap-icons-map, "3-circle"); } -.bi-3-square-fill::before { content: map-get($bootstrap-icons-map, "3-square-fill"); } -.bi-3-square::before { content: map-get($bootstrap-icons-map, "3-square"); } -.bi-4-circle-1::before { content: map-get($bootstrap-icons-map, "4-circle-1"); } -.bi-4-circle-fill-1::before { content: map-get($bootstrap-icons-map, "4-circle-fill-1"); } -.bi-4-circle-fill::before { content: map-get($bootstrap-icons-map, "4-circle-fill"); } -.bi-4-circle::before { content: map-get($bootstrap-icons-map, "4-circle"); } -.bi-4-square-fill::before { content: map-get($bootstrap-icons-map, "4-square-fill"); } -.bi-4-square::before { content: map-get($bootstrap-icons-map, "4-square"); } -.bi-5-circle-1::before { content: map-get($bootstrap-icons-map, "5-circle-1"); } -.bi-5-circle-fill-1::before { content: map-get($bootstrap-icons-map, "5-circle-fill-1"); } -.bi-5-circle-fill::before { content: map-get($bootstrap-icons-map, "5-circle-fill"); } -.bi-5-circle::before { content: map-get($bootstrap-icons-map, "5-circle"); } -.bi-5-square-fill::before { content: map-get($bootstrap-icons-map, "5-square-fill"); } -.bi-5-square::before { content: map-get($bootstrap-icons-map, "5-square"); } -.bi-6-circle-1::before { content: map-get($bootstrap-icons-map, "6-circle-1"); } -.bi-6-circle-fill-1::before { content: map-get($bootstrap-icons-map, "6-circle-fill-1"); } -.bi-6-circle-fill::before { content: map-get($bootstrap-icons-map, "6-circle-fill"); } -.bi-6-circle::before { content: map-get($bootstrap-icons-map, "6-circle"); } -.bi-6-square-fill::before { content: map-get($bootstrap-icons-map, "6-square-fill"); } -.bi-6-square::before { content: map-get($bootstrap-icons-map, "6-square"); } -.bi-7-circle-1::before { content: map-get($bootstrap-icons-map, "7-circle-1"); } -.bi-7-circle-fill-1::before { content: map-get($bootstrap-icons-map, "7-circle-fill-1"); } -.bi-7-circle-fill::before { content: map-get($bootstrap-icons-map, "7-circle-fill"); } -.bi-7-circle::before { content: map-get($bootstrap-icons-map, "7-circle"); } -.bi-7-square-fill::before { content: map-get($bootstrap-icons-map, "7-square-fill"); } -.bi-7-square::before { content: map-get($bootstrap-icons-map, "7-square"); } -.bi-8-circle-1::before { content: map-get($bootstrap-icons-map, "8-circle-1"); } -.bi-8-circle-fill-1::before { content: map-get($bootstrap-icons-map, "8-circle-fill-1"); } -.bi-8-circle-fill::before { content: map-get($bootstrap-icons-map, "8-circle-fill"); } -.bi-8-circle::before { content: map-get($bootstrap-icons-map, "8-circle"); } -.bi-8-square-fill::before { content: map-get($bootstrap-icons-map, "8-square-fill"); } -.bi-8-square::before { content: map-get($bootstrap-icons-map, "8-square"); } -.bi-9-circle-1::before { content: map-get($bootstrap-icons-map, "9-circle-1"); } -.bi-9-circle-fill-1::before { content: map-get($bootstrap-icons-map, "9-circle-fill-1"); } -.bi-9-circle-fill::before { content: map-get($bootstrap-icons-map, "9-circle-fill"); } -.bi-9-circle::before { content: map-get($bootstrap-icons-map, "9-circle"); } -.bi-9-square-fill::before { content: map-get($bootstrap-icons-map, "9-square-fill"); } -.bi-9-square::before { content: map-get($bootstrap-icons-map, "9-square"); } -.bi-airplane-engines-fill::before { content: map-get($bootstrap-icons-map, "airplane-engines-fill"); } -.bi-airplane-engines::before { content: map-get($bootstrap-icons-map, "airplane-engines"); } -.bi-airplane-fill::before { content: map-get($bootstrap-icons-map, "airplane-fill"); } -.bi-airplane::before { content: map-get($bootstrap-icons-map, "airplane"); } -.bi-alexa::before { content: map-get($bootstrap-icons-map, "alexa"); } -.bi-alipay::before { content: map-get($bootstrap-icons-map, "alipay"); } -.bi-android::before { content: map-get($bootstrap-icons-map, "android"); } -.bi-android2::before { content: map-get($bootstrap-icons-map, "android2"); } -.bi-box-fill::before { content: map-get($bootstrap-icons-map, "box-fill"); } -.bi-box-seam-fill::before { content: map-get($bootstrap-icons-map, "box-seam-fill"); } -.bi-browser-chrome::before { content: map-get($bootstrap-icons-map, "browser-chrome"); } -.bi-browser-edge::before { content: map-get($bootstrap-icons-map, "browser-edge"); } -.bi-browser-firefox::before { content: map-get($bootstrap-icons-map, "browser-firefox"); } -.bi-browser-safari::before { content: map-get($bootstrap-icons-map, "browser-safari"); } -.bi-c-circle-1::before { content: map-get($bootstrap-icons-map, "c-circle-1"); } -.bi-c-circle-fill-1::before { content: map-get($bootstrap-icons-map, "c-circle-fill-1"); } -.bi-c-circle-fill::before { content: map-get($bootstrap-icons-map, "c-circle-fill"); } -.bi-c-circle::before { content: map-get($bootstrap-icons-map, "c-circle"); } -.bi-c-square-fill::before { content: map-get($bootstrap-icons-map, "c-square-fill"); } -.bi-c-square::before { content: map-get($bootstrap-icons-map, "c-square"); } -.bi-capsule-pill::before { content: map-get($bootstrap-icons-map, "capsule-pill"); } -.bi-capsule::before { content: map-get($bootstrap-icons-map, "capsule"); } -.bi-car-front-fill::before { content: map-get($bootstrap-icons-map, "car-front-fill"); } -.bi-car-front::before { content: map-get($bootstrap-icons-map, "car-front"); } -.bi-cassette-fill::before { content: map-get($bootstrap-icons-map, "cassette-fill"); } -.bi-cassette::before { content: map-get($bootstrap-icons-map, "cassette"); } -.bi-cc-circle-1::before { content: map-get($bootstrap-icons-map, "cc-circle-1"); } -.bi-cc-circle-fill-1::before { content: map-get($bootstrap-icons-map, "cc-circle-fill-1"); } -.bi-cc-circle-fill::before { content: map-get($bootstrap-icons-map, "cc-circle-fill"); } -.bi-cc-circle::before { content: map-get($bootstrap-icons-map, "cc-circle"); } -.bi-cc-square-fill::before { content: map-get($bootstrap-icons-map, "cc-square-fill"); } -.bi-cc-square::before { content: map-get($bootstrap-icons-map, "cc-square"); } -.bi-cup-hot-fill::before { content: map-get($bootstrap-icons-map, "cup-hot-fill"); } -.bi-cup-hot::before { content: map-get($bootstrap-icons-map, "cup-hot"); } -.bi-currency-rupee::before { content: map-get($bootstrap-icons-map, "currency-rupee"); } -.bi-dropbox::before { content: map-get($bootstrap-icons-map, "dropbox"); } -.bi-escape::before { content: map-get($bootstrap-icons-map, "escape"); } -.bi-fast-forward-btn-fill::before { content: map-get($bootstrap-icons-map, "fast-forward-btn-fill"); } -.bi-fast-forward-btn::before { content: map-get($bootstrap-icons-map, "fast-forward-btn"); } -.bi-fast-forward-circle-fill::before { content: map-get($bootstrap-icons-map, "fast-forward-circle-fill"); } -.bi-fast-forward-circle::before { content: map-get($bootstrap-icons-map, "fast-forward-circle"); } -.bi-fast-forward-fill::before { content: map-get($bootstrap-icons-map, "fast-forward-fill"); } -.bi-fast-forward::before { content: map-get($bootstrap-icons-map, "fast-forward"); } -.bi-filetype-sql::before { content: map-get($bootstrap-icons-map, "filetype-sql"); } -.bi-fire::before { content: map-get($bootstrap-icons-map, "fire"); } -.bi-google-play::before { content: map-get($bootstrap-icons-map, "google-play"); } -.bi-h-circle-1::before { content: map-get($bootstrap-icons-map, "h-circle-1"); } -.bi-h-circle-fill-1::before { content: map-get($bootstrap-icons-map, "h-circle-fill-1"); } -.bi-h-circle-fill::before { content: map-get($bootstrap-icons-map, "h-circle-fill"); } -.bi-h-circle::before { content: map-get($bootstrap-icons-map, "h-circle"); } -.bi-h-square-fill::before { content: map-get($bootstrap-icons-map, "h-square-fill"); } -.bi-h-square::before { content: map-get($bootstrap-icons-map, "h-square"); } -.bi-indent::before { content: map-get($bootstrap-icons-map, "indent"); } -.bi-lungs-fill::before { content: map-get($bootstrap-icons-map, "lungs-fill"); } -.bi-lungs::before { content: map-get($bootstrap-icons-map, "lungs"); } -.bi-microsoft-teams::before { content: map-get($bootstrap-icons-map, "microsoft-teams"); } -.bi-p-circle-1::before { content: map-get($bootstrap-icons-map, "p-circle-1"); } -.bi-p-circle-fill-1::before { content: map-get($bootstrap-icons-map, "p-circle-fill-1"); } -.bi-p-circle-fill::before { content: map-get($bootstrap-icons-map, "p-circle-fill"); } -.bi-p-circle::before { content: map-get($bootstrap-icons-map, "p-circle"); } -.bi-p-square-fill::before { content: map-get($bootstrap-icons-map, "p-square-fill"); } -.bi-p-square::before { content: map-get($bootstrap-icons-map, "p-square"); } -.bi-pass-fill::before { content: map-get($bootstrap-icons-map, "pass-fill"); } -.bi-pass::before { content: map-get($bootstrap-icons-map, "pass"); } -.bi-prescription::before { content: map-get($bootstrap-icons-map, "prescription"); } -.bi-prescription2::before { content: map-get($bootstrap-icons-map, "prescription2"); } -.bi-r-circle-1::before { content: map-get($bootstrap-icons-map, "r-circle-1"); } -.bi-r-circle-fill-1::before { content: map-get($bootstrap-icons-map, "r-circle-fill-1"); } -.bi-r-circle-fill::before { content: map-get($bootstrap-icons-map, "r-circle-fill"); } -.bi-r-circle::before { content: map-get($bootstrap-icons-map, "r-circle"); } -.bi-r-square-fill::before { content: map-get($bootstrap-icons-map, "r-square-fill"); } -.bi-r-square::before { content: map-get($bootstrap-icons-map, "r-square"); } -.bi-repeat-1::before { content: map-get($bootstrap-icons-map, "repeat-1"); } -.bi-repeat::before { content: map-get($bootstrap-icons-map, "repeat"); } -.bi-rewind-btn-fill::before { content: map-get($bootstrap-icons-map, "rewind-btn-fill"); } -.bi-rewind-btn::before { content: map-get($bootstrap-icons-map, "rewind-btn"); } -.bi-rewind-circle-fill::before { content: map-get($bootstrap-icons-map, "rewind-circle-fill"); } -.bi-rewind-circle::before { content: map-get($bootstrap-icons-map, "rewind-circle"); } -.bi-rewind-fill::before { content: map-get($bootstrap-icons-map, "rewind-fill"); } -.bi-rewind::before { content: map-get($bootstrap-icons-map, "rewind"); } -.bi-train-freight-front-fill::before { content: map-get($bootstrap-icons-map, "train-freight-front-fill"); } -.bi-train-freight-front::before { content: map-get($bootstrap-icons-map, "train-freight-front"); } -.bi-train-front-fill::before { content: map-get($bootstrap-icons-map, "train-front-fill"); } -.bi-train-front::before { content: map-get($bootstrap-icons-map, "train-front"); } -.bi-train-lightrail-front-fill::before { content: map-get($bootstrap-icons-map, "train-lightrail-front-fill"); } -.bi-train-lightrail-front::before { content: map-get($bootstrap-icons-map, "train-lightrail-front"); } -.bi-truck-front-fill::before { content: map-get($bootstrap-icons-map, "truck-front-fill"); } -.bi-truck-front::before { content: map-get($bootstrap-icons-map, "truck-front"); } -.bi-ubuntu::before { content: map-get($bootstrap-icons-map, "ubuntu"); } -.bi-unindent::before { content: map-get($bootstrap-icons-map, "unindent"); } -.bi-unity::before { content: map-get($bootstrap-icons-map, "unity"); } -.bi-universal-access-circle::before { content: map-get($bootstrap-icons-map, "universal-access-circle"); } -.bi-universal-access::before { content: map-get($bootstrap-icons-map, "universal-access"); } -.bi-virus::before { content: map-get($bootstrap-icons-map, "virus"); } -.bi-virus2::before { content: map-get($bootstrap-icons-map, "virus2"); } -.bi-wechat::before { content: map-get($bootstrap-icons-map, "wechat"); } -.bi-yelp::before { content: map-get($bootstrap-icons-map, "yelp"); } -.bi-sign-stop-fill::before { content: map-get($bootstrap-icons-map, "sign-stop-fill"); } -.bi-sign-stop-lights-fill::before { content: map-get($bootstrap-icons-map, "sign-stop-lights-fill"); } -.bi-sign-stop-lights::before { content: map-get($bootstrap-icons-map, "sign-stop-lights"); } -.bi-sign-stop::before { content: map-get($bootstrap-icons-map, "sign-stop"); } -.bi-sign-turn-left-fill::before { content: map-get($bootstrap-icons-map, "sign-turn-left-fill"); } -.bi-sign-turn-left::before { content: map-get($bootstrap-icons-map, "sign-turn-left"); } -.bi-sign-turn-right-fill::before { content: map-get($bootstrap-icons-map, "sign-turn-right-fill"); } -.bi-sign-turn-right::before { content: map-get($bootstrap-icons-map, "sign-turn-right"); } -.bi-sign-turn-slight-left-fill::before { content: map-get($bootstrap-icons-map, "sign-turn-slight-left-fill"); } -.bi-sign-turn-slight-left::before { content: map-get($bootstrap-icons-map, "sign-turn-slight-left"); } -.bi-sign-turn-slight-right-fill::before { content: map-get($bootstrap-icons-map, "sign-turn-slight-right-fill"); } -.bi-sign-turn-slight-right::before { content: map-get($bootstrap-icons-map, "sign-turn-slight-right"); } -.bi-sign-yield-fill::before { content: map-get($bootstrap-icons-map, "sign-yield-fill"); } -.bi-sign-yield::before { content: map-get($bootstrap-icons-map, "sign-yield"); } -.bi-ev-station-fill::before { content: map-get($bootstrap-icons-map, "ev-station-fill"); } -.bi-ev-station::before { content: map-get($bootstrap-icons-map, "ev-station"); } -.bi-fuel-pump-diesel-fill::before { content: map-get($bootstrap-icons-map, "fuel-pump-diesel-fill"); } -.bi-fuel-pump-diesel::before { content: map-get($bootstrap-icons-map, "fuel-pump-diesel"); } -.bi-fuel-pump-fill::before { content: map-get($bootstrap-icons-map, "fuel-pump-fill"); } -.bi-fuel-pump::before { content: map-get($bootstrap-icons-map, "fuel-pump"); } +@each $icon, $codepoint in $bootstrap-icons-map { + .bi-#{$icon}::before { content: $codepoint; } +} diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff index cf0a6d0fb..51204d27d 100644 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff and b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff2 b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff2 index ecc8f4c5d..92c483021 100644 Binary files a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff2 and b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/fonts/bootstrap-icons.woff2 differ diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/index.html b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/index.html deleted file mode 100644 index 1dd4cb1ae..000000000 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/font/index.html +++ /dev/null @@ -1,7291 +0,0 @@ - - - - - bootstrap-icons - - - - - - - - -

bootstrap-icons

- -
-
- -
123
-
-
- -
1-circle-fill
-
-
- -
1-circle
-
-
- -
1-square-fill
-
-
- -
1-square
-
-
- -
2-circle-fill
-
-
- -
2-circle
-
-
- -
2-square-fill
-
-
- -
2-square
-
-
- -
3-circle-fill
-
-
- -
3-circle
-
-
- -
3-square-fill
-
-
- -
3-square
-
-
- -
4-circle-fill
-
-
- -
4-circle
-
-
- -
4-square-fill
-
-
- -
4-square
-
-
- -
5-circle-fill
-
-
- -
5-circle
-
-
- -
5-square-fill
-
-
- -
5-square
-
-
- -
6-circle-fill
-
-
- -
6-circle
-
-
- -
6-square-fill
-
-
- -
6-square
-
-
- -
7-circle-fill
-
-
- -
7-circle
-
-
- -
7-square-fill
-
-
- -
7-square
-
-
- -
8-circle-fill
-
-
- -
8-circle
-
-
- -
8-square-fill
-
-
- -
8-square
-
-
- -
9-circle-fill
-
-
- -
9-circle
-
-
- -
9-square-fill
-
-
- -
9-square
-
-
- -
activity
-
-
- -
airplane-engines-fill
-
-
- -
airplane-engines
-
-
- -
airplane-fill
-
-
- -
airplane
-
-
- -
alarm-fill
-
-
- -
alarm
-
-
- -
alexa
-
-
- -
align-bottom
-
-
- -
align-center
-
-
- -
align-end
-
-
- -
align-middle
-
-
- -
align-start
-
-
- -
align-top
-
-
- -
alipay
-
-
- -
alt
-
-
- -
android
-
-
- -
android2
-
-
- -
app-indicator
-
-
- -
app
-
-
- -
apple
-
-
- -
archive-fill
-
-
- -
archive
-
-
- -
arrow-90deg-down
-
-
- -
arrow-90deg-left
-
-
- -
arrow-90deg-right
-
-
- -
arrow-90deg-up
-
-
- -
arrow-bar-down
-
-
- -
arrow-bar-left
-
-
- -
arrow-bar-right
-
-
- -
arrow-bar-up
-
-
- -
arrow-clockwise
-
-
- -
arrow-counterclockwise
-
-
- -
arrow-down-circle-fill
-
-
- -
arrow-down-circle
-
-
- -
arrow-down-left-circle-fill
-
-
- -
arrow-down-left-circle
-
-
- -
arrow-down-left-square-fill
-
-
- -
arrow-down-left-square
-
-
- -
arrow-down-left
-
-
- -
arrow-down-right-circle-fill
-
-
- -
arrow-down-right-circle
-
-
- -
arrow-down-right-square-fill
-
-
- -
arrow-down-right-square
-
-
- -
arrow-down-right
-
-
- -
arrow-down-short
-
-
- -
arrow-down-square-fill
-
-
- -
arrow-down-square
-
-
- -
arrow-down-up
-
-
- -
arrow-down
-
-
- -
arrow-left-circle-fill
-
-
- -
arrow-left-circle
-
-
- -
arrow-left-right
-
-
- -
arrow-left-short
-
-
- -
arrow-left-square-fill
-
-
- -
arrow-left-square
-
-
- -
arrow-left
-
-
- -
arrow-repeat
-
-
- -
arrow-return-left
-
-
- -
arrow-return-right
-
-
- -
arrow-right-circle-fill
-
-
- -
arrow-right-circle
-
-
- -
arrow-right-short
-
-
- -
arrow-right-square-fill
-
-
- -
arrow-right-square
-
-
- -
arrow-right
-
-
- -
arrow-through-heart-fill
-
-
- -
arrow-through-heart
-
-
- -
arrow-up-circle-fill
-
-
- -
arrow-up-circle
-
-
- -
arrow-up-left-circle-fill
-
-
- -
arrow-up-left-circle
-
-
- -
arrow-up-left-square-fill
-
-
- -
arrow-up-left-square
-
-
- -
arrow-up-left
-
-
- -
arrow-up-right-circle-fill
-
-
- -
arrow-up-right-circle
-
-
- -
arrow-up-right-square-fill
-
-
- -
arrow-up-right-square
-
-
- -
arrow-up-right
-
-
- -
arrow-up-short
-
-
- -
arrow-up-square-fill
-
-
- -
arrow-up-square
-
-
- -
arrow-up
-
-
- -
arrows-angle-contract
-
-
- -
arrows-angle-expand
-
-
- -
arrows-collapse
-
-
- -
arrows-expand
-
-
- -
arrows-fullscreen
-
-
- -
arrows-move
-
-
- -
aspect-ratio-fill
-
-
- -
aspect-ratio
-
-
- -
asterisk
-
-
- -
at
-
-
- -
award-fill
-
-
- -
award
-
-
- -
back
-
-
- -
backspace-fill
-
-
- -
backspace-reverse-fill
-
-
- -
backspace-reverse
-
-
- -
backspace
-
-
- -
badge-3d-fill
-
-
- -
badge-3d
-
-
- -
badge-4k-fill
-
-
- -
badge-4k
-
-
- -
badge-8k-fill
-
-
- -
badge-8k
-
-
- -
badge-ad-fill
-
-
- -
badge-ad
-
-
- -
badge-ar-fill
-
-
- -
badge-ar
-
-
- -
badge-cc-fill
-
-
- -
badge-cc
-
-
- -
badge-hd-fill
-
-
- -
badge-hd
-
-
- -
badge-sd-fill
-
-
- -
badge-sd
-
-
- -
badge-tm-fill
-
-
- -
badge-tm
-
-
- -
badge-vo-fill
-
-
- -
badge-vo
-
-
- -
badge-vr-fill
-
-
- -
badge-vr
-
-
- -
badge-wc-fill
-
-
- -
badge-wc
-
-
- -
bag-check-fill
-
-
- -
bag-check
-
-
- -
bag-dash-fill
-
-
- -
bag-dash
-
-
- -
bag-fill
-
-
- -
bag-heart-fill
-
-
- -
bag-heart
-
-
- -
bag-plus-fill
-
-
- -
bag-plus
-
-
- -
bag-x-fill
-
-
- -
bag-x
-
-
- -
bag
-
-
- -
balloon-fill
-
-
- -
balloon-heart-fill
-
-
- -
balloon-heart
-
-
- -
balloon
-
-
- -
bandaid-fill
-
-
- -
bandaid
-
-
- -
bank
-
-
- -
bank2
-
-
- -
bar-chart-fill
-
-
- -
bar-chart-line-fill
-
-
- -
bar-chart-line
-
-
- -
bar-chart-steps
-
-
- -
bar-chart
-
-
- -
basket-fill
-
-
- -
basket
-
-
- -
basket2-fill
-
-
- -
basket2
-
-
- -
basket3-fill
-
-
- -
basket3
-
-
- -
battery-charging
-
-
- -
battery-full
-
-
- -
battery-half
-
-
- -
battery
-
-
- -
behance
-
-
- -
bell-fill
-
-
- -
bell-slash-fill
-
-
- -
bell-slash
-
-
- -
bell
-
-
- -
bezier
-
-
- -
bezier2
-
-
- -
bicycle
-
-
- -
binoculars-fill
-
-
- -
binoculars
-
-
- -
blockquote-left
-
-
- -
blockquote-right
-
-
- -
bluetooth
-
-
- -
body-text
-
-
- -
book-fill
-
-
- -
book-half
-
-
- -
book
-
-
- -
bookmark-check-fill
-
-
- -
bookmark-check
-
-
- -
bookmark-dash-fill
-
-
- -
bookmark-dash
-
-
- -
bookmark-fill
-
-
- -
bookmark-heart-fill
-
-
- -
bookmark-heart
-
-
- -
bookmark-plus-fill
-
-
- -
bookmark-plus
-
-
- -
bookmark-star-fill
-
-
- -
bookmark-star
-
-
- -
bookmark-x-fill
-
-
- -
bookmark-x
-
-
- -
bookmark
-
-
- -
bookmarks-fill
-
-
- -
bookmarks
-
-
- -
bookshelf
-
-
- -
boombox-fill
-
-
- -
boombox
-
-
- -
bootstrap-fill
-
-
- -
bootstrap-reboot
-
-
- -
bootstrap
-
-
- -
border-all
-
-
- -
border-bottom
-
-
- -
border-center
-
-
- -
border-inner
-
-
- -
border-left
-
-
- -
border-middle
-
-
- -
border-outer
-
-
- -
border-right
-
-
- -
border-style
-
-
- -
border-top
-
-
- -
border-width
-
-
- -
border
-
-
- -
bounding-box-circles
-
-
- -
bounding-box
-
-
- -
box-arrow-down-left
-
-
- -
box-arrow-down-right
-
-
- -
box-arrow-down
-
-
- -
box-arrow-in-down-left
-
-
- -
box-arrow-in-down-right
-
-
- -
box-arrow-in-down
-
-
- -
box-arrow-in-left
-
-
- -
box-arrow-in-right
-
-
- -
box-arrow-in-up-left
-
-
- -
box-arrow-in-up-right
-
-
- -
box-arrow-in-up
-
-
- -
box-arrow-left
-
-
- -
box-arrow-right
-
-
- -
box-arrow-up-left
-
-
- -
box-arrow-up-right
-
-
- -
box-arrow-up
-
-
- -
box-fill
-
-
- -
box-seam-fill
-
-
- -
box-seam
-
-
- -
box
-
-
- -
box2-fill
-
-
- -
box2-heart-fill
-
-
- -
box2-heart
-
-
- -
box2
-
-
- -
boxes
-
-
- -
braces-asterisk
-
-
- -
braces
-
-
- -
bricks
-
-
- -
briefcase-fill
-
-
- -
briefcase
-
-
- -
brightness-alt-high-fill
-
-
- -
brightness-alt-high
-
-
- -
brightness-alt-low-fill
-
-
- -
brightness-alt-low
-
-
- -
brightness-high-fill
-
-
- -
brightness-high
-
-
- -
brightness-low-fill
-
-
- -
brightness-low
-
-
- -
broadcast-pin
-
-
- -
broadcast
-
-
- -
browser-chrome
-
-
- -
browser-edge
-
-
- -
browser-firefox
-
-
- -
browser-safari
-
-
- -
brush-fill
-
-
- -
brush
-
-
- -
bucket-fill
-
-
- -
bucket
-
-
- -
bug-fill
-
-
- -
bug
-
-
- -
building
-
-
- -
bullseye
-
-
- -
c-circle-fill
-
-
- -
c-circle
-
-
- -
c-square-fill
-
-
- -
c-square
-
-
- -
calculator-fill
-
-
- -
calculator
-
-
- -
calendar-check-fill
-
-
- -
calendar-check
-
-
- -
calendar-date-fill
-
-
- -
calendar-date
-
-
- -
calendar-day-fill
-
-
- -
calendar-day
-
-
- -
calendar-event-fill
-
-
- -
calendar-event
-
-
- -
calendar-fill
-
-
- -
calendar-heart-fill
-
-
- -
calendar-heart
-
-
- -
calendar-minus-fill
-
-
- -
calendar-minus
-
-
- -
calendar-month-fill
-
-
- -
calendar-month
-
-
- -
calendar-plus-fill
-
-
- -
calendar-plus
-
-
- -
calendar-range-fill
-
-
- -
calendar-range
-
-
- -
calendar-week-fill
-
-
- -
calendar-week
-
-
- -
calendar-x-fill
-
-
- -
calendar-x
-
-
- -
calendar
-
-
- -
calendar2-check-fill
-
-
- -
calendar2-check
-
-
- -
calendar2-date-fill
-
-
- -
calendar2-date
-
-
- -
calendar2-day-fill
-
-
- -
calendar2-day
-
-
- -
calendar2-event-fill
-
-
- -
calendar2-event
-
-
- -
calendar2-fill
-
-
- -
calendar2-heart-fill
-
-
- -
calendar2-heart
-
-
- -
calendar2-minus-fill
-
-
- -
calendar2-minus
-
-
- -
calendar2-month-fill
-
-
- -
calendar2-month
-
-
- -
calendar2-plus-fill
-
-
- -
calendar2-plus
-
-
- -
calendar2-range-fill
-
-
- -
calendar2-range
-
-
- -
calendar2-week-fill
-
-
- -
calendar2-week
-
-
- -
calendar2-x-fill
-
-
- -
calendar2-x
-
-
- -
calendar2
-
-
- -
calendar3-event-fill
-
-
- -
calendar3-event
-
-
- -
calendar3-fill
-
-
- -
calendar3-range-fill
-
-
- -
calendar3-range
-
-
- -
calendar3-week-fill
-
-
- -
calendar3-week
-
-
- -
calendar3
-
-
- -
calendar4-event
-
-
- -
calendar4-range
-
-
- -
calendar4-week
-
-
- -
calendar4
-
-
- -
camera-fill
-
-
- -
camera-reels-fill
-
-
- -
camera-reels
-
-
- -
camera-video-fill
-
-
- -
camera-video-off-fill
-
-
- -
camera-video-off
-
-
- -
camera-video
-
-
- -
camera
-
-
- -
camera2
-
-
- -
capslock-fill
-
-
- -
capslock
-
-
- -
capsule-pill
-
-
- -
capsule
-
-
- -
car-front-fill
-
-
- -
car-front
-
-
- -
card-checklist
-
-
- -
card-heading
-
-
- -
card-image
-
-
- -
card-list
-
-
- -
card-text
-
-
- -
caret-down-fill
-
-
- -
caret-down-square-fill
-
-
- -
caret-down-square
-
-
- -
caret-down
-
-
- -
caret-left-fill
-
-
- -
caret-left-square-fill
-
-
- -
caret-left-square
-
-
- -
caret-left
-
-
- -
caret-right-fill
-
-
- -
caret-right-square-fill
-
-
- -
caret-right-square
-
-
- -
caret-right
-
-
- -
caret-up-fill
-
-
- -
caret-up-square-fill
-
-
- -
caret-up-square
-
-
- -
caret-up
-
-
- -
cart-check-fill
-
-
- -
cart-check
-
-
- -
cart-dash-fill
-
-
- -
cart-dash
-
-
- -
cart-fill
-
-
- -
cart-plus-fill
-
-
- -
cart-plus
-
-
- -
cart-x-fill
-
-
- -
cart-x
-
-
- -
cart
-
-
- -
cart2
-
-
- -
cart3
-
-
- -
cart4
-
-
- -
cash-coin
-
-
- -
cash-stack
-
-
- -
cash
-
-
- -
cassette-fill
-
-
- -
cassette
-
-
- -
cast
-
-
- -
cc-circle-fill
-
-
- -
cc-circle
-
-
- -
cc-square-fill
-
-
- -
cc-square
-
-
- -
chat-dots-fill
-
-
- -
chat-dots
-
-
- -
chat-fill
-
-
- -
chat-heart-fill
-
-
- -
chat-heart
-
-
- -
chat-left-dots-fill
-
-
- -
chat-left-dots
-
-
- -
chat-left-fill
-
-
- -
chat-left-heart-fill
-
-
- -
chat-left-heart
-
-
- -
chat-left-quote-fill
-
-
- -
chat-left-quote
-
-
- -
chat-left-text-fill
-
-
- -
chat-left-text
-
-
- -
chat-left
-
-
- -
chat-quote-fill
-
-
- -
chat-quote
-
-
- -
chat-right-dots-fill
-
-
- -
chat-right-dots
-
-
- -
chat-right-fill
-
-
- -
chat-right-heart-fill
-
-
- -
chat-right-heart
-
-
- -
chat-right-quote-fill
-
-
- -
chat-right-quote
-
-
- -
chat-right-text-fill
-
-
- -
chat-right-text
-
-
- -
chat-right
-
-
- -
chat-square-dots-fill
-
-
- -
chat-square-dots
-
-
- -
chat-square-fill
-
-
- -
chat-square-heart-fill
-
-
- -
chat-square-heart
-
-
- -
chat-square-quote-fill
-
-
- -
chat-square-quote
-
-
- -
chat-square-text-fill
-
-
- -
chat-square-text
-
-
- -
chat-square
-
-
- -
chat-text-fill
-
-
- -
chat-text
-
-
- -
chat
-
-
- -
check-all
-
-
- -
check-circle-fill
-
-
- -
check-circle
-
-
- -
check-lg
-
-
- -
check-square-fill
-
-
- -
check-square
-
-
- -
check
-
-
- -
check2-all
-
-
- -
check2-circle
-
-
- -
check2-square
-
-
- -
check2
-
-
- -
chevron-bar-contract
-
-
- -
chevron-bar-down
-
-
- -
chevron-bar-expand
-
-
- -
chevron-bar-left
-
-
- -
chevron-bar-right
-
-
- -
chevron-bar-up
-
-
- -
chevron-compact-down
-
-
- -
chevron-compact-left
-
-
- -
chevron-compact-right
-
-
- -
chevron-compact-up
-
-
- -
chevron-contract
-
-
- -
chevron-double-down
-
-
- -
chevron-double-left
-
-
- -
chevron-double-right
-
-
- -
chevron-double-up
-
-
- -
chevron-down
-
-
- -
chevron-expand
-
-
- -
chevron-left
-
-
- -
chevron-right
-
-
- -
chevron-up
-
-
- -
circle-fill
-
-
- -
circle-half
-
-
- -
circle-square
-
-
- -
circle
-
-
- -
clipboard-check-fill
-
-
- -
clipboard-check
-
-
- -
clipboard-data-fill
-
-
- -
clipboard-data
-
-
- -
clipboard-fill
-
-
- -
clipboard-heart-fill
-
-
- -
clipboard-heart
-
-
- -
clipboard-minus-fill
-
-
- -
clipboard-minus
-
-
- -
clipboard-plus-fill
-
-
- -
clipboard-plus
-
-
- -
clipboard-pulse
-
-
- -
clipboard-x-fill
-
-
- -
clipboard-x
-
-
- -
clipboard
-
-
- -
clipboard2-check-fill
-
-
- -
clipboard2-check
-
-
- -
clipboard2-data-fill
-
-
- -
clipboard2-data
-
-
- -
clipboard2-fill
-
-
- -
clipboard2-heart-fill
-
-
- -
clipboard2-heart
-
-
- -
clipboard2-minus-fill
-
-
- -
clipboard2-minus
-
-
- -
clipboard2-plus-fill
-
-
- -
clipboard2-plus
-
-
- -
clipboard2-pulse-fill
-
-
- -
clipboard2-pulse
-
-
- -
clipboard2-x-fill
-
-
- -
clipboard2-x
-
-
- -
clipboard2
-
-
- -
clock-fill
-
-
- -
clock-history
-
-
- -
clock
-
-
- -
cloud-arrow-down-fill
-
-
- -
cloud-arrow-down
-
-
- -
cloud-arrow-up-fill
-
-
- -
cloud-arrow-up
-
-
- -
cloud-check-fill
-
-
- -
cloud-check
-
-
- -
cloud-download-fill
-
-
- -
cloud-download
-
-
- -
cloud-drizzle-fill
-
-
- -
cloud-drizzle
-
-
- -
cloud-fill
-
-
- -
cloud-fog-fill
-
-
- -
cloud-fog
-
-
- -
cloud-fog2-fill
-
-
- -
cloud-fog2
-
-
- -
cloud-hail-fill
-
-
- -
cloud-hail
-
-
- -
cloud-haze-fill
-
-
- -
cloud-haze
-
-
- -
cloud-haze2-fill
-
-
- -
cloud-haze2
-
-
- -
cloud-lightning-fill
-
-
- -
cloud-lightning-rain-fill
-
-
- -
cloud-lightning-rain
-
-
- -
cloud-lightning
-
-
- -
cloud-minus-fill
-
-
- -
cloud-minus
-
-
- -
cloud-moon-fill
-
-
- -
cloud-moon
-
-
- -
cloud-plus-fill
-
-
- -
cloud-plus
-
-
- -
cloud-rain-fill
-
-
- -
cloud-rain-heavy-fill
-
-
- -
cloud-rain-heavy
-
-
- -
cloud-rain
-
-
- -
cloud-slash-fill
-
-
- -
cloud-slash
-
-
- -
cloud-sleet-fill
-
-
- -
cloud-sleet
-
-
- -
cloud-snow-fill
-
-
- -
cloud-snow
-
-
- -
cloud-sun-fill
-
-
- -
cloud-sun
-
-
- -
cloud-upload-fill
-
-
- -
cloud-upload
-
-
- -
cloud
-
-
- -
clouds-fill
-
-
- -
clouds
-
-
- -
cloudy-fill
-
-
- -
cloudy
-
-
- -
code-slash
-
-
- -
code-square
-
-
- -
code
-
-
- -
coin
-
-
- -
collection-fill
-
-
- -
collection-play-fill
-
-
- -
collection-play
-
-
- -
collection
-
-
- -
columns-gap
-
-
- -
columns
-
-
- -
command
-
-
- -
compass-fill
-
-
- -
compass
-
-
- -
cone-striped
-
-
- -
cone
-
-
- -
controller
-
-
- -
cpu-fill
-
-
- -
cpu
-
-
- -
credit-card-2-back-fill
-
-
- -
credit-card-2-back
-
-
- -
credit-card-2-front-fill
-
-
- -
credit-card-2-front
-
-
- -
credit-card-fill
-
-
- -
credit-card
-
-
- -
crop
-
-
- -
cup-fill
-
-
- -
cup-hot-fill
-
-
- -
cup-hot
-
-
- -
cup-straw
-
-
- -
cup
-
-
- -
currency-bitcoin
-
-
- -
currency-dollar
-
-
- -
currency-euro
-
-
- -
currency-exchange
-
-
- -
currency-pound
-
-
- -
currency-rupee
-
-
- -
currency-yen
-
-
- -
cursor-fill
-
-
- -
cursor-text
-
-
- -
cursor
-
-
- -
dash-circle-dotted
-
-
- -
dash-circle-fill
-
-
- -
dash-circle
-
-
- -
dash-lg
-
-
- -
dash-square-dotted
-
-
- -
dash-square-fill
-
-
- -
dash-square
-
-
- -
dash
-
-
- -
device-hdd-fill
-
-
- -
device-hdd
-
-
- -
device-ssd-fill
-
-
- -
device-ssd
-
-
- -
diagram-2-fill
-
-
- -
diagram-2
-
-
- -
diagram-3-fill
-
-
- -
diagram-3
-
-
- -
diamond-fill
-
-
- -
diamond-half
-
-
- -
diamond
-
-
- -
dice-1-fill
-
-
- -
dice-1
-
-
- -
dice-2-fill
-
-
- -
dice-2
-
-
- -
dice-3-fill
-
-
- -
dice-3
-
-
- -
dice-4-fill
-
-
- -
dice-4
-
-
- -
dice-5-fill
-
-
- -
dice-5
-
-
- -
dice-6-fill
-
-
- -
dice-6
-
-
- -
disc-fill
-
-
- -
disc
-
-
- -
discord
-
-
- -
display-fill
-
-
- -
display
-
-
- -
displayport-fill
-
-
- -
displayport
-
-
- -
distribute-horizontal
-
-
- -
distribute-vertical
-
-
- -
door-closed-fill
-
-
- -
door-closed
-
-
- -
door-open-fill
-
-
- -
door-open
-
-
- -
dot
-
-
- -
download
-
-
- -
dpad-fill
-
-
- -
dpad
-
-
- -
dribbble
-
-
- -
dropbox
-
-
- -
droplet-fill
-
-
- -
droplet-half
-
-
- -
droplet
-
-
- -
ear-fill
-
-
- -
ear
-
-
- -
earbuds
-
-
- -
easel-fill
-
-
- -
easel
-
-
- -
easel2-fill
-
-
- -
easel2
-
-
- -
easel3-fill
-
-
- -
easel3
-
-
- -
egg-fill
-
-
- -
egg-fried
-
-
- -
egg
-
-
- -
eject-fill
-
-
- -
eject
-
-
- -
emoji-angry-fill
-
-
- -
emoji-angry
-
-
- -
emoji-dizzy-fill
-
-
- -
emoji-dizzy
-
-
- -
emoji-expressionless-fill
-
-
- -
emoji-expressionless
-
-
- -
emoji-frown-fill
-
-
- -
emoji-frown
-
-
- -
emoji-heart-eyes-fill
-
-
- -
emoji-heart-eyes
-
-
- -
emoji-kiss-fill
-
-
- -
emoji-kiss
-
-
- -
emoji-laughing-fill
-
-
- -
emoji-laughing
-
-
- -
emoji-neutral-fill
-
-
- -
emoji-neutral
-
-
- -
emoji-smile-fill
-
-
- -
emoji-smile-upside-down-fill
-
-
- -
emoji-smile-upside-down
-
-
- -
emoji-smile
-
-
- -
emoji-sunglasses-fill
-
-
- -
emoji-sunglasses
-
-
- -
emoji-wink-fill
-
-
- -
emoji-wink
-
-
- -
envelope-check-fill
-
-
- -
envelope-check
-
-
- -
envelope-dash-fill
-
-
- -
envelope-dash
-
-
- -
envelope-exclamation-fill
-
-
- -
envelope-exclamation
-
-
- -
envelope-fill
-
-
- -
envelope-heart-fill
-
-
- -
envelope-heart
-
-
- -
envelope-open-fill
-
-
- -
envelope-open-heart-fill
-
-
- -
envelope-open-heart
-
-
- -
envelope-open
-
-
- -
envelope-paper-fill
-
-
- -
envelope-paper-heart-fill
-
-
- -
envelope-paper-heart
-
-
- -
envelope-paper
-
-
- -
envelope-plus-fill
-
-
- -
envelope-plus
-
-
- -
envelope-slash-fill
-
-
- -
envelope-slash
-
-
- -
envelope-x-fill
-
-
- -
envelope-x
-
-
- -
envelope
-
-
- -
eraser-fill
-
-
- -
eraser
-
-
- -
escape
-
-
- -
ethernet
-
-
- -
ev-station-fill
-
-
- -
ev-station
-
-
- -
exclamation-circle-fill
-
-
- -
exclamation-circle
-
-
- -
exclamation-diamond-fill
-
-
- -
exclamation-diamond
-
-
- -
exclamation-lg
-
-
- -
exclamation-octagon-fill
-
-
- -
exclamation-octagon
-
-
- -
exclamation-square-fill
-
-
- -
exclamation-square
-
-
- -
exclamation-triangle-fill
-
-
- -
exclamation-triangle
-
-
- -
exclamation
-
-
- -
exclude
-
-
- -
explicit-fill
-
-
- -
explicit
-
-
- -
eye-fill
-
-
- -
eye-slash-fill
-
-
- -
eye-slash
-
-
- -
eye
-
-
- -
eyedropper
-
-
- -
eyeglasses
-
-
- -
facebook
-
-
- -
fan
-
-
- -
fast-forward-btn-fill
-
-
- -
fast-forward-btn
-
-
- -
fast-forward-circle-fill
-
-
- -
fast-forward-circle
-
-
- -
fast-forward-fill
-
-
- -
fast-forward
-
-
- -
file-arrow-down-fill
-
-
- -
file-arrow-down
-
-
- -
file-arrow-up-fill
-
-
- -
file-arrow-up
-
-
- -
file-bar-graph-fill
-
-
- -
file-bar-graph
-
-
- -
file-binary-fill
-
-
- -
file-binary
-
-
- -
file-break-fill
-
-
- -
file-break
-
-
- -
file-check-fill
-
-
- -
file-check
-
-
- -
file-code-fill
-
-
- -
file-code
-
-
- -
file-diff-fill
-
-
- -
file-diff
-
-
- -
file-earmark-arrow-down-fill
-
-
- -
file-earmark-arrow-down
-
-
- -
file-earmark-arrow-up-fill
-
-
- -
file-earmark-arrow-up
-
-
- -
file-earmark-bar-graph-fill
-
-
- -
file-earmark-bar-graph
-
-
- -
file-earmark-binary-fill
-
-
- -
file-earmark-binary
-
-
- -
file-earmark-break-fill
-
-
- -
file-earmark-break
-
-
- -
file-earmark-check-fill
-
-
- -
file-earmark-check
-
-
- -
file-earmark-code-fill
-
-
- -
file-earmark-code
-
-
- -
file-earmark-diff-fill
-
-
- -
file-earmark-diff
-
-
- -
file-earmark-easel-fill
-
-
- -
file-earmark-easel
-
-
- -
file-earmark-excel-fill
-
-
- -
file-earmark-excel
-
-
- -
file-earmark-fill
-
-
- -
file-earmark-font-fill
-
-
- -
file-earmark-font
-
-
- -
file-earmark-image-fill
-
-
- -
file-earmark-image
-
-
- -
file-earmark-lock-fill
-
-
- -
file-earmark-lock
-
-
- -
file-earmark-lock2-fill
-
-
- -
file-earmark-lock2
-
-
- -
file-earmark-medical-fill
-
-
- -
file-earmark-medical
-
-
- -
file-earmark-minus-fill
-
-
- -
file-earmark-minus
-
-
- -
file-earmark-music-fill
-
-
- -
file-earmark-music
-
-
- -
file-earmark-pdf-fill
-
-
- -
file-earmark-pdf
-
-
- -
file-earmark-person-fill
-
-
- -
file-earmark-person
-
-
- -
file-earmark-play-fill
-
-
- -
file-earmark-play
-
-
- -
file-earmark-plus-fill
-
-
- -
file-earmark-plus
-
-
- -
file-earmark-post-fill
-
-
- -
file-earmark-post
-
-
- -
file-earmark-ppt-fill
-
-
- -
file-earmark-ppt
-
-
- -
file-earmark-richtext-fill
-
-
- -
file-earmark-richtext
-
-
- -
file-earmark-ruled-fill
-
-
- -
file-earmark-ruled
-
-
- -
file-earmark-slides-fill
-
-
- -
file-earmark-slides
-
-
- -
file-earmark-spreadsheet-fill
-
-
- -
file-earmark-spreadsheet
-
-
- -
file-earmark-text-fill
-
-
- -
file-earmark-text
-
-
- -
file-earmark-word-fill
-
-
- -
file-earmark-word
-
-
- -
file-earmark-x-fill
-
-
- -
file-earmark-x
-
-
- -
file-earmark-zip-fill
-
-
- -
file-earmark-zip
-
-
- -
file-earmark
-
-
- -
file-easel-fill
-
-
- -
file-easel
-
-
- -
file-excel-fill
-
-
- -
file-excel
-
-
- -
file-fill
-
-
- -
file-font-fill
-
-
- -
file-font
-
-
- -
file-image-fill
-
-
- -
file-image
-
-
- -
file-lock-fill
-
-
- -
file-lock
-
-
- -
file-lock2-fill
-
-
- -
file-lock2
-
-
- -
file-medical-fill
-
-
- -
file-medical
-
-
- -
file-minus-fill
-
-
- -
file-minus
-
-
- -
file-music-fill
-
-
- -
file-music
-
-
- -
file-pdf-fill
-
-
- -
file-pdf
-
-
- -
file-person-fill
-
-
- -
file-person
-
-
- -
file-play-fill
-
-
- -
file-play
-
-
- -
file-plus-fill
-
-
- -
file-plus
-
-
- -
file-post-fill
-
-
- -
file-post
-
-
- -
file-ppt-fill
-
-
- -
file-ppt
-
-
- -
file-richtext-fill
-
-
- -
file-richtext
-
-
- -
file-ruled-fill
-
-
- -
file-ruled
-
-
- -
file-slides-fill
-
-
- -
file-slides
-
-
- -
file-spreadsheet-fill
-
-
- -
file-spreadsheet
-
-
- -
file-text-fill
-
-
- -
file-text
-
-
- -
file-word-fill
-
-
- -
file-word
-
-
- -
file-x-fill
-
-
- -
file-x
-
-
- -
file-zip-fill
-
-
- -
file-zip
-
-
- -
file
-
-
- -
files-alt
-
-
- -
files
-
-
- -
filetype-aac
-
-
- -
filetype-ai
-
-
- -
filetype-bmp
-
-
- -
filetype-cs
-
-
- -
filetype-css
-
-
- -
filetype-csv
-
-
- -
filetype-doc
-
-
- -
filetype-docx
-
-
- -
filetype-exe
-
-
- -
filetype-gif
-
-
- -
filetype-heic
-
-
- -
filetype-html
-
-
- -
filetype-java
-
-
- -
filetype-jpg
-
-
- -
filetype-js
-
-
- -
filetype-json
-
-
- -
filetype-jsx
-
-
- -
filetype-key
-
-
- -
filetype-m4p
-
-
- -
filetype-md
-
-
- -
filetype-mdx
-
-
- -
filetype-mov
-
-
- -
filetype-mp3
-
-
- -
filetype-mp4
-
-
- -
filetype-otf
-
-
- -
filetype-pdf
-
-
- -
filetype-php
-
-
- -
filetype-png
-
-
- -
filetype-ppt
-
-
- -
filetype-pptx
-
-
- -
filetype-psd
-
-
- -
filetype-py
-
-
- -
filetype-raw
-
-
- -
filetype-rb
-
-
- -
filetype-sass
-
-
- -
filetype-scss
-
-
- -
filetype-sh
-
-
- -
filetype-sql
-
-
- -
filetype-svg
-
-
- -
filetype-tiff
-
-
- -
filetype-tsx
-
-
- -
filetype-ttf
-
-
- -
filetype-txt
-
-
- -
filetype-wav
-
-
- -
filetype-woff
-
-
- -
filetype-xls
-
-
- -
filetype-xlsx
-
-
- -
filetype-xml
-
-
- -
filetype-yml
-
-
- -
film
-
-
- -
filter-circle-fill
-
-
- -
filter-circle
-
-
- -
filter-left
-
-
- -
filter-right
-
-
- -
filter-square-fill
-
-
- -
filter-square
-
-
- -
filter
-
-
- -
fingerprint
-
-
- -
fire
-
-
- -
flag-fill
-
-
- -
flag
-
-
- -
flower1
-
-
- -
flower2
-
-
- -
flower3
-
-
- -
folder-check
-
-
- -
folder-fill
-
-
- -
folder-minus
-
-
- -
folder-plus
-
-
- -
folder-symlink-fill
-
-
- -
folder-symlink
-
-
- -
folder-x
-
-
- -
folder
-
-
- -
folder2-open
-
-
- -
folder2
-
-
- -
fonts
-
-
- -
forward-fill
-
-
- -
forward
-
-
- -
front
-
-
- -
fuel-pump-diesel-fill
-
-
- -
fuel-pump-diesel
-
-
- -
fuel-pump-fill
-
-
- -
fuel-pump
-
-
- -
fullscreen-exit
-
-
- -
fullscreen
-
-
- -
funnel-fill
-
-
- -
funnel
-
-
- -
gear-fill
-
-
- -
gear-wide-connected
-
-
- -
gear-wide
-
-
- -
gear
-
-
- -
gem
-
-
- -
gender-ambiguous
-
-
- -
gender-female
-
-
- -
gender-male
-
-
- -
gender-trans
-
-
- -
geo-alt-fill
-
-
- -
geo-alt
-
-
- -
geo-fill
-
-
- -
geo
-
-
- -
gift-fill
-
-
- -
gift
-
-
- -
git
-
-
- -
github
-
-
- -
globe
-
-
- -
globe2
-
-
- -
google-play
-
-
- -
google
-
-
- -
gpu-card
-
-
- -
graph-down-arrow
-
-
- -
graph-down
-
-
- -
graph-up-arrow
-
-
- -
graph-up
-
-
- -
grid-1x2-fill
-
-
- -
grid-1x2
-
-
- -
grid-3x2-gap-fill
-
-
- -
grid-3x2-gap
-
-
- -
grid-3x2
-
-
- -
grid-3x3-gap-fill
-
-
- -
grid-3x3-gap
-
-
- -
grid-3x3
-
-
- -
grid-fill
-
-
- -
grid
-
-
- -
grip-horizontal
-
-
- -
grip-vertical
-
-
- -
h-circle-fill
-
-
- -
h-circle
-
-
- -
h-square-fill
-
-
- -
h-square
-
-
- -
hammer
-
-
- -
hand-index-fill
-
-
- -
hand-index-thumb-fill
-
-
- -
hand-index-thumb
-
-
- -
hand-index
-
-
- -
hand-thumbs-down-fill
-
-
- -
hand-thumbs-down
-
-
- -
hand-thumbs-up-fill
-
-
- -
hand-thumbs-up
-
-
- -
handbag-fill
-
-
- -
handbag
-
-
- -
hash
-
-
- -
hdd-fill
-
-
- -
hdd-network-fill
-
-
- -
hdd-network
-
-
- -
hdd-rack-fill
-
-
- -
hdd-rack
-
-
- -
hdd-stack-fill
-
-
- -
hdd-stack
-
-
- -
hdd
-
-
- -
hdmi-fill
-
-
- -
hdmi
-
-
- -
headphones
-
-
- -
headset-vr
-
-
- -
headset
-
-
- -
heart-arrow
-
-
- -
heart-fill
-
-
- -
heart-half
-
-
- -
heart-pulse-fill
-
-
- -
heart-pulse
-
-
- -
heart
-
-
- -
heartbreak-fill
-
-
- -
heartbreak
-
-
- -
hearts
-
-
- -
heptagon-fill
-
-
- -
heptagon-half
-
-
- -
heptagon
-
-
- -
hexagon-fill
-
-
- -
hexagon-half
-
-
- -
hexagon
-
-
- -
hospital-fill
-
-
- -
hospital
-
-
- -
hourglass-bottom
-
-
- -
hourglass-split
-
-
- -
hourglass-top
-
-
- -
hourglass
-
-
- -
house-door-fill
-
-
- -
house-door
-
-
- -
house-fill
-
-
- -
house-heart-fill
-
-
- -
house-heart
-
-
- -
house
-
-
- -
hr
-
-
- -
hurricane
-
-
- -
hypnotize
-
-
- -
image-alt
-
-
- -
image-fill
-
-
- -
image
-
-
- -
images
-
-
- -
inbox-fill
-
-
- -
inbox
-
-
- -
inboxes-fill
-
-
- -
inboxes
-
-
- -
incognito
-
-
- -
indent
-
-
- -
infinity
-
-
- -
info-circle-fill
-
-
- -
info-circle
-
-
- -
info-lg
-
-
- -
info-square-fill
-
-
- -
info-square
-
-
- -
info
-
-
- -
input-cursor-text
-
-
- -
input-cursor
-
-
- -
instagram
-
-
- -
intersect
-
-
- -
journal-album
-
-
- -
journal-arrow-down
-
-
- -
journal-arrow-up
-
-
- -
journal-bookmark-fill
-
-
- -
journal-bookmark
-
-
- -
journal-check
-
-
- -
journal-code
-
-
- -
journal-medical
-
-
- -
journal-minus
-
-
- -
journal-plus
-
-
- -
journal-richtext
-
-
- -
journal-text
-
-
- -
journal-x
-
-
- -
journal
-
-
- -
journals
-
-
- -
joystick
-
-
- -
justify-left
-
-
- -
justify-right
-
-
- -
justify
-
-
- -
kanban-fill
-
-
- -
kanban
-
-
- -
key-fill
-
-
- -
key
-
-
- -
keyboard-fill
-
-
- -
keyboard
-
-
- -
ladder
-
-
- -
lamp-fill
-
-
- -
lamp
-
-
- -
laptop-fill
-
-
- -
laptop
-
-
- -
layer-backward
-
-
- -
layer-forward
-
-
- -
layers-fill
-
-
- -
layers-half
-
-
- -
layers
-
-
- -
layout-sidebar-inset-reverse
-
-
- -
layout-sidebar-inset
-
-
- -
layout-sidebar-reverse
-
-
- -
layout-sidebar
-
-
- -
layout-split
-
-
- -
layout-text-sidebar-reverse
-
-
- -
layout-text-sidebar
-
-
- -
layout-text-window-reverse
-
-
- -
layout-text-window
-
-
- -
layout-three-columns
-
-
- -
layout-wtf
-
-
- -
life-preserver
-
-
- -
lightbulb-fill
-
-
- -
lightbulb-off-fill
-
-
- -
lightbulb-off
-
-
- -
lightbulb
-
-
- -
lightning-charge-fill
-
-
- -
lightning-charge
-
-
- -
lightning-fill
-
-
- -
lightning
-
-
- -
line
-
-
- -
link-45deg
-
-
- -
link
-
-
- -
linkedin
-
-
- -
list-check
-
-
- -
list-columns-reverse
-
-
- -
list-columns
-
-
- -
list-nested
-
-
- -
list-ol
-
-
- -
list-stars
-
-
- -
list-task
-
-
- -
list-ul
-
-
- -
list
-
-
- -
lock-fill
-
-
- -
lock
-
-
- -
lungs-fill
-
-
- -
lungs
-
-
- -
magic
-
-
- -
magnet-fill
-
-
- -
magnet
-
-
- -
mailbox
-
-
- -
mailbox2
-
-
- -
map-fill
-
-
- -
map
-
-
- -
markdown-fill
-
-
- -
markdown
-
-
- -
mask
-
-
- -
mastodon
-
-
- -
medium
-
-
- -
megaphone-fill
-
-
- -
megaphone
-
-
- -
memory
-
-
- -
menu-app-fill
-
-
- -
menu-app
-
-
- -
menu-button-fill
-
-
- -
menu-button-wide-fill
-
-
- -
menu-button-wide
-
-
- -
menu-button
-
-
- -
menu-down
-
-
- -
menu-up
-
-
- -
messenger
-
-
- -
meta
-
-
- -
mic-fill
-
-
- -
mic-mute-fill
-
-
- -
mic-mute
-
-
- -
mic
-
-
- -
microsoft-teams
-
-
- -
microsoft
-
-
- -
minecart-loaded
-
-
- -
minecart
-
-
- -
modem-fill
-
-
- -
modem
-
-
- -
moisture
-
-
- -
moon-fill
-
-
- -
moon-stars-fill
-
-
- -
moon-stars
-
-
- -
moon
-
-
- -
mortarboard-fill
-
-
- -
mortarboard
-
-
- -
motherboard-fill
-
-
- -
motherboard
-
-
- -
mouse-fill
-
-
- -
mouse
-
-
- -
mouse2-fill
-
-
- -
mouse2
-
-
- -
mouse3-fill
-
-
- -
mouse3
-
-
- -
music-note-beamed
-
-
- -
music-note-list
-
-
- -
music-note
-
-
- -
music-player-fill
-
-
- -
music-player
-
-
- -
newspaper
-
-
- -
nintendo-switch
-
-
- -
node-minus-fill
-
-
- -
node-minus
-
-
- -
node-plus-fill
-
-
- -
node-plus
-
-
- -
nut-fill
-
-
- -
nut
-
-
- -
octagon-fill
-
-
- -
octagon-half
-
-
- -
octagon
-
-
- -
optical-audio-fill
-
-
- -
optical-audio
-
-
- -
option
-
-
- -
outlet
-
-
- -
p-circle-fill
-
-
- -
p-circle
-
-
- -
p-square-fill
-
-
- -
p-square
-
-
- -
paint-bucket
-
-
- -
palette-fill
-
-
- -
palette
-
-
- -
palette2
-
-
- -
paperclip
-
-
- -
paragraph
-
-
- -
pass-fill
-
-
- -
pass
-
-
- -
patch-check-fill
-
-
- -
patch-check
-
-
- -
patch-exclamation-fill
-
-
- -
patch-exclamation
-
-
- -
patch-minus-fill
-
-
- -
patch-minus
-
-
- -
patch-plus-fill
-
-
- -
patch-plus
-
-
- -
patch-question-fill
-
-
- -
patch-question
-
-
- -
pause-btn-fill
-
-
- -
pause-btn
-
-
- -
pause-circle-fill
-
-
- -
pause-circle
-
-
- -
pause-fill
-
-
- -
pause
-
-
- -
paypal
-
-
- -
pc-display-horizontal
-
-
- -
pc-display
-
-
- -
pc-horizontal
-
-
- -
pc
-
-
- -
pci-card
-
-
- -
peace-fill
-
-
- -
peace
-
-
- -
pen-fill
-
-
- -
pen
-
-
- -
pencil-fill
-
-
- -
pencil-square
-
-
- -
pencil
-
-
- -
pentagon-fill
-
-
- -
pentagon-half
-
-
- -
pentagon
-
-
- -
people-fill
-
-
- -
people
-
-
- -
percent
-
-
- -
person-badge-fill
-
-
- -
person-badge
-
-
- -
person-bounding-box
-
-
- -
person-check-fill
-
-
- -
person-check
-
-
- -
person-circle
-
-
- -
person-dash-fill
-
-
- -
person-dash
-
-
- -
person-fill
-
-
- -
person-heart
-
-
- -
person-hearts
-
-
- -
person-lines-fill
-
-
- -
person-plus-fill
-
-
- -
person-plus
-
-
- -
person-rolodex
-
-
- -
person-square
-
-
- -
person-video
-
-
- -
person-video2
-
-
- -
person-video3
-
-
- -
person-workspace
-
-
- -
person-x-fill
-
-
- -
person-x
-
-
- -
person
-
-
- -
phone-fill
-
-
- -
phone-flip
-
-
- -
phone-landscape-fill
-
-
- -
phone-landscape
-
-
- -
phone-vibrate-fill
-
-
- -
phone-vibrate
-
-
- -
phone
-
-
- -
pie-chart-fill
-
-
- -
pie-chart
-
-
- -
piggy-bank-fill
-
-
- -
piggy-bank
-
-
- -
pin-angle-fill
-
-
- -
pin-angle
-
-
- -
pin-fill
-
-
- -
pin-map-fill
-
-
- -
pin-map
-
-
- -
pin
-
-
- -
pinterest
-
-
- -
pip-fill
-
-
- -
pip
-
-
- -
play-btn-fill
-
-
- -
play-btn
-
-
- -
play-circle-fill
-
-
- -
play-circle
-
-
- -
play-fill
-
-
- -
play
-
-
- -
playstation
-
-
- -
plug-fill
-
-
- -
plug
-
-
- -
plugin
-
-
- -
plus-circle-dotted
-
-
- -
plus-circle-fill
-
-
- -
plus-circle
-
-
- -
plus-lg
-
-
- -
plus-slash-minus
-
-
- -
plus-square-dotted
-
-
- -
plus-square-fill
-
-
- -
plus-square
-
-
- -
plus
-
-
- -
postage-fill
-
-
- -
postage-heart-fill
-
-
- -
postage-heart
-
-
- -
postage
-
-
- -
postcard-fill
-
-
- -
postcard-heart-fill
-
-
- -
postcard-heart
-
-
- -
postcard
-
-
- -
power
-
-
- -
prescription
-
-
- -
prescription2
-
-
- -
printer-fill
-
-
- -
printer
-
-
- -
projector-fill
-
-
- -
projector
-
-
- -
puzzle-fill
-
-
- -
puzzle
-
-
- -
qr-code-scan
-
-
- -
qr-code
-
-
- -
question-circle-fill
-
-
- -
question-circle
-
-
- -
question-diamond-fill
-
-
- -
question-diamond
-
-
- -
question-lg
-
-
- -
question-octagon-fill
-
-
- -
question-octagon
-
-
- -
question-square-fill
-
-
- -
question-square
-
-
- -
question
-
-
- -
quora
-
-
- -
quote
-
-
- -
r-circle-fill
-
-
- -
r-circle
-
-
- -
r-square-fill
-
-
- -
r-square
-
-
- -
radioactive
-
-
- -
rainbow
-
-
- -
receipt-cutoff
-
-
- -
receipt
-
-
- -
reception-0
-
-
- -
reception-1
-
-
- -
reception-2
-
-
- -
reception-3
-
-
- -
reception-4
-
-
- -
record-btn-fill
-
-
- -
record-btn
-
-
- -
record-circle-fill
-
-
- -
record-circle
-
-
- -
record-fill
-
-
- -
record
-
-
- -
record2-fill
-
-
- -
record2
-
-
- -
recycle
-
-
- -
reddit
-
-
- -
repeat-1
-
-
- -
repeat
-
-
- -
reply-all-fill
-
-
- -
reply-all
-
-
- -
reply-fill
-
-
- -
reply
-
-
- -
rewind-btn-fill
-
-
- -
rewind-btn
-
-
- -
rewind-circle-fill
-
-
- -
rewind-circle
-
-
- -
rewind-fill
-
-
- -
rewind
-
-
- -
robot
-
-
- -
router-fill
-
-
- -
router
-
-
- -
rss-fill
-
-
- -
rss
-
-
- -
rulers
-
-
- -
safe-fill
-
-
- -
safe
-
-
- -
safe2-fill
-
-
- -
safe2
-
-
- -
save-fill
-
-
- -
save
-
-
- -
save2-fill
-
-
- -
save2
-
-
- -
scissors
-
-
- -
screwdriver
-
-
- -
sd-card-fill
-
-
- -
sd-card
-
-
- -
search-heart-fill
-
-
- -
search-heart
-
-
- -
search
-
-
- -
segmented-nav
-
-
- -
send-check-fill
-
-
- -
send-check
-
-
- -
send-dash-fill
-
-
- -
send-dash
-
-
- -
send-exclamation-fill
-
-
- -
send-exclamation
-
-
- -
send-fill
-
-
- -
send-plus-fill
-
-
- -
send-plus
-
-
- -
send-slash-fill
-
-
- -
send-slash
-
-
- -
send-x-fill
-
-
- -
send-x
-
-
- -
send
-
-
- -
server
-
-
- -
share-fill
-
-
- -
share
-
-
- -
shield-check
-
-
- -
shield-exclamation
-
-
- -
shield-fill-check
-
-
- -
shield-fill-exclamation
-
-
- -
shield-fill-minus
-
-
- -
shield-fill-plus
-
-
- -
shield-fill-x
-
-
- -
shield-fill
-
-
- -
shield-lock-fill
-
-
- -
shield-lock
-
-
- -
shield-minus
-
-
- -
shield-plus
-
-
- -
shield-shaded
-
-
- -
shield-slash-fill
-
-
- -
shield-slash
-
-
- -
shield-x
-
-
- -
shield
-
-
- -
shift-fill
-
-
- -
shift
-
-
- -
shop-window
-
-
- -
shop
-
-
- -
shuffle
-
-
- -
sign-stop-fill
-
-
- -
sign-stop-lights-fill
-
-
- -
sign-stop-lights
-
-
- -
sign-stop
-
-
- -
sign-turn-left-fill
-
-
- -
sign-turn-left
-
-
- -
sign-turn-right-fill
-
-
- -
sign-turn-right
-
-
- -
sign-turn-slight-left-fill
-
-
- -
sign-turn-slight-left
-
-
- -
sign-turn-slight-right-fill
-
-
- -
sign-turn-slight-right
-
-
- -
sign-yield-fill
-
-
- -
sign-yield
-
-
- -
signal
-
-
- -
signpost-2-fill
-
-
- -
signpost-2
-
-
- -
signpost-fill
-
-
- -
signpost-split-fill
-
-
- -
signpost-split
-
-
- -
signpost
-
-
- -
sim-fill
-
-
- -
sim
-
-
- -
skip-backward-btn-fill
-
-
- -
skip-backward-btn
-
-
- -
skip-backward-circle-fill
-
-
- -
skip-backward-circle
-
-
- -
skip-backward-fill
-
-
- -
skip-backward
-
-
- -
skip-end-btn-fill
-
-
- -
skip-end-btn
-
-
- -
skip-end-circle-fill
-
-
- -
skip-end-circle
-
-
- -
skip-end-fill
-
-
- -
skip-end
-
-
- -
skip-forward-btn-fill
-
-
- -
skip-forward-btn
-
-
- -
skip-forward-circle-fill
-
-
- -
skip-forward-circle
-
-
- -
skip-forward-fill
-
-
- -
skip-forward
-
-
- -
skip-start-btn-fill
-
-
- -
skip-start-btn
-
-
- -
skip-start-circle-fill
-
-
- -
skip-start-circle
-
-
- -
skip-start-fill
-
-
- -
skip-start
-
-
- -
skype
-
-
- -
slack
-
-
- -
slash-circle-fill
-
-
- -
slash-circle
-
-
- -
slash-lg
-
-
- -
slash-square-fill
-
-
- -
slash-square
-
-
- -
slash
-
-
- -
sliders
-
-
- -
sliders2-vertical
-
-
- -
sliders2
-
-
- -
smartwatch
-
-
- -
snapchat
-
-
- -
snow
-
-
- -
snow2
-
-
- -
snow3
-
-
- -
sort-alpha-down-alt
-
-
- -
sort-alpha-down
-
-
- -
sort-alpha-up-alt
-
-
- -
sort-alpha-up
-
-
- -
sort-down-alt
-
-
- -
sort-down
-
-
- -
sort-numeric-down-alt
-
-
- -
sort-numeric-down
-
-
- -
sort-numeric-up-alt
-
-
- -
sort-numeric-up
-
-
- -
sort-up-alt
-
-
- -
sort-up
-
-
- -
soundwave
-
-
- -
speaker-fill
-
-
- -
speaker
-
-
- -
speedometer
-
-
- -
speedometer2
-
-
- -
spellcheck
-
-
- -
spotify
-
-
- -
square-fill
-
-
- -
square-half
-
-
- -
square
-
-
- -
stack-overflow
-
-
- -
stack
-
-
- -
star-fill
-
-
- -
star-half
-
-
- -
star
-
-
- -
stars
-
-
- -
steam
-
-
- -
stickies-fill
-
-
- -
stickies
-
-
- -
sticky-fill
-
-
- -
sticky
-
-
- -
stop-btn-fill
-
-
- -
stop-btn
-
-
- -
stop-circle-fill
-
-
- -
stop-circle
-
-
- -
stop-fill
-
-
- -
stop
-
-
- -
stoplights-fill
-
-
- -
stoplights
-
-
- -
stopwatch-fill
-
-
- -
stopwatch
-
-
- -
strava
-
-
- -
subtract
-
-
- -
suit-club-fill
-
-
- -
suit-club
-
-
- -
suit-diamond-fill
-
-
- -
suit-diamond
-
-
- -
suit-heart-fill
-
-
- -
suit-heart
-
-
- -
suit-spade-fill
-
-
- -
suit-spade
-
-
- -
sun-fill
-
-
- -
sun
-
-
- -
sunglasses
-
-
- -
sunrise-fill
-
-
- -
sunrise
-
-
- -
sunset-fill
-
-
- -
sunset
-
-
- -
symmetry-horizontal
-
-
- -
symmetry-vertical
-
-
- -
table
-
-
- -
tablet-fill
-
-
- -
tablet-landscape-fill
-
-
- -
tablet-landscape
-
-
- -
tablet
-
-
- -
tag-fill
-
-
- -
tag
-
-
- -
tags-fill
-
-
- -
tags
-
-
- -
telegram
-
-
- -
telephone-fill
-
-
- -
telephone-forward-fill
-
-
- -
telephone-forward
-
-
- -
telephone-inbound-fill
-
-
- -
telephone-inbound
-
-
- -
telephone-minus-fill
-
-
- -
telephone-minus
-
-
- -
telephone-outbound-fill
-
-
- -
telephone-outbound
-
-
- -
telephone-plus-fill
-
-
- -
telephone-plus
-
-
- -
telephone-x-fill
-
-
- -
telephone-x
-
-
- -
telephone
-
-
- -
terminal-dash
-
-
- -
terminal-fill
-
-
- -
terminal-plus
-
-
- -
terminal-split
-
-
- -
terminal-x
-
-
- -
terminal
-
-
- -
text-center
-
-
- -
text-indent-left
-
-
- -
text-indent-right
-
-
- -
text-left
-
-
- -
text-paragraph
-
-
- -
text-right
-
-
- -
textarea-resize
-
-
- -
textarea-t
-
-
- -
textarea
-
-
- -
thermometer-half
-
-
- -
thermometer-high
-
-
- -
thermometer-low
-
-
- -
thermometer-snow
-
-
- -
thermometer-sun
-
-
- -
thermometer
-
-
- -
three-dots-vertical
-
-
- -
three-dots
-
-
- -
thunderbolt-fill
-
-
- -
thunderbolt
-
-
- -
ticket-detailed-fill
-
-
- -
ticket-detailed
-
-
- -
ticket-fill
-
-
- -
ticket-perforated-fill
-
-
- -
ticket-perforated
-
-
- -
ticket
-
-
- -
tiktok
-
-
- -
toggle-off
-
-
- -
toggle-on
-
-
- -
toggle2-off
-
-
- -
toggle2-on
-
-
- -
toggles
-
-
- -
toggles2
-
-
- -
tools
-
-
- -
tornado
-
-
- -
train-freight-front-fill
-
-
- -
train-freight-front
-
-
- -
train-front-fill
-
-
- -
train-front
-
-
- -
train-lightrail-front-fill
-
-
- -
train-lightrail-front
-
-
- -
translate
-
-
- -
trash-fill
-
-
- -
trash
-
-
- -
trash2-fill
-
-
- -
trash2
-
-
- -
trash3-fill
-
-
- -
trash3
-
-
- -
tree-fill
-
-
- -
tree
-
-
- -
triangle-fill
-
-
- -
triangle-half
-
-
- -
triangle
-
-
- -
trophy-fill
-
-
- -
trophy
-
-
- -
tropical-storm
-
-
- -
truck-flatbed
-
-
- -
truck-front-fill
-
-
- -
truck-front
-
-
- -
truck
-
-
- -
tsunami
-
-
- -
tv-fill
-
-
- -
tv
-
-
- -
twitch
-
-
- -
twitter
-
-
- -
type-bold
-
-
- -
type-h1
-
-
- -
type-h2
-
-
- -
type-h3
-
-
- -
type-italic
-
-
- -
type-strikethrough
-
-
- -
type-underline
-
-
- -
type
-
-
- -
ubuntu
-
-
- -
ui-checks-grid
-
-
- -
ui-checks
-
-
- -
ui-radios-grid
-
-
- -
ui-radios
-
-
- -
umbrella-fill
-
-
- -
umbrella
-
-
- -
unindent
-
-
- -
union
-
-
- -
unity
-
-
- -
universal-access-circle
-
-
- -
universal-access
-
-
- -
unlock-fill
-
-
- -
unlock
-
-
- -
upc-scan
-
-
- -
upc
-
-
- -
upload
-
-
- -
usb-c-fill
-
-
- -
usb-c
-
-
- -
usb-drive-fill
-
-
- -
usb-drive
-
-
- -
usb-fill
-
-
- -
usb-micro-fill
-
-
- -
usb-micro
-
-
- -
usb-mini-fill
-
-
- -
usb-mini
-
-
- -
usb-plug-fill
-
-
- -
usb-plug
-
-
- -
usb-symbol
-
-
- -
usb
-
-
- -
valentine
-
-
- -
valentine2
-
-
- -
vector-pen
-
-
- -
view-list
-
-
- -
view-stacked
-
-
- -
vimeo
-
-
- -
vinyl-fill
-
-
- -
vinyl
-
-
- -
virus
-
-
- -
virus2
-
-
- -
voicemail
-
-
- -
volume-down-fill
-
-
- -
volume-down
-
-
- -
volume-mute-fill
-
-
- -
volume-mute
-
-
- -
volume-off-fill
-
-
- -
volume-off
-
-
- -
volume-up-fill
-
-
- -
volume-up
-
-
- -
vr
-
-
- -
wallet-fill
-
-
- -
wallet
-
-
- -
wallet2
-
-
- -
watch
-
-
- -
water
-
-
- -
webcam-fill
-
-
- -
webcam
-
-
- -
wechat
-
-
- -
whatsapp
-
-
- -
wifi-1
-
-
- -
wifi-2
-
-
- -
wifi-off
-
-
- -
wifi
-
-
- -
wind
-
-
- -
window-dash
-
-
- -
window-desktop
-
-
- -
window-dock
-
-
- -
window-fullscreen
-
-
- -
window-plus
-
-
- -
window-sidebar
-
-
- -
window-split
-
-
- -
window-stack
-
-
- -
window-x
-
-
- -
window
-
-
- -
windows
-
-
- -
wordpress
-
-
- -
wrench-adjustable-circle-fill
-
-
- -
wrench-adjustable-circle
-
-
- -
wrench-adjustable
-
-
- -
wrench
-
-
- -
x-circle-fill
-
-
- -
x-circle
-
-
- -
x-diamond-fill
-
-
- -
x-diamond
-
-
- -
x-lg
-
-
- -
x-octagon-fill
-
-
- -
x-octagon
-
-
- -
x-square-fill
-
-
- -
x-square
-
-
- -
x
-
-
- -
xbox
-
-
- -
yelp
-
-
- -
yin-yang
-
-
- -
youtube
-
-
- -
zoom-in
-
-
- -
zoom-out
-
-
- - - diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-circle-fill.svg new file mode 100644 index 000000000..2f5f026ce --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-circle-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-circle.svg new file mode 100644 index 000000000..5e84c8cf2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-circle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-square-fill.svg new file mode 100644 index 000000000..cca049bca --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-square-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-square.svg new file mode 100644 index 000000000..73b7bc168 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/0-square.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle-fill.svg index 5141c8f12..b10d0f3ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle.svg index 785af34d7..2aa21f9e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square-fill.svg index de579e6fe..2ce792fdb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square.svg index 4f57d79b7..773534a2f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/1-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/123.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/123.svg index 3ee3396cd..277d40520 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/123.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/123.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle-fill.svg index 03a925132..169df3a14 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle.svg index fea4a5630..00260b6c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square-fill.svg index a89e1f71e..2760ca773 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square.svg index 558c78b90..4a79ed6e1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/2-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle-fill.svg index 06d2ea54e..9aedc47b6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle.svg index 23c347954..c2fc5178d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square-fill.svg index c6890a381..20c71a762 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square.svg index b56b6844a..f0907ccb6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/3-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle-fill.svg index 199a5e28d..51dc8a580 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle.svg index 3af547d70..e25df5107 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square-fill.svg index 03b0f94a6..ce3aa94af 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square.svg index dd8545591..769102de0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/4-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle-fill.svg index e940e9880..915462dcc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle.svg index 47eefd087..5ef620d44 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square-fill.svg index 1a878609a..2b066bbe0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square.svg index 9c54c773e..ad3e28117 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/5-square.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle-fill.svg index 18f66ef5c..21b601ad9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle.svg index ab5f748d7..fc036b024 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square-fill.svg index d67fa52d6..908706d4d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square.svg index 79762c8bf..ce28209f7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/6-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle-fill.svg index bb4522bf8..0f4f6d9c5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle.svg index 0dc4685e5..796f45c3e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square-fill.svg index 8a4789cc4..604186dd9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square.svg index a314c4a8b..f26903292 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/7-square.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle-fill.svg index 15cd6b4ae..579846b2d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle.svg index fb1454264..2dbb93edf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square-fill.svg index 766d42ac0..f8efe93eb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square.svg index f450b1786..f29d225d4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/8-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle-fill.svg index 6ebd865c4..b19b3f5fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle.svg index 7c97f2940..ff919495f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square-fill.svg index daee3e84c..e2ca2c362 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square-fill.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square.svg index ade92337e..3410f7bdc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/9-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/activity.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/activity.svg index 1c45d1b44..1ca946ec6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/activity.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/activity.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines-fill.svg index b58d49ffc..3d8f185e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines.svg index 78b79342b..2efc31ecc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-engines.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-fill.svg index c8f2fce87..b1e4fa3a0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane.svg index 2e04c928c..5f937e1c0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/airplane.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm-fill.svg index bec569f99..a53c88fa7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm.svg index 53f7cbe99..27160b3bf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alarm.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alexa.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alexa.svg index a68f1d9d0..0827c41db 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alexa.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alexa.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-bottom.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-bottom.svg index d9484c03e..5c2569f07 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-bottom.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-bottom.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-center.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-center.svg index af0d75b0a..ea4290bce 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-center.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-center.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-end.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-end.svg index 28f861df2..15429bbd9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-end.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-end.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-middle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-middle.svg index 95c6598f3..82f680168 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-middle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-middle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-start.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-start.svg index a72ba984b..75dca501f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-start.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-start.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-top.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-top.svg index d2934f523..f354fc530 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-top.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/align-top.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alipay.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alipay.svg index df7def9d9..30b6fe7dd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alipay.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alipay.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alphabet-uppercase.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alphabet-uppercase.svg new file mode 100644 index 000000000..d0887b5d2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alphabet-uppercase.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alphabet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alphabet.svg new file mode 100644 index 000000000..5d097d7a5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alphabet.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alt.svg index 22b788648..2141bcb50 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/alt.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/amazon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/amazon.svg new file mode 100644 index 000000000..07525458e --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/amazon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/amd.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/amd.svg new file mode 100644 index 000000000..ef0757ce9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/amd.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android.svg index d8909520a..4c1f097cf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android2.svg index 37613cc60..f49c14ea2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/android2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app-indicator.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app-indicator.svg index 450a01127..b28e2195d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app-indicator.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app-indicator.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app.svg index 819df1bdc..b36130807 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/app.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/apple.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/apple.svg index b8bc2a015..58235c3fb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/apple.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/apple.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive-fill.svg index 077aa2973..e5ea32be2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive.svg index b41be300f..7bc5eb2aa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/archive.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-down.svg index 1193b5d5a..7459597ed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-left.svg index 1656b2286..4e17ab49d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-right.svg index a7d32ce4a..9fb6363ce 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-up.svg index 6c95e3de4..11be0dec4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-90deg-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-down.svg index fe18e3915..1b212d514 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-left.svg index f53a1fdd7..621bf2a2d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-right.svg index 5019863d7..0210410a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-up.svg index 090b6bd4c..d5510da2e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-bar-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-clockwise.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-clockwise.svg index b072eb097..324d5af1a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-clockwise.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-clockwise.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-counterclockwise.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-counterclockwise.svg index b0b23b9bb..3d9ff62ef 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-counterclockwise.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-counterclockwise.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle-fill.svg index 8e837c0b4..242eb58d8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle.svg index fe215b941..42e96b54e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle-fill.svg index bcebc12de..e7f4735c4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle.svg index 8b52276fd..f67491fbf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square-fill.svg index 57c099f10..6e03bf23d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square.svg index 08e00288e..1278d394e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left.svg index 96a6b08db..4011c77b8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle-fill.svg index 35ab8c26e..4ff73b6ae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle.svg index 1cd51bca4..054e83f68 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square-fill.svg index 3ccff9ba1..a556e0667 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square.svg index 5019b260b..4bfb67926 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right.svg index 80487bd0a..08aef1661 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-short.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-short.svg index 2fda34073..66b7fa1b6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-short.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-short.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square-fill.svg index ea8f14b49..c9020dc7b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square.svg index 633671f25..c492b71a7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-up.svg index a128d9bed..04cb3a535 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down.svg index 1344ca9dd..f66f74bad 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle-fill.svg index 2eebe62ae..ae19d97b6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle.svg index 39f86b8c6..f3246ea01 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-right.svg index 8aabd7bea..89c40034e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-short.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-short.svg index 13005fb27..abb15dd76 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-short.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-short.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square-fill.svg index 76dbe9e75..3ee717ebc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square.svg index 4db19b31e..8f09a48c9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left.svg index 9d885017c..587d4fec8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-repeat.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-repeat.svg index d0d71546a..b17dba47b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-repeat.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-repeat.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-left.svg index f6b125ede..3c13fc406 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-right.svg index 228e24b4a..60d282c91 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-return-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle-fill.svg index 336a34ea0..32c21ea3f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle.svg index 1339b5206..ad7293e1a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-short.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-short.svg index 4626398ce..fa238fffb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-short.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-short.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square-fill.svg index 55285ebe7..c7c8eec92 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square.svg index 7209ead8f..7a4b78b7c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right.svg index d4b878b34..236290439 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart-fill.svg index 1b3c30f68..e98bce15b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart.svg index f35287083..daf397bbb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-through-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle-fill.svg index ab0a54cce..9e7ef362c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle.svg index 9923ae3e7..e18a6890b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle-fill.svg index df6e194c6..e1e2e97d4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle.svg index dfdaf71fe..3101b65d2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square-fill.svg index 220169da8..e699865fa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square.svg index 9d3767f5b..4f31190bb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left.svg index da5bb6c1c..938d0d01f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle-fill.svg index ba547c827..0eb9b5440 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle.svg index f2fcabc7e..ed6ae4151 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square-fill.svg index 7454537b3..c2246ec7e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square.svg index 97544231d..b14940fb8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right.svg index 6924a38d0..7b3794b8d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-short.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-short.svg index 3863f1535..543089afb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-short.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-short.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square-fill.svg index bb51b25a7..9d7f65f4f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square.svg index d21f03eb6..bec15c49b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up.svg index c46d49eb4..951521a68 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrow-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-contract.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-contract.svg index d140e1943..1aa78751c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-contract.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-contract.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-expand.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-expand.svg index 3697f605b..578d3b66c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-expand.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-angle-expand.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-collapse-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-collapse-vertical.svg new file mode 100644 index 000000000..0fc447799 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-collapse-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-collapse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-collapse.svg index 353eed672..ca055c3e2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-collapse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-collapse.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-expand-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-expand-vertical.svg new file mode 100644 index 000000000..9dbfac458 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-expand-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-expand.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-expand.svg index 9880f2564..99eb276ca 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-expand.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-expand.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-fullscreen.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-fullscreen.svg index dc0acc3ca..7633e3f1d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-fullscreen.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-fullscreen.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-move.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-move.svg index eef62ef00..ef2b885fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-move.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-move.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-vertical.svg new file mode 100644 index 000000000..22f60fdc4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows-vertical.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows.svg new file mode 100644 index 000000000..a6bd3d758 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/arrows.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio-fill.svg index 81dcfcba5..6d6cb8dcb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio.svg index 66719a76e..ee634b01d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/aspect-ratio.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/asterisk.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/asterisk.svg index 8b0a9daef..fbc13b7b2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/asterisk.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/asterisk.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/at.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/at.svg index 4a85e1453..3cab29e84 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/at.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/at.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award-fill.svg index 6b589962b..f996790b3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award.svg index 8f572ff07..67c760b68 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/award.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/back.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/back.svg index 4c6cbcbac..9f5534000 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/back.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/back.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack-fill.svg new file mode 100644 index 000000000..ec737a946 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack.svg new file mode 100644 index 000000000..819aa29de --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack2-fill.svg new file mode 100644 index 000000000..3c9b4dac4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack2.svg new file mode 100644 index 000000000..e0e4fe598 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack2.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack3-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack3-fill.svg new file mode 100644 index 000000000..4583c4f25 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack3-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack3.svg new file mode 100644 index 000000000..819f8dfce --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack3.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack4-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack4-fill.svg new file mode 100644 index 000000000..a3bba3a26 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack4-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack4.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack4.svg new file mode 100644 index 000000000..140a12da2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backpack4.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-fill.svg index ab63109ca..078757864 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse-fill.svg index ed509ec2f..41c8dbbd0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse.svg index 446e019db..7b3fafd08 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace-reverse.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace.svg index 55c802cf1..39b688f1d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/backspace.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d-fill.svg index ac61cb5e6..750598c35 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d.svg index 34858372a..b3153f2a7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-3d.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k-fill.svg index f353033d2..72f34b9b7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k.svg index 24ddcb195..3dfc9b1d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-4k.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k-fill.svg index 1e1d4c162..4bd9b8012 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k.svg index 7df4c753e..d11f82d71 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-8k.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad-fill.svg index b383de673..023f2102a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad.svg index 942b018b8..616ad74d4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ad.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar-fill.svg index f98caaca4..48aee0fb2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar.svg index 221050609..0fc197541 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-ar.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc-fill.svg index d9783fe5a..0f3d1f944 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc.svg index 7868cb404..d5f42e185 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-cc.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd-fill.svg index 9f0a4982d..96f087580 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd.svg index e3f4ae753..5689042f2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-hd.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd-fill.svg index 538b64275..a37154a2d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd.svg index 68667dd7e..df8d02954 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-sd.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm-fill.svg index 7d334ce58..632b56971 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm.svg index 452dd3bd8..33de5c24a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-tm.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo-fill.svg index 1f74e75b5..1d27b0740 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo.svg index 500d98f97..f5e2ecea3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vo.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr-fill.svg index 6cde11ff0..e614af6fd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr.svg index 5c0685248..c1c73dc28 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-vr.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc-fill.svg index 47db37b1a..d16436aaa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc.svg index 3f0cc5b13..ea459bab4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/badge-wc.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check-fill.svg index a1ba2d27d..9976d5c5c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check.svg index c6ad9acb1..a4e327857 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash-fill.svg index a73924286..ccb5589c9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash.svg index 9bcb202f4..0997f332d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-dash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-fill.svg index 1a1e2e2e7..812fcfcc8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart-fill.svg index a859e05c4..4938b838a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart.svg index 2a6bd30c2..e7b906a18 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus-fill.svg index a110b32ff..b98f6b219 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus.svg index b99a1a571..0d7ddc61a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x-fill.svg index 879bffe60..cbe77df72 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x.svg index 616532ca4..4f380082d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag.svg index 603de5f44..acd028733 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bag.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-fill.svg index b66389461..2d57e2d88 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart-fill.svg index cebfb9381..ab17865c4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart.svg index dadf467ed..0e056cb0e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon.svg index 6ca06c3e5..6906cb397 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/balloon.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ban-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ban-fill.svg new file mode 100644 index 000000000..0bb7df0e3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ban-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ban.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ban.svg new file mode 100644 index 000000000..5acfd4193 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ban.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid-fill.svg index 41d350a84..052ad7375 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid.svg index de16de979..e09850e0a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bandaid.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank.svg index 264eaaa52..2e7f4f07a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank2.svg index b03840cae..acc8ef9a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bank2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-fill.svg index 23ba4f6e4..7e4ebee2f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line-fill.svg index a5059c460..6808e6f6a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line.svg index e3f0cf255..567a80866 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-line.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-steps.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-steps.svg index 933fba881..346e97be6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-steps.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart-steps.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart.svg index c34c0d450..8e57c801d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bar-chart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket-fill.svg index ebf223c72..b2e01f5ab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket.svg index 4bc584b2d..418a5f948 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2-fill.svg index 9ebf8dbbb..03c707955 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2.svg index 94f0bcb87..9b78be2d6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket3.svg index ac46c0117..57fa6a024 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/basket3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-charging.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-charging.svg index cbd91075c..4ae74d2d3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-charging.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-charging.svg @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-full.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-full.svg index 48cf92eae..bff6a3f1b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-full.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-full.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-half.svg index 8c3afca28..de57848bb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery-half.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery.svg index 126036069..2bacfa836 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/battery.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/behance.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/behance.svg index a6a2c42b1..805f142f0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/behance.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/behance.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-fill.svg index 76d9b6015..a537c3a0a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash-fill.svg index 2e6f8cf89..534dd13ee 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash.svg index eddbb8adf..7817e2b47 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell-slash.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell.svg index 585d417ae..a71eba308 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bell.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier.svg index 21ec7b3ef..075b721d9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier2.svg index 48722d0c4..8a59238a4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bezier2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bicycle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bicycle.svg index 17a2105b7..395654512 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bicycle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bicycle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bing.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bing.svg new file mode 100644 index 000000000..9368917d6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bing.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars-fill.svg index de09c7353..d6d6dc0a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars.svg index 47bca444f..015d62234 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/binoculars.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-left.svg index f2e0fa270..f8b6b2d8b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-right.svg index 253518db0..afc81c95c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/blockquote-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bluetooth.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bluetooth.svg index 5021e77f1..8726e225d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bluetooth.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bluetooth.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/body-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/body-text.svg index 81ede1331..fd5e4358a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/body-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/body-text.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-fill.svg index 276a281f3..ddb00006c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-half.svg index 76589a577..8eabe817c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book.svg index f0e5e49eb..302acf09e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/book.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check-fill.svg index 039e4555a..325fbde1e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check.svg index b1f572f58..f4c91496a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash-fill.svg index e17119240..dbf9cc1b2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash.svg index 1138dbb81..115b44878 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-dash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-fill.svg index 94661024c..3c237a9ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart-fill.svg index 83db817bc..6647b7c4c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart.svg index be0adb19e..c368f5dd6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-heart.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus-fill.svg index bb4502ab2..41e073381 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus.svg index 986a2223f..37b137c28 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star-fill.svg index 220f16ff4..89fd33593 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star.svg index 0d2f26267..2f792deb7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-star.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x-fill.svg index 69fd98233..acac0cf2c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x.svg index 6ac9e8016..eb85c76ab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark.svg index 93e1d995b..a21b14b0c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmark.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks-fill.svg index eb5a2db27..abf580008 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks.svg index 6efa0bc01..ceb92bb11 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookmarks.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookshelf.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookshelf.svg index 6549ea1b1..7f435d5d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookshelf.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bookshelf.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox-fill.svg index 6103ae227..299e95ed0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox.svg index 520f23ed6..35af8072a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boombox.svg @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-fill.svg index 9d163208c..21253b0a9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-reboot.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-reboot.svg index 4a184cf7c..8d2103052 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-reboot.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap-reboot.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap.svg index b6aed61d2..089e31f94 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bootstrap.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-all.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-all.svg index 803f5e228..19128f2ea 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-all.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-all.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-bottom.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-bottom.svg index dbc219291..84edccac8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-bottom.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-bottom.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-center.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-center.svg index 009b97da3..a9cf9c458 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-center.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-center.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-inner.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-inner.svg index 2beaa0cdd..63690070e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-inner.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-inner.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-left.svg index 69df882a0..483c804ff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-middle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-middle.svg index 90296f906..c9de407cf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-middle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-middle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-outer.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-outer.svg index 355e05ef3..4791bcb71 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-outer.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-outer.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-right.svg index b0c16da58..23e09dc65 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-style.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-style.svg index d742b2c95..cec3fef54 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-style.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-style.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-top.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-top.svg index 5aab36858..77189dfd7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-top.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-top.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-width.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-width.svg index 0cbd0e668..61753269c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-width.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border-width.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border.svg index 0e8c9b52d..a6390f9d4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/border.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box-circles.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box-circles.svg index 2e59f31a0..02113ba73 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box-circles.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box-circles.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box.svg index d5292928f..e8be147fa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bounding-box.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-left.svg index 6ad3e177d..20ffed9dc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-right.svg index 321cddf0b..33780ef24 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down.svg index 9a2ca1279..bf33d5140 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-left.svg index 76a687a0d..fe3c57985 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-right.svg index 923729341..07082eb9d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down.svg index 90f9301ca..3b185d643 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-left.svg index a237dafb3..1e1bc9a1f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-right.svg index d158daede..5d78def3c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-left.svg index 6937f6a69..8401c433a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-right.svg index 891861172..8a95e0020 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up.svg index e6a4a7bec..6197bc34e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-in-up.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-left.svg index 8602603ad..5d142b473 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-right.svg index 2c4e26c52..682e03357 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-left.svg index 7fb0b4581..7dec12d01 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-right.svg index 1d93acb91..03f68d558 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up.svg index beaf33467..8f768920d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-arrow-up.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-fill.svg index 8cf213f81..b1fe40778 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam-fill.svg index 97566ab58..b9283c7f0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam.svg index e1506b838..ec2cb8adc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box-seam.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box.svg index 58cbe2c28..01b34c763 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-fill.svg index 242ad6a84..78e75838a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart-fill.svg index 810e2ee6b..49da4861d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart.svg index 400eb431b..a5e413a75 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2.svg index 6020bafec..bfeb554c9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/box2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boxes.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boxes.svg index b53fac847..af0d1d0a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boxes.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/boxes.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces-asterisk.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces-asterisk.svg index 0a1a25bfa..e159e9c36 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces-asterisk.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces-asterisk.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces.svg index 3fed8c903..d345d3b56 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/braces.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bricks.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bricks.svg index 99e288642..23c2c3697 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bricks.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bricks.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase-fill.svg index bc6150dc4..b37f2be52 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase.svg index 95d13a23e..712998d36 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/briefcase.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high-fill.svg index 766065825..06f7d0c38 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high.svg index 88f5255c1..e519ca742 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-high.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low-fill.svg index 1692df21f..ab308376d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low.svg index 2d68fb0b7..58bf6ed87 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-alt-low.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high-fill.svg index 8969e9ba0..b759b046f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high.svg index 42b2c205b..f00d05096 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-high.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low-fill.svg index 29a1c3b1c..fc556807a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low.svg index fdd251ddc..317918e6f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brightness-low.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brilliance.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brilliance.svg new file mode 100644 index 000000000..f6b5da676 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brilliance.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast-pin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast-pin.svg index 5576e0ec2..9c5f4a6c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast-pin.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast-pin.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast.svg index 776a2378b..b420a0b5e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/broadcast.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-chrome.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-chrome.svg index a34ab4281..63c344ba7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-chrome.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-chrome.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-edge.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-edge.svg index c8191d880..ed1dc7cfe 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-edge.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-edge.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-firefox.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-firefox.svg index 2bfdba7e8..ce0eabb09 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-firefox.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-firefox.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-safari.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-safari.svg index b304b1b9b..8c0129698 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-safari.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/browser-safari.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush-fill.svg index 53ec4d62d..db776150d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush.svg index cc3429b9d..86d88ef39 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/brush.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket-fill.svg index e14f4a879..c0c95ab73 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket.svg index 4911ef254..252e75bfd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bucket.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug-fill.svg index bf1644711..a36ff3747 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug.svg index a97ffa177..296ef3247 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bug.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-add.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-add.svg new file mode 100644 index 000000000..c2c367004 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-add.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-check.svg new file mode 100644 index 000000000..95c3c54bd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-check.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-dash.svg new file mode 100644 index 000000000..1e1634bad --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-dash.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-down.svg new file mode 100644 index 000000000..8538cf6ee --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-down.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-exclamation.svg new file mode 100644 index 000000000..ebfc7091f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-add.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-add.svg new file mode 100644 index 000000000..6bbe56727 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-check.svg new file mode 100644 index 000000000..c4f188178 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-dash.svg new file mode 100644 index 000000000..1ce28a655 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-down.svg new file mode 100644 index 000000000..b1c55cd9c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-exclamation.svg new file mode 100644 index 000000000..3491f245c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-gear.svg new file mode 100644 index 000000000..747a1bc7d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-lock.svg new file mode 100644 index 000000000..be73a4130 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-slash.svg new file mode 100644 index 000000000..d8673092b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-up.svg new file mode 100644 index 000000000..d8cc4bcec --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-x.svg new file mode 100644 index 000000000..236aae34b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill.svg new file mode 100644 index 000000000..6924b41d6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-gear.svg new file mode 100644 index 000000000..eabe79099 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-lock.svg new file mode 100644 index 000000000..591a2e963 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-slash.svg new file mode 100644 index 000000000..c3f77877c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-slash.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-up.svg new file mode 100644 index 000000000..ff2d5d964 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-up.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-x.svg new file mode 100644 index 000000000..70e67a397 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building.svg index eb90b93a7..916b04947 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/building.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/buildings-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/buildings-fill.svg new file mode 100644 index 000000000..6aea68df4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/buildings-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/buildings.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/buildings.svg new file mode 100644 index 000000000..3028498bd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/buildings.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bullseye.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bullseye.svg index 85a807cee..16c220726 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bullseye.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bullseye.svg @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bus-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bus-front-fill.svg new file mode 100644 index 000000000..de21228dd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bus-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bus-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bus-front.svg new file mode 100644 index 000000000..95c5df5fe --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/bus-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle-fill.svg index 0b4adad1e..c0adc1863 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle.svg index 3e4e26801..ac3dfacd9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square-fill.svg index 0b24f7340..b26a27e7a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square.svg index 822ae023d..cdd74a3e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/c-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake-fill.svg new file mode 100644 index 000000000..4370e02f7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake.svg new file mode 100644 index 000000000..500747be6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake2-fill.svg new file mode 100644 index 000000000..1ed25f755 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake2.svg new file mode 100644 index 000000000..a10dc806d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cake2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator-fill.svg index c4ee270f1..293341963 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator.svg index be8e11a96..cc9761ebb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calculator.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check-fill.svg index 76afaa26a..967d18293 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check.svg index 125b358d2..f778cd2ff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date-fill.svg index 37e9cb52e..59b31f2c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date.svg index 7c53231a1..b73c8f509 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-date.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day-fill.svg index 7f1c3c151..b9bcbf87c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day.svg index f043369eb..6f8d871b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-day.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event-fill.svg index 844dd151b..5b09eeab6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event.svg index 41c0ef9b4..57c734bfc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-event.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-fill.svg index 0cdeb35ec..789eb8bfb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart-fill.svg index bed00d813..63d9e4c2c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart.svg index 2fe7c13f9..8ed9c381d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus-fill.svg index f23e6482e..8dad6e19b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus.svg index 8f970accf..ecd4e97c4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month-fill.svg index 9123437c4..d8d56fe6e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month.svg index ad6a330cc..95b64197f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-month.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus-fill.svg index 3928c6389..0ed0c8364 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus.svg index 70746dbe5..189b15271 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range-fill.svg index 41bb2a2f0..324def020 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range.svg index 934a45c80..7db09471e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-range.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week-fill.svg index 00930aa40..ab2128d2c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week.svg index 06d6995f6..b5dbcc9af 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-week.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x-fill.svg index 01be3013c..450e114a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x.svg index faf46e11f..dc85a91ad 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar.svg index c8590dd08..d32ebe7ea 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check-fill.svg index f49354e82..a0c36c5d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check.svg index f82605669..54298422a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-check.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date-fill.svg index ac005e0f6..93b3941ab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date.svg index 2dd64e8e1..61193cdf2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-date.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day-fill.svg index 2ab1b216c..b60545387 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day.svg index d4d485660..ce59878a3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-day.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event-fill.svg index 5bdbc27ed..2b245d1ba 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event.svg index 8c1c7cd04..36910dd7a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-event.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-fill.svg index 4b81563f3..b28e594cd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart-fill.svg index a1782e3d7..f3723cd99 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart.svg index 6d6698105..995d9b8cd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus-fill.svg index af8721331..bf52a36a7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus.svg index eff8110de..62e6bbc34 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-minus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month-fill.svg index 58deabc10..24b9c69ef 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month.svg index 88c922e26..65d8295f0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-month.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus-fill.svg index 8b41682f1..26a20477d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus.svg index 7ec7d4956..728148fbd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range-fill.svg index 39ba322f6..1ba46b6a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range.svg index 4a8d9adc7..9a657f409 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-range.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week-fill.svg index 830377986..f3586679f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week.svg index 835ce0671..07906a9e9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-week.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x-fill.svg index 2157939a5..def799dd2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x.svg index e7cc3390d..d6f9e6f85 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2-x.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2.svg index db2e06dda..957f99363 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event-fill.svg index c4940904a..d228ccbdc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event.svg index 681ce4d23..f702c3629 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-event.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-fill.svg index e37c234ec..f3bc116cb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range-fill.svg index 00875b2a5..e21d0eefd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range.svg index a45251605..c19d9ca94 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-range.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week-fill.svg index 53e5bc702..d828d85bb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week.svg index e9a768cb7..6d577b7ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3-week.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3.svg index eb3c5f2d9..8e2aedfc7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar3.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-event.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-event.svg index 51d40e873..0d29c0c6c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-event.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-event.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-range.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-range.svg index 129d7c195..b260479fe 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-range.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-range.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-week.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-week.svg index 5644238ca..d934881f0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-week.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4-week.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4.svg index 1c6268515..69c474c03 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/calendar4.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-fill.svg index be16451ad..6aa02409c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels-fill.svg index 347f44e06..6bd48dec9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels-fill.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels.svg index 1aa7b1ca3..0c6eca8c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-reels.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-fill.svg index 0222b9587..72dee3759 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off-fill.svg index 99090600f..290dc19ce 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off.svg index 7635e9b5c..c9eb587da 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video-off.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video.svg index 199e7a837..a042d1aa2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera-video.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera.svg index fb337fe50..3a926d537 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera2.svg index a33ae6b6a..ba9521e11 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/camera2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock-fill.svg index e4af909ee..f1c0bab0d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock.svg index 12155de35..b0b894a83 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capslock.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule-pill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule-pill.svg index c57efff62..b9f3b5483 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule-pill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule-pill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule.svg index 844aa8ee2..53d1a66ea 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/capsule.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front-fill.svg index e9c5204ec..a47a8705a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front.svg index a2ec568e1..890e4cf4c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/car-front.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-checklist.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-checklist.svg index ce2a553e1..3044e686a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-checklist.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-checklist.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-heading.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-heading.svg index 682bd4e41..a6be8739b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-heading.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-heading.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-image.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-image.svg index 473ff030b..7343f7785 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-image.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-image.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-list.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-list.svg index 3dc5d429f..62708023a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-list.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-list.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-text.svg index d218f554b..8f7470c25 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/card-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square-fill.svg index ae8fbb468..63199bb4d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square.svg index cf34038a7..037262514 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down.svg index 026b0ff35..627258a46 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square-fill.svg index 5d8ab5672..cc7e3a81c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square.svg index 099b54ddd..5e8cb2067 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left.svg index 89732f090..4415336d4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square-fill.svg index ea06657fd..2aded36f1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square.svg index 4039064d7..a3a44e2c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right.svg index 451686e4a..7bcd8bba6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square-fill.svg index 25c66a58b..348fcf290 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square.svg index d59ecbf9e..8ac2af450 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up.svg index 36ca8f0ef..8e3351903 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/caret-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check-fill.svg index 019c1fc92..612358c53 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check.svg index 986706a40..68301dbcd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash-fill.svg index 2562744b9..a335b0779 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash.svg index ecd23f336..9c97c3b66 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-dash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-fill.svg index a2b95bf09..974fc295f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus-fill.svg index 9858fe1e3..59e46e489 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus.svg index acafe1324..2baaae461 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x-fill.svg index a9a32caa0..7ca0688c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x.svg index feddfdd50..2d8f213b6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart.svg index 486adb1c4..0e0f96cea 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart2.svg index 7e1bd9abf..ea7f69647 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart3.svg index 2187149f5..af1b3c539 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart4.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart4.svg index b40891fbc..4631ac9f1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart4.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cart4.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-coin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-coin.svg index bc82c64af..2904f15e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-coin.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-coin.svg @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-stack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-stack.svg index 492cb381f..fc8c282f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-stack.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash-stack.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash.svg index ef3a4e7dc..18cbff3a4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette-fill.svg index e8dd8f1aa..18fd5e42e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette.svg index c28170c07..2effe7165 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cassette.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cast.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cast.svg index 1eda17371..01a979506 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cast.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cast.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle-fill.svg index ca9779e19..483d90cfc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle.svg index 6de6b7603..2dc2b4b36 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square-fill.svg index f9b44d549..b0a835029 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square.svg index 90c52bce0..b0f05b619 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cc-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots-fill.svg index 2e3d22505..5f74345ed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots.svg index a74267d28..20137cc18 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-dots.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-fill.svg index 69ed44bea..c89693900 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart-fill.svg index 9be92ca2e..ea01c34d5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart.svg index 90c276b8c..8ea104ea8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots-fill.svg index eb7f531bd..a6614c3f7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots.svg index c73169d7d..35f466a10 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-dots.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-fill.svg index 38c389f51..0de6e13ab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart-fill.svg index 787ed61cf..cc9502ca0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart.svg index 1604e7bca..ec11692e7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote-fill.svg index b115a9fe6..d634a126a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote.svg index 448827f4f..376ceb19b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-quote.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text-fill.svg index 28a0f4710..a78e3437c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text.svg index 2b69a9891..88d190672 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left.svg index fd2f4ee4c..d93f0af77 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote-fill.svg index 4a3af86eb..7150c44d2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote.svg index f890c38d2..aa0edc4e1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-quote.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots-fill.svg index 49ce0973c..1020581b7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots.svg index 423d221dc..d9b8bd238 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-dots.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-fill.svg index 41b767bbf..6381ddcfa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart-fill.svg index b55dc621e..cf4abfe32 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart.svg index 744e8a0d0..e6b388042 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote-fill.svg index e63f92b52..1c4e5360b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote.svg index 42c8dbe04..e9091bcf1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-quote.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text-fill.svg index 32df9212a..3455983b8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text.svg index d8b600464..88341afa3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right.svg index b702b5d18..a930c9a58 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots-fill.svg index 1025978df..09c97d1d6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots.svg index e59cd1a1f..b06c02ff9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-dots.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-fill.svg index 2fb73ac48..4688831a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart-fill.svg index f200049c1..902e0b5aa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart.svg index 89ca6efd7..6ba687e45 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote-fill.svg index 761cb91c9..2496b7008 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote.svg index 40893f495..a8f6b0990 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-quote.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text-fill.svg index 1dd17c513..6ebf567d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text.svg index ae3fd8da2..1296f9225 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square.svg index 7611729e7..4e13ae827 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text-fill.svg index fff3db332..93639f128 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text.svg index 75a79f1a3..f64f43fe5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat.svg index 3cb81b293..487d142ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chat.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-all.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-all.svg index b0019d00f..f91fd7701 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-all.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-all.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle-fill.svg index e861174a8..0b7f41265 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle.svg index d8dd0cd57..016f6072a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-lg.svg index 7afb0ae18..63a8a3df9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square-fill.svg index 45d682850..cbb56edc1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square.svg index d71c1f348..b7d11160c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check.svg index 9de6cc752..11ab54741 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-all.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-all.svg index 25d8ba52f..cccc0b7b0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-all.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-all.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-circle.svg index 7319d37b3..166e6612d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-square.svg index 2d5e6eb87..64c3669d7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2.svg index e187956c5..87168de30 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/check2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-contract.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-contract.svg index f12917f79..52ec3f629 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-contract.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-contract.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-down.svg index 4df2259f1..8c10216a7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-expand.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-expand.svg index 6cb775f94..1260a2056 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-expand.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-expand.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-left.svg index 5d53406ec..36afeb766 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-right.svg index b71553ca9..b71e040de 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-up.svg index 9ca140895..c5da17526 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-bar-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-down.svg index fb1767e01..53d9d9a3d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-left.svg index 5dd6b6bfc..277ddd897 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-right.svg index ecb5994e4..24b530907 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-up.svg index 8bc0a55e6..2a4f35410 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-compact-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-contract.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-contract.svg index 5243d4354..354ee863b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-contract.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-contract.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-down.svg index 0df76eed4..bc99e59bb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-left.svg index 7181fd111..c4cd7f238 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-right.svg index 73e1b352d..dccd6c582 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-up.svg index 5c9a01326..ad7ba127a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-double-up.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-down.svg index 1f0b8bc70..a2819073a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-expand.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-expand.svg index 0a2b81a3b..33e4ad810 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-expand.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-expand.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-left.svg index 018f8b673..5bcc1bb5e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-right.svg index d621289b3..ab39af834 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-up.svg index 3b2bd42e6..4f3c7a015 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/chevron-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-half.svg index 53809298b..497f6b72e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-square.svg index 37d86229f..c0f62b748 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle.svg index dc57919b3..9bbabcae3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check-fill.svg index 4c0c18fcf..598e85072 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check.svg index f7591aecf..cb9d8a200 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-check.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data-fill.svg index e7de45a26..e4a9425a5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data.svg index b4fcb336b..622acbfbf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-data.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-fill.svg index 86d3da067..176c5e42b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart-fill.svg index c653de1c9..92de0a8fa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart.svg index 0b5b31921..c430ed2cc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-heart.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus-fill.svg index 7828cb8b5..e47f43fad 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus.svg index 4826c3e65..d3675c40a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-minus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus-fill.svg index 2ebdba42c..8140aa84c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus.svg index 79020c074..1d095d73f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-plus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-pulse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-pulse.svg index 0c43dab87..a357209bc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-pulse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-pulse.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x-fill.svg index 8cba1ea76..10fba82c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x.svg index bba444da7..46df235ad 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard-x.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard.svg index 360e0894c..b92f42a5b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check-fill.svg index 01aed624b..b64043280 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check.svg index c2352080c..aba15bfa5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-check.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data-fill.svg index 40656a7b9..56c127ed8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data.svg index 74b26f516..75ac6c688 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-data.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-fill.svg index ca2df5711..6898571f8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart-fill.svg index 2abc35947..ce98945bc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart.svg index 4883c3f2a..879fef215 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-heart.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus-fill.svg index 000a2c631..fcd4b561c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus.svg index a634bb0f0..f8c10e3c1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-minus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus-fill.svg index f1702d8bc..be310e526 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus.svg index 474ffdcc2..33eaa289c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-plus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse-fill.svg index 5017f6dcc..bc7d6b319 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse.svg index 1e6370c08..c641c6158 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-pulse.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x-fill.svg index 8f63584d8..08828c6ee 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x.svg index 9ac82117a..06832cc96 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2-x.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2.svg index d729ddb6e..d0f452975 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clipboard2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-fill.svg index 189dec101..148abcf1c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-history.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-history.svg index 414b526c6..f685e10a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-history.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock-history.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock.svg index 72f293960..31c3c64cd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clock.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down-fill.svg index 6e18ca9ed..1b23dc967 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down.svg index cb8e33aee..bb79bbe72 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up-fill.svg index 89d72fbe1..8366f05b6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up.svg index 6f69abc7c..704756b0e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-arrow-up.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check-fill.svg index 81f28b5f4..a71feee5d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check.svg index 917d5c2e3..d7599e990 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download-fill.svg index 53c4242f8..c8a44badd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download.svg index c6b3fe31e..b71d7d722 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-download.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle-fill.svg index 996aec436..0d381ae7f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle.svg index bb1e68bfc..f3c859931 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-drizzle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fill.svg index 23755bd82..8849faa38 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog-fill.svg index 07f10f4c4..214cabad1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog.svg index b40c9838c..26a574aff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2-fill.svg index 1d498514e..8f67deadc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2.svg index 1bd3c2501..9b0664ff0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-fog2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail-fill.svg index d8a096bd2..0fa737f38 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail.svg index 2f9bec11c..3206a027c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-hail.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze-fill.svg index 002fc26b9..aa16c2cc5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze.svg index 513e346cd..578a56524 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2-fill.svg index e9b7bda75..3e22656a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2.svg index c213dfb7f..c43d91ce9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-haze2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-fill.svg index 1d309046b..88fd930b3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain-fill.svg index 2b6d4a1cc..f5cd8458e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain.svg index 31badb3b0..588b2745d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning-rain.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning.svg index 5a8bafd8d..20c268093 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-lightning.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus-fill.svg index 753727fe8..2fcc2bb69 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus.svg index a4ab6f97c..54f47b204 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon-fill.svg index d968faf9f..232dd4e24 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon.svg index 1089204ad..cc91617c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-moon.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus-fill.svg index 92620aa46..5337dc428 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus.svg index 4ef51f2d2..9448796bc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-fill.svg index 94cddbaa5..3ffee56b1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy-fill.svg index 167c8af48..d92411b52 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy.svg index a5c41e5aa..ee9ef8587 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain-heavy.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain.svg index eb4003236..e22f16c52 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-rain.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash-fill.svg index a4b8bad4d..08a709b94 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash.svg index fe8917838..d7b680cb3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-slash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet-fill.svg index 73764dcfb..1df3f33bd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet.svg index d3c8f2eb8..edc48c6d5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sleet.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow-fill.svg index 0ffc577ac..32cda8dcf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow.svg index b1643fbc6..26150c4c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-snow.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun-fill.svg index 9ecf7de7e..da5ecac0a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun.svg index 76ebc49e1..caa95e935 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-sun.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload-fill.svg index 766015d4a..2d0df59f6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload.svg index 6184b727f..e5ca56e4a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud-upload.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud.svg index 7b0b9b388..de877ab3e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloud.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds-fill.svg index fe7fc071c..d70e81711 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds.svg index c9a5ba005..7e253e761 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/clouds.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy-fill.svg index 3e90f9e83..7bf27b782 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy.svg index 0783bcae6..87c20175b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cloudy.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-slash.svg index ef0ef0181..51a5c570f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-slash.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-square.svg index 415b56c6d..30fdef306 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code.svg index 079f5c67f..c0760e979 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/code.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/coin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/coin.svg index 045d428fa..fb94cc564 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/coin.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/coin.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-fill.svg index fee7f54f7..4e5fbce03 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play-fill.svg index 2601e48c9..b6820d077 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play.svg index 96b5c6eec..0c59f5d97 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection-play.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection.svg index 0870f5afd..8b5d5fdfe 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/collection.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns-gap.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns-gap.svg index b3cb17536..8b4bb4e7c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns-gap.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns-gap.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns.svg index d7854918c..17632df7e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/columns.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/command.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/command.svg index d1622544e..64fa00ba3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/command.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/command.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass-fill.svg index ad821c4a1..1396c1f08 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass.svg index 86494617e..9b402f335 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/compass.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone-striped.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone-striped.svg index 44e96065d..28a9529b3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone-striped.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone-striped.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone.svg index 2de05c5ba..b1a7d9722 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cone.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/controller.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/controller.svg index 15e777456..b7ceedb0d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/controller.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/controller.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cookie.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cookie.svg new file mode 100644 index 000000000..7a4b2fae2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cookie.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/copy.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/copy.svg new file mode 100644 index 000000000..b59068028 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/copy.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu-fill.svg index 50d0a0782..ce6e29424 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu.svg index a9fbaa3f4..88c0d56c8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cpu.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back-fill.svg index c80bb6c9b..032fb4a22 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back.svg index e99159cd0..b29419cbb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-back.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front-fill.svg index c082ef0ab..06684d5df 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front.svg index 95b071df9..0bbc290ae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-2-front.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-fill.svg index d0686a8aa..a4f899aed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card.svg index f716d39df..406233dd5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/credit-card.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crop.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crop.svg index b7e174904..3b4bb608b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crop.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crop.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crosshair.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crosshair.svg new file mode 100644 index 000000000..13bed746f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crosshair.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crosshair2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crosshair2.svg new file mode 100644 index 000000000..3c2858601 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/crosshair2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-fill.svg index c8119352d..7173787bf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot-fill.svg index f512ae01d..9d7c465e4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot.svg index 789f1ea58..a6f7e899c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-hot.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-straw.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-straw.svg index bda9d076f..9388da968 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-straw.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup-straw.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup.svg index 490fe09a6..2694ac8e1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cup.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-bitcoin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-bitcoin.svg index 488adca31..0477ff898 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-bitcoin.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-bitcoin.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-dollar.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-dollar.svg index 572e34c27..7ead9a785 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-dollar.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-dollar.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-euro.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-euro.svg index 1fcaa7c6d..90c83d5f8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-euro.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-euro.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-exchange.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-exchange.svg index 1e3eaf399..e332aa785 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-exchange.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-exchange.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-pound.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-pound.svg index 60dbd5852..465087600 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-pound.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-pound.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-rupee.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-rupee.svg index 843d0fa88..4fdf9a2ba 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-rupee.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/currency-rupee.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor-text.svg index 42a48fa8a..27c057b0d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor-text.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor.svg index 315106bc4..e23e3fda3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/cursor.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-dotted.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-dotted.svg index 7e29372df..1c011e21b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-dotted.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-dotted.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-fill.svg index db27419ee..ac4eae070 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle.svg index 17483d6f9..c4abdd215 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-lg.svg index 0f4c5e9a3..454aa7d0c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-dotted.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-dotted.svg index 15b8d4b09..90886c358 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-dotted.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-dotted.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-fill.svg index 85a95b2cd..dbe0db2e5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square.svg index b63e53698..9381872fd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash.svg index 4ac42887a..c3834b409 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dash.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-add.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-add.svg new file mode 100644 index 000000000..5f7634045 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-check.svg new file mode 100644 index 000000000..29c02b859 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-dash.svg new file mode 100644 index 000000000..184db0a08 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-down.svg new file mode 100644 index 000000000..e07745254 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-exclamation.svg new file mode 100644 index 000000000..dbde50f22 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-add.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-add.svg new file mode 100644 index 000000000..4273d5d53 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-check.svg new file mode 100644 index 000000000..7690eed83 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-dash.svg new file mode 100644 index 000000000..48c468dff --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-down.svg new file mode 100644 index 000000000..c3560d886 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-exclamation.svg new file mode 100644 index 000000000..00073d070 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-exclamation.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-gear.svg new file mode 100644 index 000000000..94fc2e6d1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-lock.svg new file mode 100644 index 000000000..a94820560 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-lock.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-slash.svg new file mode 100644 index 000000000..467e6bbc7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-up.svg new file mode 100644 index 000000000..07f2d39dc --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-x.svg new file mode 100644 index 000000000..73ff26979 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill-x.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill.svg new file mode 100644 index 000000000..860387433 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-fill.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-gear.svg new file mode 100644 index 000000000..451763ce3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-lock.svg new file mode 100644 index 000000000..e150cd207 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-slash.svg new file mode 100644 index 000000000..e0cc9f2e2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-up.svg new file mode 100644 index 000000000..63f7a1050 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-x.svg new file mode 100644 index 000000000..f97779bd1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database.svg new file mode 100644 index 000000000..231c50cd4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/database.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd-fill.svg index 5b5ae29e9..d5380c070 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd.svg index 960e609ba..5163a585d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-hdd.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd-fill.svg index 9ba580203..0d1f9c34d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd.svg index 0dd8ae5e9..8405f21f8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/device-ssd.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2-fill.svg index b46a21200..397ae1534 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2.svg index 2b330e550..a6e5439e3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3-fill.svg index 6cc31c04c..7e4742365 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3.svg index 464b051d8..ee3fd6f2d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diagram-3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-fill.svg index e6e3151ff..1f86d1f7f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-half.svg index 4e1379143..68254b644 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond.svg index 4cddafaf9..44e2855c5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/diamond.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1-fill.svg index 0b20aa019..a32e2cf9f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1.svg index 97c2432de..afc64b0e6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-1.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2-fill.svg index f55f92168..131013e1f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2.svg index 38013a8e2..ba1a79ce7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3-fill.svg index ae5a1ba4d..158065db3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3.svg index 705b7e7f3..b64c675c1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-3.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4-fill.svg index 6dad92bb2..7cf6e2c7a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4.svg index 070f9812a..bd8bbf7c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-4.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5-fill.svg index a92382b30..289cb4592 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5.svg index b4369c7e9..cc96a3595 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-5.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6-fill.svg index fce8cb4b1..9b3d652ea 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6.svg index 44d25dc17..47ba0aac6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dice-6.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc-fill.svg index b03f34d0e..0d2d7f1e4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc.svg index f3475a2e3..360034cf8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/disc.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/discord.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/discord.svg index 877cfdf91..9905364b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/discord.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/discord.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display-fill.svg index f7c3fca74..ed8e17efc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display.svg index 700d7805b..40a7d4d52 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/display.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport-fill.svg index 17fe77191..503a96091 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport.svg index 3e5748a91..6b9e0bfd3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/displayport.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-horizontal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-horizontal.svg index fe90ff8c5..3f7044f0c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-horizontal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-horizontal.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-vertical.svg index 234b2c275..cb77d1ef6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-vertical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/distribute-vertical.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed-fill.svg index 1d2a0366d..1cad66bb0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed.svg index 3eab448fe..e20b918fd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-closed.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open-fill.svg index d4833a36c..38eaff070 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open.svg index d9638a319..328f35366 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/door-open.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dot.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dot.svg index 183e4a895..edc674e52 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dot.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dot.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/download.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/download.svg index 80a5817fb..90a34a3b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/download.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/download.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad-fill.svg index ea54468de..1c1153a8f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad.svg index 9363c904a..71ddb24bd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dpad.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dribbble.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dribbble.svg index 809f2d3c0..725ff7fe1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dribbble.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dribbble.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dropbox.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dropbox.svg index 64311413c..d052f25a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dropbox.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/dropbox.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-fill.svg index a240876bc..85feddf95 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-half.svg index 43eb20850..bcd1c7637 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet-half.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet.svg index 2b405d682..204ec672e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/droplet.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/duffle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/duffle-fill.svg new file mode 100644 index 000000000..885ee80d0 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/duffle-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/duffle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/duffle.svg new file mode 100644 index 000000000..1180de846 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/duffle.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear-fill.svg index 8e564c08b..2d135d6de 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear.svg index 8c8b869e2..061fe2f6b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ear.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/earbuds.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/earbuds.svg index 7bc0019ea..923bfca69 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/earbuds.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/earbuds.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel-fill.svg index db0079836..808650745 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel.svg index f95976eae..5c0f5a2ad 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2-fill.svg index c39324221..309b43896 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2.svg index d1736ded6..74372fb46 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3-fill.svg index 2e5722319..fc547ea28 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3.svg index a39ad3d72..f06a8681b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/easel3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fill.svg index 33b7d4434..b70cf16e6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fried.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fried.svg index b99cac3bf..b0cefbc5b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fried.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg-fried.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg.svg index 9fb5c1ed0..f23d8d45d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/egg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject-fill.svg index 3255af67d..9604988f5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject.svg index 540cbc813..71a3ab66b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eject.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry-fill.svg index 1bf7eb600..d14d92d97 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry.svg index d6d891432..ee925fe3d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-angry.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-astonished-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-astonished-fill.svg new file mode 100644 index 000000000..22a566f72 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-astonished-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-astonished.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-astonished.svg new file mode 100644 index 000000000..13f7cdd58 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-astonished.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy-fill.svg index d8018007b..98ab490f7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy.svg index f64fade85..fcef6027a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-dizzy.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless-fill.svg index f70140ac7..17ac34874 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless.svg index 208a72d8e..8d7f68fa2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-expressionless.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown-fill.svg index c8a9ddc4a..7a16dfb9e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown.svg index b7766ebf6..696031b2a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-frown.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grimace-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grimace-fill.svg new file mode 100644 index 000000000..6cabf8094 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grimace-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grimace.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grimace.svg new file mode 100644 index 000000000..75a2a09a5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grimace.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grin-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grin-fill.svg new file mode 100644 index 000000000..08c675e69 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grin-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grin.svg new file mode 100644 index 000000000..32bbf519d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-grin.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes-fill.svg index cc91552ae..d34f6e876 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes.svg index c19ec51ac..58328228f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-heart-eyes.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss-fill.svg index ab4624564..15a9cdd3f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss.svg index 4646628b8..2348d9712 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-kiss.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing-fill.svg index cc8c69b8e..0130bf438 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing.svg index 68d9b25f5..76b87f509 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-laughing.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral-fill.svg index 58bcb6b9f..662603a27 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral.svg index 2f3204a7b..d6b60f855 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-neutral.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-fill.svg index 76a6a1632..439dff0ef 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down-fill.svg index c6829332c..2d6acca97 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down.svg index 1e1842434..d2d93edb2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile-upside-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile.svg index d222a9ae8..bba78dabd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-smile.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses-fill.svg index 00e7bc0d8..a1318c228 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses.svg index 4771e4dbf..188b56cc7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-sunglasses.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-surprise-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-surprise-fill.svg new file mode 100644 index 000000000..9f6f620e6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-surprise-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-surprise.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-surprise.svg new file mode 100644 index 000000000..af246bf8a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-surprise.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-tear-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-tear-fill.svg new file mode 100644 index 000000000..3ccf87d4c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-tear-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-tear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-tear.svg new file mode 100644 index 000000000..31b6597c6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-tear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink-fill.svg index 8601a584c..2f3e480f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink.svg index ee3b3d0e2..7fe9116f3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/emoji-wink.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-down-fill.svg new file mode 100644 index 000000000..a5160e79a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-down-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-down.svg new file mode 100644 index 000000000..36b6f5406 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-up-fill.svg new file mode 100644 index 000000000..2757974e0 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-up-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-up.svg new file mode 100644 index 000000000..ff2fae0d5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-at-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-at-fill.svg new file mode 100644 index 000000000..e39ff3870 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-at-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-at.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-at.svg new file mode 100644 index 000000000..163c3bafe --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-at.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check-fill.svg index ca06ad0bf..26d753a77 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check.svg index 8a501810c..fbc765b86 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash-fill.svg index 7275d3df4..6e9e745bf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash.svg index 7ae3e5cf1..d6457cf9b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-dash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation-fill.svg index 4bc91d20e..e14f047a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation.svg index 936b77771..4aca0a9cd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-exclamation.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-fill.svg index 0b28c8661..966ef94a4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart-fill.svg index 8ed9e0288..41588417c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart.svg index b104999f1..c886df2fa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-fill.svg index 29d8fe77e..972d0e342 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart-fill.svg index 478b85bf4..13263fa94 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart.svg index 7d324a281..701811619 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open.svg index 9a542d2e2..3daa2ebc1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-open.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-fill.svg index 14f613e9f..c8a187c7e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart-fill.svg index e422accd7..7f58d571d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart.svg index 2d925aec9..73b91d9e4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper.svg index a909c6377..20fcc2ac1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-paper.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus-fill.svg index 96703c2b5..453a9fdc1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus.svg index 0abb96690..7e960a05e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash-fill.svg index 09690e54a..90eb7ef04 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash.svg index 35b378331..65cb167f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-slash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x-fill.svg index b8348b18f..8f6a79c4e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x.svg index cd78475f1..ea74027a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope.svg index 122fc357d..78bf1ded1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/envelope.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser-fill.svg index 10959b3d3..c3866e15c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser.svg index e7060e56f..fe62336f8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eraser.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/escape.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/escape.svg index 112c87b0b..66176b662 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/escape.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/escape.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ethernet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ethernet.svg index 9b97a3afe..739a2de74 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ethernet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ethernet.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-front-fill.svg new file mode 100644 index 000000000..53b947d46 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-front.svg new file mode 100644 index 000000000..7f053a576 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station-fill.svg index a30f613b1..a1ad0071d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station.svg index faec20c29..90470f637 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ev-station.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle-fill.svg index f7a7d17bf..13ce7ab6b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle.svg index 73c7e8d70..f3befe03d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond-fill.svg index 5987fe724..cb14aee9e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond.svg index 6c0388bf0..4881e6d24 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-diamond.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-lg.svg index b21e727fc..18f6a8736 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon-fill.svg index 3347f64c5..494010b4e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon.svg index 6ef1db9ff..7f2593813 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-octagon.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square-fill.svg index e99eab8e6..d80a4e92a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square.svg index 41436cb5a..2a0f2aed4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle-fill.svg index 50e17525e..52fd50886 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle.svg index 7ca0dc745..506b7774a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation-triangle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation.svg index 953004b39..d39cb9590 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclamation.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclude.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclude.svg index 9be5f93b5..ef18355eb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclude.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exclude.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit-fill.svg index 159d3654e..c0cb6f02e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit.svg index 22a0ef46e..a7ffae701 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/explicit.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exposure.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exposure.svg new file mode 100644 index 000000000..9de0e64f1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/exposure.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-fill.svg index 2697206ca..288d2eb03 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash-fill.svg index 933926287..10cca7435 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash.svg index c5208375a..359c270fb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye-slash.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye.svg index 412ff6928..393b485db 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eye.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyedropper.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyedropper.svg index 698d40ddb..457988852 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyedropper.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyedropper.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyeglasses.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyeglasses.svg index 020d94327..6b2eb970a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyeglasses.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/eyeglasses.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/facebook.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/facebook.svg index e8d1443db..5fc7cec17 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/facebook.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/facebook.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fan.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fan.svg index fab6eab8f..ec8fe2020 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fan.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fan.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn-fill.svg index 9c9a1c6b7..fd750fed2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn.svg index a3d605c7f..5e68554a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle-fill.svg index aa5c37bbb..3946fa392 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle.svg index 2eceb91a3..e7f71581a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-fill.svg index 329cad062..ae17a7eb8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward.svg index 10643600e..2142979fe 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fast-forward.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/feather.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/feather.svg new file mode 100644 index 000000000..f0462c2a6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/feather.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/feather2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/feather2.svg new file mode 100644 index 000000000..badc17ac2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/feather2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down-fill.svg index 910fc07e3..5b5c55227 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down.svg index 6f75d5040..f504b982a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up-fill.svg index 9dba20517..ef5619994 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up.svg index 223379e4d..9c8846751 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-arrow-up.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph-fill.svg index a0e31d6c9..686b60b8e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph.svg index e66be6a9f..f953927ec 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-bar-graph.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary-fill.svg index 13343bfb7..920c2a756 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary.svg index 7e667bcc2..0ecece7a9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-binary.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break-fill.svg index 4eddc4a85..bdbe1b967 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break.svg index b4485d7fc..e94b3a333 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-break.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check-fill.svg index fb54b18c1..cf165d319 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check.svg index 142631140..10863e1ad 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code-fill.svg index ee2f0f6a1..912ed0c79 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code.svg index a8c390b65..58b00142f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-code.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff-fill.svg index 945aef153..c108b8797 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff.svg index dd848f04c..088c6de71 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-diff.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down-fill.svg index 0e960474a..3941f1f31 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down.svg index 81cc43ab6..37c4cd5dd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up-fill.svg index ce881cc89..97a339d51 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up.svg index 6cf324ab2..1c827e916 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-arrow-up.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph-fill.svg index 7dc0df929..a4c69b5c2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph.svg index eefb68770..d367eca20 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-bar-graph.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary-fill.svg index 1652562c8..a1f99334a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary.svg index e068bf6dd..1528578a6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-binary.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break-fill.svg index b36ea25af..e9aadce47 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break.svg index e98c6470a..48747151b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-break.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check-fill.svg index de7bf69d8..f3e9eb386 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check.svg index f2fbf6657..dc36963b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code-fill.svg index c23a7b6b2..2c154fa56 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code.svg index 1b94a6aa4..ccd352887 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-code.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff-fill.svg index 5f18a8870..3b71e66ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff.svg index 0b28667e3..97dfc0b97 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-diff.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel-fill.svg index 0743de43c..e74c97426 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel.svg index 045fc870f..2feeabc28 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-easel.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel-fill.svg index 2d492a8e4..405a572a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel.svg index c40f16b24..5432bcff1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-excel.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-fill.svg index 668247e31..2f3ef6c5f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font-fill.svg index c4fe9a455..d3014bf86 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font.svg index f7aad0bb9..c9864cd0e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-font.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image-fill.svg index e568ee780..3ec76dc97 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image-fill.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image.svg index 1e1964b43..1c3815d8a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-image.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock-fill.svg index 18a7fb717..4c45a615f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock.svg index b15ec6a65..c8319a62d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2-fill.svg index 828a545a2..76e8bd8af 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2.svg index cf76d3fab..bd8f592d7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-lock2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical-fill.svg index ed5f6bc1e..42a0581c1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical.svg index 6fa8a39f3..e24c90c4a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-medical.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus-fill.svg index 25e708ff1..a5dca2dfc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus.svg index e87b163c2..cdc00262e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music-fill.svg index c64c79789..b865e4eb7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music.svg index cc25eb99b..e18ec8551 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-music.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf-fill.svg index 0ba21ab3f..219c5a31a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf.svg index 52da96fe4..51b9975d9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-pdf.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person-fill.svg index 29a81294a..49ceda6ab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person.svg index 59a6a2a39..08a78cc49 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-person.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play-fill.svg index 80731db8a..341eb37d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play.svg index 62042ab52..abe215b09 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-play.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus-fill.svg index 6cead3a48..ef99c8a4b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus.svg index 92840267f..964e8556b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post-fill.svg index 0c0e7b87c..548773f1c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post.svg index 1f0d4359f..d9f44a161 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-post.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt-fill.svg index 68e97930f..e96a4615f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt.svg index bedf55261..cab71bcaa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ppt.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext-fill.svg index e5c82ee75..55da25ce7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext.svg index 35af14671..3f3a6ad74 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-richtext.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled-fill.svg index 43aecce83..ee90c80ba 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled.svg index 4f288581f..ebd617ecd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-ruled.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides-fill.svg index 98b59df53..e7f76f407 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides-fill.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides.svg index 2d97fa7f9..e643cd34c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-slides.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet-fill.svg index 9a71e732d..02ac9ec15 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet.svg index a111232b0..a6bb6e4c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-spreadsheet.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text-fill.svg index b32991979..bfedd6b3a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text.svg index 0d60c7957..7ae53fc4a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word-fill.svg index 717b0497b..259e7673d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word.svg index 7186b69ec..ef4727cd1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-word.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x-fill.svg index a19d14b9c..f683966e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x.svg index bedb970d2..4dd9da98f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip-fill.svg index b92ff9a52..a17fa9ee9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip.svg index b82afcc35..f5f8ccc0e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark-zip.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark.svg index c3d086b9c..d8d8774d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-earmark.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel-fill.svg index e1122e5ec..fa9f91585 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel.svg index c6d6a4deb..6366bc085 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-easel.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel-fill.svg index 350a7df8c..bddcea2be 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel.svg index 0f43afe2a..8bf2f5e7b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-excel.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-fill.svg index a1f4de09a..e5f8c4ae4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font-fill.svg index 198a2591d..6bda7b1d6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font.svg index 1d67f5e78..b75f9a4ba 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-font.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image-fill.svg index f4e81abaa..7c73ece46 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image-fill.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image.svg index 127fd8963..b063628bf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-image.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock-fill.svg index a14dafc13..1f7c3f228 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock.svg index 4206978a4..195deefc4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2-fill.svg index a68a5d42f..82426e8f8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2.svg index 134f7473d..125a74771 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-lock2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical-fill.svg index 6caf0a323..2f4d3b2b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical.svg index afec18e97..07e84957b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-medical.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus-fill.svg index 85d99997a..99f7d53dd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus.svg index 67a45381d..880409b0d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music-fill.svg index c7dfa82f4..38099f33e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music.svg index 6531a959c..a24d41e4c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-music.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf-fill.svg index 87543f552..c88d1b855 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf.svg index e8ba0a157..e7bdcbab8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-pdf.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person-fill.svg index d7e05e401..c212b00f6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person.svg index 892800ad9..e102abf67 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-person.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play-fill.svg index 838dda9ef..a6dce8a1e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play.svg index fef9adf45..d5505e332 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-play.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus-fill.svg index 1730c276e..bdb7ee034 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus.svg index d0ef464ac..af41ae161 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post-fill.svg index c3fc7e05b..336f21942 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post.svg index dd8aefc20..6807724b1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-post.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt-fill.svg index 3d3ac35b1..b8c397345 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt.svg index 0100d0b24..bee27d41a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ppt.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext-fill.svg index 64c1fc832..a98e5d742 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext.svg index 22edf68c1..3ceb42037 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-richtext.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled-fill.svg index f93c25577..8bfe72621 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled.svg index 431b4eb55..7236cfcd0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-ruled.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides-fill.svg index e8cb12ad1..b75d3f75a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides-fill.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides.svg index df3f65d98..0e63548f6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-slides.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet-fill.svg index a3977e189..6db7eb6db 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet.svg index e83e73350..55b53ecde 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-spreadsheet.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text-fill.svg index 29c9fc471..6da36b2ce 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text.svg index fa1e86118..95dc704ff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word-fill.svg index 2df1fca25..6f578f0a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word.svg index 61a96c238..732b59a81 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-word.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x-fill.svg index 980e40528..04556592c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x.svg index 1fe66e61a..5ab0b486b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip-fill.svg index 95d3966fb..1d803662e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip.svg index 3da93c8ce..e3b633e8f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file-zip.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file.svg index 3562fb2b2..4a5dd731d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/file.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files-alt.svg index 1d4d06931..b42d764cf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files-alt.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files.svg index f8842f89b..6170ab5c8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/files.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-aac.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-aac.svg index 8a2d02ace..b6a5c47b3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-aac.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-aac.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ai.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ai.svg index 23e2ebc70..fe2bcaac6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ai.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ai.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-bmp.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-bmp.svg index acf902f7d..587381ca1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-bmp.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-bmp.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-cs.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-cs.svg index fb76aecbd..90ed8de42 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-cs.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-cs.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-css.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-css.svg index da12ac651..8f0864ff6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-css.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-css.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-csv.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-csv.svg index efda95c3b..fa097aa99 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-csv.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-csv.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-doc.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-doc.svg index 14fb54453..f75847fa3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-doc.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-doc.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-docx.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-docx.svg index 29a54ffec..1b6c17292 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-docx.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-docx.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-exe.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-exe.svg index 2c4bea4dc..cdafeb199 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-exe.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-exe.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-gif.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-gif.svg index 6b016d854..b39234f3c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-gif.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-gif.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-heic.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-heic.svg index dcdb6f1f0..a022060eb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-heic.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-heic.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-html.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-html.svg index 35d721856..1661a94d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-html.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-html.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-java.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-java.svg index c9dc543c8..eeeab41a5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-java.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-java.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jpg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jpg.svg index 5e4ae64c1..7d939ec18 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jpg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jpg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-js.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-js.svg index 8b198bfcd..4f4a00ccf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-js.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-js.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-json.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-json.svg index 2b9d988f8..a4cccedc9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-json.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-json.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jsx.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jsx.svg index c23ba4c33..256b5c534 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jsx.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-jsx.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-key.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-key.svg index 5b980500e..d164bc879 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-key.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-key.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-m4p.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-m4p.svg index a10dc24ab..ae8f611fd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-m4p.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-m4p.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-md.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-md.svg index ca5cd597d..40e5139b7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-md.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-md.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mdx.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mdx.svg index e8774d2e2..43a91538b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mdx.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mdx.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mov.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mov.svg index 9f05d6378..27e639132 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mov.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mov.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp3.svg index 017035126..f25e6a85b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp4.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp4.svg index 997c427a7..d27e9ffee 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp4.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-mp4.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-otf.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-otf.svg index 44d0c8eee..f16eb619c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-otf.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-otf.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pdf.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pdf.svg index e1fc9b698..e8bb77250 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pdf.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pdf.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-php.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-php.svg index 422cc2df9..4d532dfd1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-php.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-php.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-png.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-png.svg index f719344ae..659e26686 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-png.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-png.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ppt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ppt.svg index cfaaf1b17..e53b1ece6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ppt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ppt.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pptx.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pptx.svg index 88ef36906..f68e939fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pptx.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-pptx.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-psd.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-psd.svg index cfcb13b08..6fefd088f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-psd.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-psd.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-py.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-py.svg index 654df7050..14fd4ef71 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-py.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-py.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-raw.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-raw.svg index fdbeefcca..da98185a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-raw.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-raw.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-rb.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-rb.svg index e3387b4f9..3466a1f49 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-rb.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-rb.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sass.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sass.svg index 5ff5ae5bd..363266277 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sass.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sass.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-scss.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-scss.svg index 68f195afa..8d1935fb4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-scss.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-scss.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sh.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sh.svg index 200fae418..592c40816 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sh.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sh.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sql.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sql.svg index b523b3760..814137d8b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sql.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-sql.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-svg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-svg.svg index ea1264c76..222d9ac08 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-svg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-svg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tiff.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tiff.svg index d6f9e9be5..e101575f1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tiff.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tiff.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tsx.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tsx.svg index cef1dc4ff..73dd6435c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tsx.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-tsx.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ttf.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ttf.svg index 549d4df1d..9c93584c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ttf.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-ttf.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-txt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-txt.svg index 6fae02a04..1e27bcf2a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-txt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-txt.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-wav.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-wav.svg index bd226e8ef..6725640f0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-wav.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-wav.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-woff.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-woff.svg index d8ec58255..f29a4b162 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-woff.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-woff.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xls.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xls.svg index 9c266cdc2..5f79b1621 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xls.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xls.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xlsx.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xlsx.svg index a1aa80243..5202bf784 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xlsx.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xlsx.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xml.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xml.svg index d82264553..ba9ffb6a7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xml.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-xml.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-yml.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-yml.svg index e8bf63d22..17a9ebeb2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-yml.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filetype-yml.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/film.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/film.svg index 5cef9395b..40c2eb9da 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/film.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/film.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle-fill.svg index f60fd59f9..1aa0f399b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle.svg index bbdc85f86..42c1b8442 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-left.svg index 22441de5e..bb1ee92a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-right.svg index 466a9b1c9..6a5083c05 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square-fill.svg index f8813b8c9..438b8c227 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square.svg index ae8c837f9..d243b0bac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter.svg index 555c61259..a7d7daca0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/filter.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fingerprint.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fingerprint.svg index 3cf204231..08252bbdd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fingerprint.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fingerprint.svg @@ -1,7 +1,7 @@ - - - - + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fire.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fire.svg index f702837cc..a58e6dda0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fire.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fire.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag-fill.svg index 73fffc250..8b92331eb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag.svg index 357c48136..f8b6daba0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flag.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy-fill.svg new file mode 100644 index 000000000..87a43ce97 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy.svg new file mode 100644 index 000000000..65ae562d0 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy2-fill.svg new file mode 100644 index 000000000..61a6cdb4d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy2-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy2.svg new file mode 100644 index 000000000..241238914 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/floppy2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower1.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower1.svg index 08a7e2e68..3495858ae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower1.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower1.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower2.svg index d793728b3..664a9c226 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower3.svg index 147e32fcf..66845c35e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/flower3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-check.svg index d59955499..57c6466b3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-fill.svg index fd10c8e6c..113350c0f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-minus.svg index f41b6601b..41db30bb7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-plus.svg index 29b5115ac..85b5a18d3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink-fill.svg index 91dc0c015..64074007f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink.svg index b258b6aa0..7137637e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-symlink.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-x.svg index d571d0854..a6ed34194 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder.svg index fd4dc5aa7..a30c45227 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2-open.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2-open.svg index 59d8382f4..7ffbb5492 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2-open.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2-open.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2.svg index 414575325..ce6a1af27 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/folder2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fonts.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fonts.svg index 3afc7d2ea..5e1f3c31b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fonts.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fonts.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward-fill.svg index 7f2839b29..27462e23b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward.svg index 4b856141f..ffe887cd7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/forward.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/front.svg index d1edeb1f1..59d854e72 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/front.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/front.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel-fill.svg index 824913cd2..997d6f890 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel.svg index ad24a929b..e170eebb4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-diesel.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-fill.svg index 515452311..46f92c394 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump.svg index f4742f5b3..1704a6fa7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fuel-pump.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen-exit.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen-exit.svg index b9bdb1b16..cfde3a3cb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen-exit.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen-exit.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen.svg index 7789d36bd..d4f8a8382 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/fullscreen.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel-fill.svg index 5f16f16ae..04d31a626 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel.svg index d027aa5cb..28bfcf230 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/funnel.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-fill.svg index 2aa36a1d5..ba8e2c55c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide-connected.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide-connected.svg index fc196dd70..19ddda9ae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide-connected.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide-connected.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide.svg index 83194ce45..c5de5678e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear-wide.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear.svg index c11dbc1db..30cfaa386 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gear.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gem.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gem.svg index 360d55499..f56d87128 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gem.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gem.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-ambiguous.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-ambiguous.svg index 2ffaf112e..674c52660 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-ambiguous.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-ambiguous.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-female.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-female.svg index 102783cca..ae6dc2784 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-female.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-female.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-male.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-male.svg index b0aee1d18..393192199 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-male.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-male.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-neuter.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-neuter.svg new file mode 100644 index 000000000..133db1a0c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-neuter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-trans.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-trans.svg index 4c4c074a6..005e6f805 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-trans.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gender-trans.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt-fill.svg index e88b77b6b..77f0478a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt.svg index 40927941a..20e18ba1f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-alt.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-fill.svg index a53f2bdf7..9678fa081 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo.svg index 6686fea98..2382b9138 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/geo.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift-fill.svg index 69f337939..bd78a8cab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift.svg index 663b87e30..592150779 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gift.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/git.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/git.svg index 092d23e98..0979cdb0f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/git.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/git.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/github.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/github.svg index bb4e45cec..013e02532 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/github.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/github.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gitlab.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gitlab.svg new file mode 100644 index 000000000..f8875f8d4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gitlab.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-americas.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-americas.svg new file mode 100644 index 000000000..5dcc7b279 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-americas.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-asia-australia.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-asia-australia.svg new file mode 100644 index 000000000..8c27539bf --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-asia-australia.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-central-south-asia.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-central-south-asia.svg new file mode 100644 index 000000000..80a13c164 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-central-south-asia.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-europe-africa.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-europe-africa.svg new file mode 100644 index 000000000..c1c5dcbb9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe-europe-africa.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe.svg index 96cf81571..835ff663f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe2.svg index 150a01ebf..b30206376 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/globe2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google-play.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google-play.svg index a970e9a0e..0751c9004 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google-play.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google-play.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google.svg index 47abd4929..9f603de0b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/google.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gpu-card.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gpu-card.svg index b75ddcee7..6ec31522c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gpu-card.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/gpu-card.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down-arrow.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down-arrow.svg index bf522b566..d811884d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down-arrow.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down-arrow.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down.svg index 55adb4f24..47dd64772 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up-arrow.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up-arrow.svg index fd582e467..7eda5f41c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up-arrow.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up-arrow.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up.svg index a68bc9db1..15e0bbf90 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/graph-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2-fill.svg index 119511763..cc568cbb0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2.svg index dd36f54c8..69ec79f07 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-1x2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap-fill.svg index 4fe82884c..9c0e855d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap.svg index a9e8689d3..730ae6562 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2-gap.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2.svg index 6dd39fde8..55ff9bb6a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap-fill.svg index d29616c75..982cb702a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap.svg index 675f4288c..bbf8ce4e1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3-gap.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3.svg index c40d98c96..d56aed778 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-3x3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-fill.svg index 202265ff8..356cc8f4a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid.svg index bc505957f..eebab7304 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grid.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-horizontal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-horizontal.svg index c4439afec..85f7e27c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-horizontal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-horizontal.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-vertical.svg index 0182ad918..a8718ab1d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-vertical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/grip-vertical.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle-fill.svg index 6a7073605..910647289 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle.svg index 6579c1f85..53c34b66f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square-fill.svg index 51d11d119..06269e0f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square.svg index 2eac5d72b..2c5ad2c98 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/h-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hammer.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hammer.svg index d702c1156..8e07b5b09 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hammer.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hammer.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-fill.svg index ef94089fb..b2a7d6463 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb-fill.svg index 43e958c92..774b18eda 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb.svg index 699e5057b..13b5475d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index-thumb.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index.svg index 789622c82..725757bfd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-index.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down-fill.svg index c2f51ebab..53584f3bc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down.svg index e8dadb5ba..f87f523ae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up-fill.svg index e7216e196..c68bc07ff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up.svg index 0d410a161..dc46d4d96 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hand-thumbs-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag-fill.svg index 5d4367c79..5f8f23f40 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag.svg index 99e5904d0..c9ef87456 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/handbag.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hash.svg index 4621b1dac..f67d000db 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hash.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-fill.svg index 9bdc467e9..dbeda24f5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network-fill.svg index 403d47230..a74874d94 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network.svg index f0db30504..722354295 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-network.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack-fill.svg index bb450781f..7c33aec5a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack.svg index 480d0d90b..d19a5149a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-rack.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack-fill.svg index c81687ab9..27e87196f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack.svg index 2f74d3b06..f9095e5d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd-stack.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd.svg index 7dd670038..92358f734 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdd.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi-fill.svg index 9b52d61e9..435b39d24 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi.svg index b8a4b416f..09d0849f4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hdmi.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headphones.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headphones.svg index c2c1d6fe1..d50b3ee15 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headphones.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headphones.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset-vr.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset-vr.svg index 9f07b76ca..0498bd651 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset-vr.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset-vr.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset.svg index 536997442..a921156c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/headset.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-arrow.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-arrow.svg index 0407ed6e2..45eb6a542 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-arrow.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-arrow.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-fill.svg index 4026252e9..5e6b8ee54 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-half.svg index 1474a7251..2e0dd31b2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse-fill.svg index 278e9e284..fc5e21976 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse-fill.svg @@ -1,3 +1,4 @@ - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse.svg index bddedceba..dd957b32a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart-pulse.svg @@ -1,3 +1,4 @@ - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart.svg index d650006d8..cd8eeb1e3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak-fill.svg index 9c3103910..335cb6a5e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak.svg index b59b4c68d..dcffc897b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heartbreak.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hearts.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hearts.svg index c1c52e418..ebd2dbb24 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hearts.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hearts.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-fill.svg index ad8e05864..a339328ae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-half.svg index 5753b6287..b4fadcf9a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon.svg index e85a0bd38..3140b8b89 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/heptagon.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-fill.svg index afd7870eb..50fdbfb9c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-half.svg index a9fc13683..452cb6ee6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon.svg index f6601f2ba..6e839399e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hexagon.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/highlighter.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/highlighter.svg new file mode 100644 index 000000000..e26f1f7b1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/highlighter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/highlights.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/highlights.svg new file mode 100644 index 000000000..b43fca0f3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/highlights.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital-fill.svg index a93213326..55bde0f0a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital.svg index 5168a2992..f6bd0daab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hospital.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-bottom.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-bottom.svg index 8ce8394ad..946f8823d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-bottom.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-bottom.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-split.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-split.svg index b8bba9b66..a9d1c5106 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-split.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-split.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-top.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-top.svg index f471084de..6a8a4a3eb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-top.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass-top.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass.svg index cecfa7e60..44ede0a8d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hourglass.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-add-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-add-fill.svg new file mode 100644 index 000000000..1e814ee97 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-add-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-add.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-add.svg new file mode 100644 index 000000000..2a89bbe9f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-check-fill.svg new file mode 100644 index 000000000..f2ddeb92d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-check-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-check.svg new file mode 100644 index 000000000..5bd790005 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-dash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-dash-fill.svg new file mode 100644 index 000000000..2fa8c57c2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-dash-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-dash.svg new file mode 100644 index 000000000..b1cb83298 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door-fill.svg index ff9f4dbd2..cf6d43940 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door.svg index c883f3487..daa093bff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-door.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-down-fill.svg new file mode 100644 index 000000000..351904fe1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-down-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-down.svg new file mode 100644 index 000000000..4d1d905ae --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-exclamation-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-exclamation-fill.svg new file mode 100644 index 000000000..52fc0ae2a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-exclamation-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-exclamation.svg new file mode 100644 index 000000000..6d414f359 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-fill.svg index e782589df..89db16446 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-gear-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-gear-fill.svg new file mode 100644 index 000000000..9003152a3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-gear-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-gear.svg new file mode 100644 index 000000000..65b5abb7a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-gear.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart-fill.svg index 6d874fc1e..be2e5f401 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart.svg index 26b239529..ece6c0616 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-lock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-lock-fill.svg new file mode 100644 index 000000000..90cc8c715 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-lock-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-lock.svg new file mode 100644 index 000000000..8dc5894ea --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-slash-fill.svg new file mode 100644 index 000000000..df8d7c62b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-slash-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-slash.svg new file mode 100644 index 000000000..a81b0ede6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-slash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-up-fill.svg new file mode 100644 index 000000000..5e6a80157 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-up-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-up.svg new file mode 100644 index 000000000..da183d27d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-x-fill.svg new file mode 100644 index 000000000..729cdb5a8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-x-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-x.svg new file mode 100644 index 000000000..68137c0bc --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house.svg index 7baa23d29..cb57f687a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/house.svg @@ -1,4 +1,3 @@ - - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/houses-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/houses-fill.svg new file mode 100644 index 000000000..63047bbfd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/houses-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/houses.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/houses.svg new file mode 100644 index 000000000..3a906bedc --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/houses.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hr.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hr.svg index b6f2e3378..6e9fbd8c5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hr.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hr.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hurricane.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hurricane.svg index e21aaec5d..5bd9ae1d8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hurricane.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hurricane.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hypnotize.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hypnotize.svg index baa2298d0..6a2899710 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hypnotize.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/hypnotize.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-alt.svg index 98142b22d..e56ee2086 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-alt.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-fill.svg index 33c40a13a..db26ae8d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image.svg index facacee61..152c333ab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/image.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/images.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/images.svg index b35ecebcb..adc7abfae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/images.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/images.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox-fill.svg index bf5c8c912..7e4816df4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox.svg index 59ad2d777..b60e94960 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inbox.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes-fill.svg index 27447dc33..4d8c5f786 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes.svg index f23f0ec5e..1a074aca8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/inboxes.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/incognito.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/incognito.svg index fc9f6dced..f2c8f9d2f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/incognito.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/incognito.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/indent.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/indent.svg index 025acef29..0065aba80 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/indent.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/indent.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/infinity.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/infinity.svg index e9dd437b9..3dca19b22 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/infinity.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/infinity.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle-fill.svg index 9d38231fc..d2e382b59 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle.svg index 8f48f86cb..e2b50eb5f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-lg.svg index d1b988e0e..ac064b928 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square-fill.svg index c2e5a6636..49196b459 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square.svg index 71e2818f5..b64cfa8e9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info.svg index 9d061b4d8..43dc24248 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/info.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor-text.svg index f2121113b..fc910f3b5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor-text.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor.svg index 3a89bb7ee..de6a35fb3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/input-cursor.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/instagram.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/instagram.svg index 0b5c5cef0..855e65377 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/instagram.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/instagram.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/intersect.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/intersect.svg index 2d8c32951..220141d53 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/intersect.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/intersect.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-album.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-album.svg index 2504b3d43..de49ccc1f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-album.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-album.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-down.svg index 79c313d8d..d922f3ccc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-down.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-up.svg index 84234612a..7edc40006 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-arrow-up.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark-fill.svg index 03e247667..8e2f17a6f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark-fill.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark.svg index 665276438..4a8f4bbd8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-bookmark.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-check.svg index 41b97e94d..eb398b935 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-check.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-code.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-code.svg index 82098b9c2..41430d26d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-code.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-code.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-medical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-medical.svg index 5500110ab..fb6d94240 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-medical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-medical.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-minus.svg index c8cd4d844..cbdfdd7fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-minus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-plus.svg index fa6d7026c..5cb82c3fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-plus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-richtext.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-richtext.svg index 14b0e1f00..db92c70b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-richtext.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-richtext.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-text.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-text.svg index 9b66f43aa..13c58bc11 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-text.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-text.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-x.svg index 2ca24f46e..fb3ea9f56 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal-x.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal.svg index 941c98783..4c166e216 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journal.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journals.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journals.svg index 03f6dad8f..6e5386a9c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journals.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/journals.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/joystick.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/joystick.svg index a8a902743..909369e49 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/joystick.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/joystick.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-left.svg index 68859b8ec..17b45e4df 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-right.svg index 1efe4f389..4d96c43fb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify.svg index 009bd7214..3eedc742a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/justify.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban-fill.svg index d633a5383..a8ed5bb59 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban.svg index c5cdaaf55..cd13b3272 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/kanban.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key-fill.svg index 25a6d45a6..fdab8d6e6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key.svg index dbaae3fad..b0d1e16d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/key.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard-fill.svg index 876321d24..b46ad2d5e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard.svg index 996c1ebab..8ba49b623 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/keyboard.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ladder.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ladder.svg index fd9182ab0..7c6864f7d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ladder.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ladder.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp-fill.svg index ff91f4ba3..836b0db7d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp.svg index 6c50a70c2..b3fa1d68a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lamp.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop-fill.svg index 5b1755dac..31e588032 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop.svg index 0fc463deb..8e71020cd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/laptop.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-backward.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-backward.svg index 073034ac2..ed43c708a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-backward.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-backward.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-forward.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-forward.svg index ffc6e2aac..d0a4abfa1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-forward.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layer-forward.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-fill.svg index 8af0b1cfb..3b6cdf642 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-half.svg index a054e25a0..8ceaaad8c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers.svg index ac2f5b297..52dbe792f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layers.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset-reverse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset-reverse.svg index 5b6f32460..0d8dc7fde 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset-reverse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset-reverse.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset.svg index 8dc0243ec..cc19c86b7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-inset.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-reverse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-reverse.svg index 8ab950907..7c03f730e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-reverse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar-reverse.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar.svg index 1cfc86e1c..ff4085859 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-sidebar.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-split.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-split.svg index 71f33d2e7..4805b25b7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-split.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-split.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar-reverse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar-reverse.svg index 46252d574..917928531 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar-reverse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar-reverse.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar.svg index 5effada29..6d89f67dc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-sidebar.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window-reverse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window-reverse.svg index fb3484090..8258ad328 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window-reverse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window-reverse.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window.svg index 0aef11070..4d27cf908 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-text-window.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-three-columns.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-three-columns.svg index 6d358d6d5..7117f074f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-three-columns.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-three-columns.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-wtf.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-wtf.svg index b603f8f3f..fd8f5a1fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-wtf.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/layout-wtf.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/life-preserver.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/life-preserver.svg index 6466ea24b..7282baa2a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/life-preserver.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/life-preserver.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-fill.svg index 99039504b..0ef90ea6b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off-fill.svg index 7d9600e48..e533739e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off.svg index 5675e9c55..15e8200af 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb-off.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb.svg index c13f62783..67bf5bd8a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightbulb.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-charge.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-charge.svg index 8a97432e9..5352e723f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-charge.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-charge.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-fill.svg index 4d05a2b83..b98af6864 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning.svg index 873706082..d2faa0fbf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lightning.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/line.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/line.svg index bedc051be..3e4bfd3b8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/line.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/line.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link-45deg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link-45deg.svg index 127956a47..abdc8cb88 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link-45deg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link-45deg.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link.svg index df35bc8a1..823e4cd69 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/link.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/linkedin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/linkedin.svg index 4c4efe595..30fc0e34c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/linkedin.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/linkedin.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-check.svg index 34dd42068..e1db37747 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-check.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns-reverse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns-reverse.svg index 2cb507860..f5e2876f7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns-reverse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns-reverse.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns.svg index d04a30f33..07d0b1a15 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-columns.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-nested.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-nested.svg index 21c9a7d24..26607c912 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-nested.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-nested.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ol.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ol.svg index 5782568d7..d111f7301 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ol.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ol.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-stars.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-stars.svg index 88dce521b..c520bdf00 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-stars.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-stars.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-task.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-task.svg index 81181905c..3905d7a66 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-task.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-task.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ul.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ul.svg index 217d1539c..f1cc202ca 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ul.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list-ul.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list.svg index e039056ea..de5885824 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/list.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock-fill.svg index 9fb8f7b80..69646f6b6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock.svg index b50a68ef1..9c730b792 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lock.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/luggage-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/luggage-fill.svg new file mode 100644 index 000000000..eb7378fda --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/luggage-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/luggage.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/luggage.svg new file mode 100644 index 000000000..ad037babb --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/luggage.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs-fill.svg index 430b0282d..2880fa667 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs.svg index 53708525c..082e7de56 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/lungs.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magic.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magic.svg index 3df2ec0b5..0b2f1fdff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magic.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magic.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet-fill.svg index 9ca186521..026d0decd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet.svg index aab17635d..36b238bfe 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/magnet.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox-flag.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox-flag.svg new file mode 100644 index 000000000..8e24db05f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox-flag.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox.svg index e2ac2f978..1048e2a74 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox2-flag.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox2-flag.svg new file mode 100644 index 000000000..a25370070 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox2-flag.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox2.svg index 60a523bba..33e22a6c8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mailbox2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map-fill.svg index 6097c5f41..7134540d7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map.svg index f9dbb0846..2b579ce3b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/map.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown-fill.svg index b87e236c9..a932fbb0e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown.svg index f9933a603..33962c697 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/markdown.svg @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/marker-tip.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/marker-tip.svg new file mode 100644 index 000000000..e00f93151 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/marker-tip.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mask.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mask.svg index 3bfe141c5..b51158c12 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mask.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mask.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mastodon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mastodon.svg index 23b34f5c0..a8c2a26cc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mastodon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mastodon.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/medium.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/medium.svg index cc4687653..065ace11f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/medium.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/medium.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone-fill.svg index 237e81467..9f44f2e83 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone.svg index 834083c19..1cedb30ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/megaphone.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/memory.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/memory.svg index cdc2943f7..48764d2b5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/memory.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/memory.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app-fill.svg index c41c6fbf8..65cfdcf55 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app.svg index 36e57dfa1..ecda144ee 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-app.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-fill.svg index 034b64d73..09b280553 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide-fill.svg index d6e17da08..d97ce7f73 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide.svg index d67ba6a23..5636c10a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button-wide.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button.svg index 4e0fff930..ec4c70a4b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-button.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-down.svg index b2d84b24e..e53a5e9ce 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-up.svg index fb35e8db7..96ff58b4e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/menu-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/messenger.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/messenger.svg index 5c6d37d4b..e896a79c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/messenger.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/messenger.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/meta.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/meta.svg index 2c6885d7a..03155a1f3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/meta.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/meta.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-fill.svg index 9be58e9d2..c92ade73d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute-fill.svg index cc325a0ce..a10a1bcf1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute.svg index 5a520a12f..59b04beef 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic-mute.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic.svg index 57be2e560..f07bf14d4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mic.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft-teams.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft-teams.svg index e0cc253e5..6bf3a0ca8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft-teams.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft-teams.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft.svg index d28281f1c..8d2a03c31 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/microsoft.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart-loaded.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart-loaded.svg index 8a754571f..48e523f54 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart-loaded.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart-loaded.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart.svg index 7f3ad000c..c4869c25d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/minecart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem-fill.svg index 1fe97bef5..a5dd5e296 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem.svg index 873090dd8..f90ad6b95 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/modem.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moisture.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moisture.svg index 732f4ac2a..490fb4950 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moisture.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moisture.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-fill.svg index 1149676d2..67f67397f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars-fill.svg index d2e1d6ed5..c50e070b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars.svg index b25ef8632..ae138c2df 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon-stars.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon.svg index 4cd882028..46458ecfb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/moon.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard-fill.svg index 7f5fb4842..02f6c8c5e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard.svg index ed82b6aa8..94f9e97d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mortarboard.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard-fill.svg index bf15e965b..fabff9761 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard.svg index ed13d0c1a..d29e25578 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/motherboard.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse-fill.svg index bd0b5eb4d..24d275ea9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse.svg index 40976e061..e01881101 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2-fill.svg index 283d1cde3..6277b4463 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2.svg index 359da4d39..fd15e7c8d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3-fill.svg index de6dbc7a1..16c1705bc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3.svg index d042bfd3a..548b244ae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/mouse3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-beamed.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-beamed.svg index 04cedf09d..9eb1506ff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-beamed.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-beamed.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-list.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-list.svg index 5c306bd8f..d33767dd1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-list.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note-list.svg @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note.svg index 1125a6627..d6fe21e45 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-note.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player-fill.svg index 6619d1e7a..68a65b507 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player.svg index 2d50a6353..7eb9c92fb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/music-player.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/newspaper.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/newspaper.svg index 7d7fa7169..9a1cf6de0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/newspaper.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/newspaper.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nintendo-switch.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nintendo-switch.svg index 0f1e2ac64..584949397 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nintendo-switch.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nintendo-switch.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus-fill.svg index 32430b925..802d67874 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus.svg index b1accd4af..8ffaa389b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-minus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus-fill.svg index e5ee855c2..9559b26b8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus.svg index 085f04fe6..028ef2817 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/node-plus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/noise-reduction.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/noise-reduction.svg new file mode 100644 index 000000000..cd5e28859 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/noise-reduction.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut-fill.svg index 4babc0339..18dfeb172 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut.svg index 4912d489d..75a401d4d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nut.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvidia.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvidia.svg new file mode 100644 index 000000000..438a6fc29 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvidia.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvme-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvme-fill.svg new file mode 100644 index 000000000..962c3c63a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvme-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvme.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvme.svg new file mode 100644 index 000000000..31a2fb6bb --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/nvme.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-fill.svg index c1283178d..73c80f0ca 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-half.svg index d95240ac5..fe6eb4154 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon.svg index 9f3657ed9..d2d9c5c33 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/octagon.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/opencollective.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/opencollective.svg new file mode 100644 index 000000000..b9a0c9ff4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/opencollective.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio-fill.svg index 5bdfd8227..9b7406074 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio.svg index 7a38b83e7..253d1d70a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/optical-audio.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/option.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/option.svg index d7702b107..32cce4c61 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/option.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/option.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/outlet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/outlet.svg index b48af60de..7787f35d6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/outlet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/outlet.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle-fill.svg index ea54307fc..e57d158e8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle.svg index 888a1fad1..bfe68d940 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square-fill.svg index ad3caa203..164f5ebee 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square.svg index ad630d047..1f6335d38 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/p-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paint-bucket.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paint-bucket.svg index ee15d10f5..9ac2df453 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paint-bucket.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paint-bucket.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette-fill.svg index 7dc5ecda5..d7a6a3bc1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette.svg index fea76d92d..1cd490fd3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette2.svg index 5d140b31d..ae65e88e8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/palette2.svg @@ -1,4 +1,3 @@ - - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paperclip.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paperclip.svg index 00f311d60..c02950b53 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paperclip.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paperclip.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paragraph.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paragraph.svg index 999cb5327..38c65d442 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paragraph.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paragraph.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass-fill.svg index a5715df48..1e15dd9d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass.svg index 3f51eb5df..20a06bc64 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pass.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/passport-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/passport-fill.svg new file mode 100644 index 000000000..d42c1b988 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/passport-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/passport.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/passport.svg new file mode 100644 index 000000000..2ecee5aa5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/passport.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check-fill.svg index 13014151d..91283e229 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check.svg index 2dd799ee1..1fd0a2e92 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation-fill.svg index fd900c4e2..e745268c2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation.svg index 153d97dda..2372cc6a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-exclamation.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus-fill.svg index 12f35c23b..bfeb96eed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus.svg index f6024f979..35a380cf9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus-fill.svg index 1a79d799e..b47509800 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus.svg index b9a78461b..4f332daf7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question-fill.svg index 665588b3c..101c25524 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question.svg index ef4ca58e5..a777cef9f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/patch-question.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn-fill.svg index efca14289..81c0720d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn.svg index 0e9eb3a1d..e2d68f9ab 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle-fill.svg index 5e3525f4a..90c4ca56c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle.svg index 1b6b64afb..6d3aeff01 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-fill.svg index 68285b2fe..92e1588fa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause.svg index 22478eafc..7bfde2ceb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pause.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paypal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paypal.svg index 41bd53661..b2cec8842 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paypal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/paypal.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display-horizontal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display-horizontal.svg index 2013f1557..724ba8658 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display-horizontal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display-horizontal.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display.svg index f5d09dafb..c3cf9dde6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-display.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-horizontal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-horizontal.svg index 9ae513a13..a8ae72fcf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-horizontal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc-horizontal.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc.svg index f0f280d6c..a8c023a05 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pc.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card-network.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card-network.svg new file mode 100644 index 000000000..a2b4359b9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card-network.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card-sound.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card-sound.svg new file mode 100644 index 000000000..2bb98f211 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card-sound.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card.svg index 600a5d064..66ff052f2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pci-card.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace-fill.svg index c8ed5bdd3..a93e64dce 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace.svg index 3e4420b06..22367e058 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/peace.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen-fill.svg index b7bb33718..59bbb2ebb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen.svg index 8eb3be7d0..a63b25057 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pen.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-fill.svg index 59d2830c5..4b3bdd737 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-square.svg index b8c90d542..95c052975 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil-square.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil.svg index f8dbfebca..0b84e3681 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pencil.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pentagon-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pentagon-half.svg index 305125cb3..6811a9359 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pentagon-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pentagon-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people-fill.svg index 2c7389d23..2b9f76876 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people-fill.svg @@ -1,5 +1,3 @@ - - - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people.svg index 528933d15..341861a40 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/people.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/percent.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/percent.svg index 8af2bc4d0..c0fd22d65 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/percent.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/percent.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-add.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-add.svg new file mode 100644 index 000000000..66e250869 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-arms-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-arms-up.svg new file mode 100644 index 000000000..deb50e8f1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-arms-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge-fill.svg index d9ebe6723..7110ed3ff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge.svg index d071d44bd..680aee15f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-badge.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-bounding-box.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-bounding-box.svg index 92e662c9d..d9be6757f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-bounding-box.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-bounding-box.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check-fill.svg index 872497a65..04b95d340 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check.svg index c4b1e38bc..39b42198f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-circle.svg index fd7f2c92a..a75f25fcc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash-fill.svg index fd719f20d..9879e6e46 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash.svg index 4c6cb7901..b61190bf8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-dash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-down.svg new file mode 100644 index 000000000..79cf29a50 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-exclamation.svg new file mode 100644 index 000000000..46fb5065a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-add.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-add.svg new file mode 100644 index 000000000..d6d15f97e --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-check.svg new file mode 100644 index 000000000..19b88a435 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-check.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-dash.svg new file mode 100644 index 000000000..24c294456 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-dash.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-down.svg new file mode 100644 index 000000000..714ae50ad --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-exclamation.svg new file mode 100644 index 000000000..5c3b7f56d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-exclamation.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-gear.svg new file mode 100644 index 000000000..33b120a1b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-gear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-lock.svg new file mode 100644 index 000000000..adbccff17 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-lock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-slash.svg new file mode 100644 index 000000000..398d56331 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-up.svg new file mode 100644 index 000000000..1edd97be5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-x.svg new file mode 100644 index 000000000..e3a66ed2f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill.svg index 6e1276872..46d1a75f8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-gear.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-gear.svg new file mode 100644 index 000000000..93ec4dacc --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-gear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-heart.svg index e9ebbf48e..51b236e4e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-hearts.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-hearts.svg index 06970be92..70bb2e056 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-hearts.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-hearts.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-lines-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-lines-fill.svg index 736421c59..cbe6c6842 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-lines-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-lines-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-lock.svg new file mode 100644 index 000000000..d3672cdbe --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-lock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus-fill.svg index 151ccfe80..6c92aea54 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus.svg index aac3a6738..4b8842441 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-raised-hand.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-raised-hand.svg new file mode 100644 index 000000000..00ac3011d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-raised-hand.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-rolodex.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-rolodex.svg index af898ca72..203949426 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-rolodex.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-rolodex.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-slash.svg new file mode 100644 index 000000000..ab5364738 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-square.svg index a7eb40efa..12a33c5e4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-standing-dress.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-standing-dress.svg new file mode 100644 index 000000000..44486067a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-standing-dress.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-standing.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-standing.svg new file mode 100644 index 000000000..ccd7b352a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-standing.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-up.svg new file mode 100644 index 000000000..93a430a80 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-vcard-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-vcard-fill.svg new file mode 100644 index 000000000..9efb1b8e7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-vcard-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-vcard.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-vcard.svg new file mode 100644 index 000000000..40ec41e3c --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-vcard.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video.svg index b8c199545..a99175973 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video2.svg index 3f4292e66..80b3b8a98 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video3.svg index be38b2492..472d998c9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-video3.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-walking.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-walking.svg new file mode 100644 index 000000000..16cc0b46e --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-walking.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-wheelchair.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-wheelchair.svg new file mode 100644 index 000000000..416cad205 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-wheelchair.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-workspace.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-workspace.svg index e72bea027..0b3cdce83 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-workspace.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-workspace.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x-fill.svg index d4903a6a4..9e3190394 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x.svg index d7ac8f668..7514c5989 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person.svg index 022d1e99b..98ea060fe 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/person.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-fill.svg index a2dfd0348..f25bd51f2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-flip.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-flip.svg index 54e2d2661..3ae28d393 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-flip.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-flip.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape-fill.svg index 295481c89..669bf6e8f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape.svg index 65cd2731d..4c30ef2be 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-landscape.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate-fill.svg index 6e61ecce4..dc35ca05f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate.svg index f380cabbc..58acbf6e1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone-vibrate.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone.svg index 3f3fd74c1..483933101 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/phone.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart-fill.svg index 6aa71eb89..f667aea44 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart.svg index a20f6a7e4..b49251b08 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pie-chart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank-fill.svg index b44f35d23..592b31d1d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank.svg index 1d836ce19..86e33ebb5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/piggy-bank.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle-fill.svg index 3112c0b85..bc3078c49 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle.svg index a07b038e6..ecc4d16e2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-angle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-fill.svg index f00b79042..f34505963 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map-fill.svg index b8c8502bd..9db0d294b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map-fill.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map.svg index f04129a7b..0462a5aad 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin-map.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin.svg index 45fd7dea6..4655620bf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pin.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pinterest.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pinterest.svg index b4fbc23df..5c850b61a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pinterest.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pinterest.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip-fill.svg index 1869f7892..4865244e7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip.svg index 58f06382f..458c9c9a5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/pip.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn-fill.svg index 18b916786..3d0d1c330 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn.svg index 576e30bf4..2fcbc5ea0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle-fill.svg index 511ef37bf..93eeb93c7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle.svg index c93144ab0..a1d742e00 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-fill.svg index 28f2e6735..e538083af 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play.svg index b3fd3dc5b..98954e7e9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/play.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/playstation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/playstation.svg index f8ce05b6f..3275d599d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/playstation.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/playstation.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug-fill.svg index d15b8e6ae..99858eb84 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug.svg index c5e6688d8..9d6a85b5c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plug.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plugin.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plugin.svg index 3f179a396..92e99d4b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plugin.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plugin.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-dotted.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-dotted.svg index c69316ed9..2a20e2d6f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-dotted.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-dotted.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-fill.svg index f32011643..d1ec2d03b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle.svg index 66308ef1f..283237356 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-lg.svg index f821cc336..531e86cd0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-slash-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-slash-minus.svg index 44a8e0eb7..e0fee7dc9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-slash-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-slash-minus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-dotted.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-dotted.svg index 4ae7ad68b..e230a0890 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-dotted.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-dotted.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-fill.svg index 0d5e15cf8..1dddd1354 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square.svg index 15c4c44f7..ef11b48c7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus.svg index 5b088c08c..9012271f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/plus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-fill.svg index 701a1c646..861a3a119 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart-fill.svg index a26890148..4737a4d5b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart.svg index 4d22b1806..aa35a6cba 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage.svg index cc49c7003..54dcfa610 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postage.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-fill.svg index 01b54dd2c..aeba5185f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart-fill.svg index 5e551fb71..1e371b832 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart.svg index e0f2f05a2..52c0053ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard.svg index 827180e2b..43ba40b2e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/postcard.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/power.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/power.svg index 6fb97563f..937b842dd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/power.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/power.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription.svg index a0f455603..b895b2b74 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription2.svg index 018ca9110..cb278b06b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/prescription2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer-fill.svg index 485d98788..43cee3646 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer.svg index 60196bc5f..0886a570a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/printer.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector-fill.svg index ff6a34189..046166cc3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector.svg index 218c6a5ea..77e68b0bd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/projector.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle-fill.svg index e9bbfae6b..92c4ea06c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle.svg index c9b07a24b..44903f7cb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/puzzle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code-scan.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code-scan.svg index 7eb599e19..3c5338719 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code-scan.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code-scan.svg @@ -1,7 +1,7 @@ - - - - - + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code.svg index bf5570dc4..e09157af9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/qr-code.svg @@ -1,7 +1,7 @@ - - - - - + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle-fill.svg index d8e5e06de..8b2a2c0c2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle.svg index 1c8cbe7f4..283e6536e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond-fill.svg index a86583bed..6bf3512f2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond.svg index a7d8233cc..a777b470c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-diamond.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-lg.svg index fa3452e4d..756ea0fd7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon-fill.svg index 2ff954ed0..c0c43efc5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon.svg index 02e8ffe23..5116862f0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-octagon.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square-fill.svg index dd7241042..a266f9fd5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square.svg index d0a56ffb1..ad44a2077 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question.svg index 05abe29c7..ba185ad07 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/question.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quora.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quora.svg index e90e571b7..85ca1bd1b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quora.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quora.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quote.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quote.svg index 03b45bf0b..0aa0e175a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quote.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/quote.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle-fill.svg index c2386c3c5..810423eeb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle.svg index ab5c574a0..bf2d8d653 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-circle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square-fill.svg index e039b8adc..b1151f36e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square.svg index 37ddc6aec..e19e688a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/r-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/radar.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/radar.svg new file mode 100644 index 000000000..024f3fd58 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/radar.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/radioactive.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/radioactive.svg index 1b1072f7b..3eaaa56bc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/radioactive.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/radioactive.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rainbow.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rainbow.svg index 8e8aea78e..e864abf23 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rainbow.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rainbow.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt-cutoff.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt-cutoff.svg index 27be3c093..21c3bc80f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt-cutoff.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt-cutoff.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt.svg index 9ea728362..ab29fe63d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/receipt.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-0.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-0.svg index 885bf3bb3..a7c78721a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-0.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-0.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-1.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-1.svg index 3deafb622..4081ceb45 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-1.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-1.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-2.svg index 7dca57aca..7e1acc55b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-3.svg index b30d5fb79..e9ea47695 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-4.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-4.svg index 611bdf1b9..7791e4b9a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-4.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reception-4.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn-fill.svg index caa3ea115..83ee303a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn.svg index 4fd261cab..7ba84cea7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle-fill.svg index 2c2429a19..0db59d1b9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle.svg index d45d91c32..5dad17ba9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-fill.svg index d4743936e..ef31df060 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record.svg index 27f82a976..5ec840cac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2-fill.svg index 764892877..8ad4fe3ef 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2.svg index 43a115044..3b5c5caf3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/record2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/recycle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/recycle.svg index e4fa6c03c..21d1bd948 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/recycle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/recycle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reddit.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reddit.svg index b1c9cfed4..777aeaddd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reddit.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reddit.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/regex.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/regex.svg new file mode 100644 index 000000000..ec8bf00ce --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/regex.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat-1.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat-1.svg index 07f4a8b95..9357fcf0c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat-1.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat-1.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat.svg index 0f6d54d86..51765c9f4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/repeat.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all-fill.svg index 7b77b069c..95e18a248 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all.svg index c95025b37..decad51d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-all.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-fill.svg index b5a87228d..82358b165 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply.svg index c2dc098ed..5bb432e7c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/reply.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn-fill.svg index 5136147dd..8ea415568 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn.svg index 45c023285..47bd7179f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle-fill.svg index afdaaf3a6..b97204468 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle.svg index 054fd9526..495214708 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-fill.svg index 79596e0e2..5919f7eb7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind.svg index 58684d4b6..bc731e723 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rewind.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/robot.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/robot.svg index 526cb9914..a2242026d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/robot.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/robot.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-fill.svg new file mode 100644 index 000000000..f3190290a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-takeoff-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-takeoff-fill.svg new file mode 100644 index 000000000..707d20522 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-takeoff-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-takeoff.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-takeoff.svg new file mode 100644 index 000000000..2abc6d4f6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket-takeoff.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket.svg new file mode 100644 index 000000000..b760e1f8e --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rocket.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router-fill.svg index de050cffe..74d14694a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router-fill.svg @@ -1,6 +1,6 @@ - + - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router.svg index 8fa22d0dc..62fac7804 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/router.svg @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss-fill.svg index 39bef06e2..50d7cfdae 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss.svg index be41f20c9..18dc9f1be 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rss.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rulers.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rulers.svg index e9891c9a0..90fb01c7e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rulers.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/rulers.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe-fill.svg index 1036d675a..6da7a7dd8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe.svg index fb5b7cb2a..d6d24c204 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2-fill.svg index d1d37f2f8..064e0736c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2.svg index 37bfbe80d..9c80f556e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/safe2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save-fill.svg index 0a43dc15f..1c42812ef 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save.svg index 26b8aed58..9dd7b2fcb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2-fill.svg index 45feb5938..207f91b88 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2.svg index 52bc9e40f..988c4f1e2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/save2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/scissors.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/scissors.svg index ab71b0dc6..2f566e4c7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/scissors.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/scissors.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/scooter.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/scooter.svg new file mode 100644 index 000000000..8828452ca --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/scooter.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/screwdriver.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/screwdriver.svg index dc9c37435..54d5a2c81 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/screwdriver.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/screwdriver.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card-fill.svg index 9fe36b687..655a96df3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card.svg index 12ed59f71..564661aed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sd-card.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart-fill.svg index 54e31c710..c57bb4874 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart.svg index 92ea0591b..d76bfe52a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search-heart.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search.svg index d3dc7ca16..331805416 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/search.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/segmented-nav.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/segmented-nav.svg index 42323b2f9..b274b68a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/segmented-nav.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/segmented-nav.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-down-fill.svg new file mode 100644 index 000000000..6d43965ea --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-down-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-down.svg new file mode 100644 index 000000000..dcbae5658 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-down.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-up-fill.svg new file mode 100644 index 000000000..19abab722 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-up-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-up.svg new file mode 100644 index 000000000..a642dac46 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-arrow-up.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check-fill.svg index 4b0a56af9..c4259c8b3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check.svg index 581ebbe22..522186874 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash-fill.svg index 254329c0c..12a82df0d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash.svg index abfbad373..63fc38c7e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-dash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation-fill.svg index 5a77e9822..fce810f55 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation.svg index 149a7f74e..8a72f31de 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-exclamation.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-fill.svg index 2a84015d7..6e95d2753 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus-fill.svg index bea3738a6..63b04824d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus.svg index 41202289e..350b388dd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash-fill.svg index 33456870b..e98aa9297 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash.svg index 782daf3e0..e434afec5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-slash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x-fill.svg index ce102ba4b..45a98a4d7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x.svg index c8bc8bf3d..5b854c2eb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send.svg index c81fc9553..8db355ea0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/send.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/server.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/server.svg index ff85feb8d..bb8ca8f3b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/server.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/server.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shadows.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shadows.svg new file mode 100644 index 000000000..6b09f0f24 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shadows.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share-fill.svg index 8b0ee88d3..bdc07ca0b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share.svg index 79d3075f0..bc62b9362 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/share.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-check.svg index ecbf54399..3908fca3d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-check.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-exclamation.svg index 825de04d7..9826504aa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-exclamation.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-exclamation.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-check.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-check.svg index a72b2baf1..f914f1f48 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-check.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-check.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-exclamation.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-exclamation.svg index b489a6816..99a6bf916 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-exclamation.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-exclamation.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-minus.svg index b9b9129c5..584f5ae15 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-minus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-plus.svg index aec96d176..43a31696b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-plus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-x.svg index d384af45f..42267cff7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill-x.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill.svg index d1d877daf..12a61bc1c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock-fill.svg index e4c96b4ea..0fccf6f28 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock.svg index ff3842503..316fb3c03 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-lock.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-minus.svg index d1cedfdf7..9fb8712c8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-plus.svg index 77bcb1a33..3b19b28e9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-shaded.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-shaded.svg index 9c4af1a72..4908f5d82 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-shaded.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-shaded.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash-fill.svg index 015d11b55..d270d6d79 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash.svg index 234afa2e4..abc01b189 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-slash.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-x.svg index 3fe166618..cc9c59d05 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield.svg index 18309d181..7e18d1b1c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shield.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift-fill.svg index da897bcd1..37583e1f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift.svg index 59a88ef55..5d8a6e320 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shift.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop-window.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop-window.svg index a306cfa41..14e0d422d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop-window.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop-window.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop.svg index 223d77b42..e6bb8c054 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shop.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shuffle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shuffle.svg index 83bf20caf..2787bf273 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shuffle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/shuffle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-dead-end-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-dead-end-fill.svg new file mode 100644 index 000000000..b362833f5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-dead-end-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-dead-end.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-dead-end.svg new file mode 100644 index 000000000..b87d368e6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-dead-end.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-do-not-enter-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-do-not-enter-fill.svg new file mode 100644 index 000000000..f86ebfab1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-do-not-enter-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-do-not-enter.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-do-not-enter.svg new file mode 100644 index 000000000..2e2c877b3 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-do-not-enter.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-fill.svg new file mode 100644 index 000000000..7fd8f3f3b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-side-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-side-fill.svg new file mode 100644 index 000000000..38870b56a --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-side-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-side.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-side.svg new file mode 100644 index 000000000..df9015a3b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-side.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-t-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-t-fill.svg new file mode 100644 index 000000000..15a007e01 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-t-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-t.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-t.svg new file mode 100644 index 000000000..4ba9f6f73 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-t.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-y-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-y-fill.svg new file mode 100644 index 000000000..01a03d0c6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-y-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-y.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-y.svg new file mode 100644 index 000000000..e0e387f80 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection-y.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection.svg new file mode 100644 index 000000000..be2ffdc58 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-intersection.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-left-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-left-fill.svg new file mode 100644 index 000000000..1408133ec --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-left-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-left.svg new file mode 100644 index 000000000..3447bcfab --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-right-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-right-fill.svg new file mode 100644 index 000000000..a952bb56f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-right-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-right.svg new file mode 100644 index 000000000..ab3e08aee --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-merge-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-left-turn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-left-turn-fill.svg new file mode 100644 index 000000000..85f421a71 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-left-turn-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-left-turn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-left-turn.svg new file mode 100644 index 000000000..d45f090aa --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-left-turn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-parking-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-parking-fill.svg new file mode 100644 index 000000000..c4100d988 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-parking-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-parking.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-parking.svg new file mode 100644 index 000000000..1679603c1 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-parking.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-right-turn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-right-turn-fill.svg new file mode 100644 index 000000000..c3883da59 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-right-turn-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-right-turn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-right-turn.svg new file mode 100644 index 000000000..209b918ae --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-no-right-turn.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-railroad-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-railroad-fill.svg new file mode 100644 index 000000000..61d88a979 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-railroad-fill.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-railroad.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-railroad.svg new file mode 100644 index 000000000..b5d7339e5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-railroad.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-fill.svg index d9b51dc78..08efb9a51 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights-fill.svg index 30c81bee1..9be8e0c23 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights.svg index 297320ad6..85918cf2c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop-lights.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop.svg index 14def7439..49128dccc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-stop.svg @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left-fill.svg index 1c82bcaba..4b8358e38 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left.svg index bea00b71b..c1b34eda4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right-fill.svg index 97c6343a8..29d8d2c22 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right.svg index da447dbe6..956614aed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left-fill.svg index 9d561754f..80b2977aa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left.svg index 79475ad0b..98f0a0ab8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-left.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right-fill.svg index 1749e66ad..273495239 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right.svg index cf22c012a..c462f1948 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-turn-slight-right.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield-fill.svg index ecad4fa8d..79fa190e8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield-fill.svg @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield.svg index aabf3fb41..23bd623d6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sign-yield.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signal.svg index 4220d4887..1583f9778 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signal.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2-fill.svg index cc51e517d..58c05a6fd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2.svg index 6a18b3ba0..e3454bdc8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-fill.svg index f95f257a9..00989a6aa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split-fill.svg index 86aa086f5..9b720f02a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split.svg index 0168ae537..7fb69b7f2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost-split.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost.svg index 90a88822d..940e66436 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/signpost.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-fill.svg index c8e2c296e..c7922b67a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-slash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-slash-fill.svg new file mode 100644 index 000000000..b608de7d6 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-slash-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-slash.svg new file mode 100644 index 000000000..c8e0dc4de --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim-slash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim.svg index cc0e86954..0d71a11c9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sim.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sina-weibo.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sina-weibo.svg new file mode 100644 index 000000000..6d484d2e5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sina-weibo.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn-fill.svg index bf064290f..202948974 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn.svg index b04455efb..7cae681aa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle-fill.svg index f6b6e4db0..75263569d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle.svg index 63e2a1956..8739f31c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-fill.svg index a0ce53cec..bf8f63eba 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward.svg index 9be60fe7f..ff5b8210c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-backward.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn-fill.svg index 55bf1ba8f..e721821a2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn.svg index 6c5b044b4..6815577b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle-fill.svg index e30375082..63c0f68af 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle.svg index 39e8cd31f..50f41d95e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-fill.svg index fa90d3fc2..afa88e33e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end.svg index 40d6fa992..b2dfde6df 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-end.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn-fill.svg index b767e9c97..954795701 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn.svg index f67d3a805..46a61b0c4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle-fill.svg index 00cea35ea..aefb63381 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle.svg index 3b55d7e3a..7ebc92831 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-fill.svg index c4071aa87..6c54d7999 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward.svg index a1c4720b7..c69cfc547 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-forward.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn-fill.svg index 56a1370fb..a0af7020a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn.svg index c86afbe44..b829fdd96 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle-fill.svg index b6d13b0aa..bb33ab012 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle.svg index f9664d9f6..d0e3323ba 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-fill.svg index c4295fc1f..56cccc3d0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start.svg index a178e0e09..76811a93c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skip-start.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skype.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skype.svg index b3beaf95d..ad4be4db3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skype.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/skype.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slack.svg index f4aa6e6d6..d914abe40 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slack.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slack.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle-fill.svg index f7031017a..5f71707bd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle.svg index 4c1344bf7..eb26f19e5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-lg.svg index 161b6ec7e..8b8b132e9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square-fill.svg index c7a393503..6fc915365 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square.svg index ccf42bd63..0757006a7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash.svg index 9616561fd..6d18af4cf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/slash.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders.svg index da4b8353f..c64a06cac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2-vertical.svg index c474281c7..4fcb8ba11 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2-vertical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2-vertical.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2.svg index 86fa70c35..975861eca 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sliders2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/smartwatch.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/smartwatch.svg index 696bd331e..0a11991b0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/smartwatch.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/smartwatch.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snapchat.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snapchat.svg index 505f55a3f..01d3684f3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snapchat.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snapchat.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow2.svg index cede335d6..6533d6320 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow3.svg index 75e5ef24c..01c0d73f7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/snow3.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down-alt.svg index fa4f4fada..d03f1aaf4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down-alt.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down.svg index e0fcad047..6ac3e84f3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-down.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up-alt.svg index 69c1a39a1..a7b332e6c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up-alt.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up.svg index 0be5e68fb..c5f0e3abf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-alpha-up.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down-alt.svg index d7f7fc8f7..86a1bf6f1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down-alt.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down.svg index 848834ce8..8cfdf23a6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down-alt.svg index 8c39a5a38..ce4e3c5dc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down-alt.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down.svg index 57a3fb030..afa87bea7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-down.svg @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up-alt.svg index e8edf8865..d83cbf967 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up-alt.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up.svg index 1cd0a37e1..25a1e548e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-numeric-up.svg @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up-alt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up-alt.svg index 96650d5aa..9f78a20e4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up-alt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up-alt.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up.svg index 215880124..cda9ac719 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sort-up.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/soundwave.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/soundwave.svg index 288f108b2..1444777d5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/soundwave.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/soundwave.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sourceforge.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sourceforge.svg new file mode 100644 index 000000000..13d0c5f81 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sourceforge.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker-fill.svg index bae80e2da..f6d9e3347 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker.svg index 461626db5..1415b5d2a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speaker.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer.svg index 5a0a43c94..f6e3e616f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer2.svg index d5676df9f..75e79c85e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/speedometer2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spellcheck.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spellcheck.svg index 029950f5a..69fec768d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spellcheck.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spellcheck.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spotify.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spotify.svg index 31b423800..09d0e9fd7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spotify.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/spotify.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-fill.svg index 31bae4f74..1e72d5ee9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-half.svg index 3f8179d5d..aa3e349a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square.svg index ded82d436..0f086dee9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/square.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack-overflow.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack-overflow.svg index b7d482ded..c5e5be6f9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack-overflow.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack-overflow.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack.svg index b8a9c940e..3cf0ecad6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stack.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star-half.svg index 8d30e7e01..8a70f537c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star.svg index 742b5e250..fcdcb1cf3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/star.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stars.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stars.svg index 2c1667796..b6fb4f25d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stars.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stars.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/steam.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/steam.svg index aecd43398..9daa3d3ed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/steam.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/steam.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies-fill.svg index a0252da0f..039c3b865 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies.svg index 8252c4978..24e6492a3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stickies.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky-fill.svg index acd42b9af..b36dcb125 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky.svg index dba01423b..0d50e884f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sticky.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn-fill.svg index 58b6c02e2..70e562e2e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn.svg index 5c392eca1..26348d56d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-btn.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle-fill.svg index ac711e030..141668efb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle.svg index 441613cac..3e1933a63 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-fill.svg index e00085a1e..ca1b9573d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop.svg index 2b86647fe..27f1fb6c6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stop.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights-fill.svg index a18566b16..f0b2d767c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights.svg index f765ab2ba..6db3e122d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stoplights.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch-fill.svg index 2d2ed116e..1228cf2b5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch.svg index 964dbb8f8..aff8c338f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stopwatch.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/strava.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/strava.svg index 7e3237d5f..0ed6bab82 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/strava.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/strava.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stripe.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stripe.svg new file mode 100644 index 000000000..ba961a06b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/stripe.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/subscript.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/subscript.svg new file mode 100644 index 000000000..51f5eea76 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/subscript.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/substack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/substack.svg new file mode 100644 index 000000000..e54179ae2 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/substack.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/subtract.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/subtract.svg index e1d878a88..129c3d5fb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/subtract.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/subtract.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club-fill.svg index d4d311ac6..a787160d8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club.svg index 75e5e8520..3fbf98b0a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-club.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond-fill.svg index 2be1b7fa8..67617d606 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond.svg index 9192a27e4..79b54c1a3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-diamond.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart-fill.svg index 0dd86f999..d09850c05 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart.svg index c761ef4b9..173b32ff0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-heart.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade-fill.svg index 63bb0c63f..cc465e5c5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade.svg index 8f14427ed..7123c1013 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suit-spade.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-fill.svg new file mode 100644 index 000000000..df6265139 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-lg-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-lg-fill.svg new file mode 100644 index 000000000..cef1da9e5 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-lg-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-lg.svg new file mode 100644 index 000000000..ea447d2a9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase.svg new file mode 100644 index 000000000..65e619dad --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase2-fill.svg new file mode 100644 index 000000000..a2cb410e4 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase2-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase2.svg new file mode 100644 index 000000000..e6ea53398 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/suitcase2.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun-fill.svg index cc1a60e6e..c83f69ab5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun.svg index c3112080e..3777f070f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sun.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunglasses.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunglasses.svg index 3f7dad01c..1ff81f77d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunglasses.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunglasses.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise-fill.svg index eb6a6687d..c922d7c5d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise.svg index 53d670ddd..98adcfb43 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunrise.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset-fill.svg index 7f5b60ecb..91a8d0ed6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset.svg index 91041cfec..e72d634fa 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/sunset.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/superscript.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/superscript.svg new file mode 100644 index 000000000..81543ae5b --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/superscript.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-horizontal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-horizontal.svg index 7e46d9049..594735b06 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-horizontal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-horizontal.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-vertical.svg index a18fa2f45..6907280da 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-vertical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/symmetry-vertical.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/table.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/table.svg index 5e70d22c4..8f705853c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/table.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/table.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-fill.svg index 571ae8f96..0746ead93 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape-fill.svg index a4a604811..6290024c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape.svg index b36f7d414..438d4d13c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet-landscape.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet.svg index be81ff5ac..eebeee387 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tablet.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag-fill.svg index 1502792b9..6a95e2de9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag.svg index ab34fdd9f..01d19b53e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tag.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags-fill.svg index f92a36101..1673abbff 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags.svg index 9f6d67646..ade5519a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tags.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/taxi-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/taxi-front-fill.svg new file mode 100644 index 000000000..ef7f45b62 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/taxi-front-fill.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/taxi-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/taxi-front.svg new file mode 100644 index 000000000..1b4337ae7 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/taxi-front.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telegram.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telegram.svg index 139af07eb..d26026677 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telegram.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telegram.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-fill.svg index efc72c073..2e9de2e0e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward-fill.svg index f4ce48311..26fc35f6d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward.svg index 17ec9ce7c..08c07bd8b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-forward.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound-fill.svg index 998c8fbe0..85434d075 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound.svg index 460fe9f27..8ec20a64c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-inbound.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus-fill.svg index bc17abbfe..7b2fe9d3c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus.svg index 4f4d93cf4..6ebc50ed8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-minus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound-fill.svg index 16013a545..0a18bda45 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound.svg index 13828860d..566eb4656 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-outbound.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus-fill.svg index 6d8c58f53..b02874f85 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus.svg index 21ef90953..787e0c697 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x-fill.svg index c8ef89497..5410e1644 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x.svg index 5aa3f95d4..3f483a169 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone.svg index 8e359b825..679e8a9e9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/telephone.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tencent-qq.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tencent-qq.svg new file mode 100644 index 000000000..0d5cd2333 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tencent-qq.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-dash.svg index 9049b5e84..9f46e8ea8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-dash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-fill.svg index d3c63943d..fabd07544 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-plus.svg index be268c8e1..32c643263 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-plus.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-split.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-split.svg index f65d2c7a2..a378c379e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-split.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-split.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-x.svg index 5128f115e..aa59e7f42 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal-x.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal.svg index e12c9f882..44aef959f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/terminal.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-center.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-center.svg index 2887a99f2..12d9e297c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-center.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-center.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-left.svg index 34d8c55e9..5a607af0c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-right.svg index fdd837fa3..de91d9e45 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-indent-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-left.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-left.svg index 045261164..36ae0d376 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-left.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-left.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-paragraph.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-paragraph.svg index 9779beabf..035a1c80d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-paragraph.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-paragraph.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-right.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-right.svg index 34686b0f1..98178e754 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-right.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-right.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-wrap.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-wrap.svg new file mode 100644 index 000000000..4c732d6cf --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/text-wrap.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-resize.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-resize.svg index c4a9d9fcc..64013203b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-resize.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-resize.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-t.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-t.svg index dc7e17c27..145cbb77a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-t.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea-t.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea.svg index 9aa54459d..176ca258a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/textarea.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-half.svg index cafefd29b..018eab1fd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-half.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-high.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-high.svg index 15acf4c6a..22e77d103 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-high.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-high.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-low.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-low.svg index ce540e03b..1f0f5e18f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-low.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-low.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-snow.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-snow.svg index 0e1b4002f..df7c1d19c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-snow.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-snow.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-sun.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-sun.svg index 07c329095..c453dee77 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-sun.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer-sun.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer.svg index 748813ec0..8a5529bc0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thermometer.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/threads-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/threads-fill.svg new file mode 100644 index 000000000..b19666ddd --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/threads-fill.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/threads.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/threads.svg new file mode 100644 index 000000000..13c9e7a61 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/threads.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots-vertical.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots-vertical.svg index cd0c79abb..f5ef7d475 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots-vertical.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots-vertical.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots.svg index ea92369aa..4706f52a9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/three-dots.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt-fill.svg index 85c437ee0..1faea430b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt.svg index b8356da42..365565939 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/thunderbolt.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed-fill.svg index bc5d192bc..cd7a3a79d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed.svg index c2701bbe7..cf32e881f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-detailed.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-fill.svg index 73728b6d5..01e910875 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated-fill.svg index 2ec1d57e7..38c18dcef 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated.svg index 194ae05e1..da4453776 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket-perforated.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket.svg index f24a93e58..eb813a18c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ticket.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tiktok.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tiktok.svg index 7edac4ee0..04c6679ea 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tiktok.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tiktok.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-off.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-off.svg index 97d6dab37..e1e89ad0b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-off.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-off.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-on.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-on.svg index d13b49556..7cd6eb3b5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-on.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle-on.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-off.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-off.svg index a8fee6b1b..61739ce1f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-off.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-off.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-on.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-on.svg index 993ec3327..d752ce821 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-on.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggle2-on.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles.svg index d53ae018a..659c18552 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles2.svg index 862fc9f2c..2f9034425 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/toggles2.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tools.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tools.svg index fcc8362f7..f6efdcc6b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tools.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tools.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tornado.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tornado.svg index 5bb53a263..2a6397cb0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tornado.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tornado.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front-fill.svg index 54210a766..e2720516d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front.svg index f1080fe66..097c9600b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-freight-front.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front-fill.svg index 0895443a4..4acad08e0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front.svg index 672ed4bb1..81ce139fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-front.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front-fill.svg index 881ffbcaf..7bd87fb4e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front.svg index db5f945b5..d7aa87fc3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/train-lightrail-front.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/translate.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/translate.svg index 39a17d228..2e0754e69 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/translate.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/translate.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/transparency.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/transparency.svg new file mode 100644 index 000000000..289a4b947 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/transparency.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash-fill.svg index 1a20e6a04..b67453ac0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash.svg index 0ba7218ec..3020264c9 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2-fill.svg index bc78b6d6a..fb1d90dd0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2.svg index 6e6468efb..0cabe8d6b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3-fill.svg index e0e81f1aa..42fbfc509 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3.svg index 1d5f42eed..5194bf087 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trash3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree-fill.svg index 4d45dd430..d00e7335e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree.svg index b97eb64f7..17a5efe95 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tree.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trello.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trello.svg new file mode 100644 index 000000000..bd88732fb --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trello.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-fill.svg index 654787f2d..474c8bb47 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-half.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-half.svg index 8f86f28c3..a495ca0c3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-half.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle-half.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle.svg index 1fa1898e4..95a6a9b8d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/triangle.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy-fill.svg index e29f0013e..f4697377e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy.svg index adfa10830..ae1395782 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/trophy.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tropical-storm.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tropical-storm.svg index c16188d48..9eb335443 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tropical-storm.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tropical-storm.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-flatbed.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-flatbed.svg index 5a37c8d88..4b381557c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-flatbed.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-flatbed.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front-fill.svg index f5b63e3a4..39f72d044 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front.svg index a676a7182..d805db5b7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck-front.svg @@ -1,5 +1,4 @@ - - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck.svg index 1afc549c1..72c54392f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/truck.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tsunami.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tsunami.svg index cf574864c..be5f9bea5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tsunami.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tsunami.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv-fill.svg index bf9830f36..483c9fda3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv.svg index bba3da169..fa8b3c19c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/tv.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitch.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitch.svg index 2975f8058..b2c8ff5a0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitch.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitch.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitter-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitter-x.svg new file mode 100644 index 000000000..2fafcc2df --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitter-x.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitter.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitter.svg index 8a83fa675..30013529a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitter.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/twitter.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-bold.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-bold.svg index 276d133c2..0814a2e4a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-bold.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-bold.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h1.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h1.svg index 4c8918175..0df41f6cf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h1.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h1.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h2.svg index b6ab76501..03379edb3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h3.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h3.svg index 154c293f8..97de53125 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h3.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h3.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h4.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h4.svg new file mode 100644 index 000000000..a7ddc818f --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h4.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h5.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h5.svg new file mode 100644 index 000000000..776bfa3be --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h5.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h6.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h6.svg new file mode 100644 index 000000000..926719294 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-h6.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-strikethrough.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-strikethrough.svg index 1c940e42a..c64eba34a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-strikethrough.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-strikethrough.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-underline.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-underline.svg index c299b8bf2..1c0b6c474 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-underline.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type-underline.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type.svg index 9ab1e4c48..8c1fde12c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/type.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ubuntu.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ubuntu.svg index 27f8c2782..89c88307c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ubuntu.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ubuntu.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks-grid.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks-grid.svg index a32d42410..e5d1ed9c0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks-grid.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks-grid.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks.svg index 9b659e271..5d028698c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-checks.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios-grid.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios-grid.svg index 00c7b0802..9f9aae0c2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios-grid.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios-grid.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios.svg index da779afc7..9165340d8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/ui-radios.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella-fill.svg index c4886e9af..3efaf13e2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella.svg index 94f32f906..f7b698cd6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/umbrella.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unindent.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unindent.svg index 19692833b..9e6825586 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unindent.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unindent.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/union.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/union.svg index b629b8816..ba23f543c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/union.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/union.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unity.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unity.svg index e179a383b..8b84508a1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unity.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unity.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access-circle.svg index 158465b46..e5ea9361c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access.svg index 3b7fc37e0..0d0d6efda 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/universal-access.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock-fill.svg index f0533548c..07156e7ed 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock.svg index 8eb0925da..4dda5e963 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/unlock.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc-scan.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc-scan.svg index 2a9a6aff0..1a8955491 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc-scan.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc-scan.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc.svg index 6669ef7a1..785297d35 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upc.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upload.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upload.svg index be3f8e378..9a4a363c5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upload.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/upload.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c-fill.svg index 0e50ac6a7..759eee2d1 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c.svg index c17d4ca1b..11983325e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-c.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive-fill.svg index 834614dd7..2f656ee63 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive.svg index ca08df5bc..739051d93 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-drive.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-fill.svg index 443c91a65..a3b17faf2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro-fill.svg index 67ad74472..1469a9bc3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro.svg index 945b6e75d..ece7da74f 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-micro.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini-fill.svg index 723563682..3ab0747b4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini.svg index 7cc383f0f..f095b67c0 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-mini.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug-fill.svg index 2f1c185cd..d1dc51800 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug.svg index 68f5f9788..f3d72209a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-plug.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-symbol.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-symbol.svg index eb02d87ae..457f93f10 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-symbol.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb-symbol.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb.svg index e82324c40..737bef528 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/usb.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine.svg index 554205510..7b8f0a3fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine2.svg index c70e2741a..6d95a2d16 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/valentine2.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vector-pen.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vector-pen.svg index 013acc258..60115b772 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vector-pen.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vector-pen.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-list.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-list.svg index 3d1a972e4..921154397 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-list.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-list.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-stacked.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-stacked.svg index 7f59bb987..84b5ccf79 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-stacked.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/view-stacked.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vignette.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vignette.svg new file mode 100644 index 000000000..d179290c9 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vignette.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vimeo.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vimeo.svg index 34eea6d24..6b8e4b5bf 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vimeo.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vimeo.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl-fill.svg index a5ab73ded..546d7bb41 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl.svg index 75c2681d9..63647e56c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vinyl.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus.svg index 64dd56f4b..fd291a572 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus2.svg index 0aa390150..53f44e942 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/virus2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/voicemail.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/voicemail.svg index ff7ce86cb..ba22eb1fc 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/voicemail.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/voicemail.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down-fill.svg index 4879b5a72..681d349e7 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down.svg index 996dbefd6..3ca7e6a8d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-down.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute-fill.svg index 7ab768441..148628c2d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute.svg index 12659d9aa..d06d3dc2d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-mute.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off-fill.svg index 4941870e7..315110afb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off.svg index 08bb6b99c..e5f82ccbd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-off.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up-fill.svg index 495ee981a..0f94073e2 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up-fill.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up.svg index 3840310db..6347f4210 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/volume-up.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vr.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vr.svg index cf2ea3761..5ad5438cb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vr.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/vr.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet-fill.svg index d44e5c836..ee1c27d0c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet.svg index d18441bcf..6c9d247db 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet2.svg index e646d94de..b127b0e53 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wallet2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/watch.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/watch.svg index 8c3ee988e..542d4d87a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/watch.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/watch.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/water.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/water.svg index 18e08257b..666653bac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/water.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/water.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam-fill.svg index 04b835bdf..e8db7ba89 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam.svg index da7ef71e2..0d23803da 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/webcam.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wechat.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wechat.svg index 06b8ff83a..3bc67dd97 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wechat.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wechat.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/whatsapp.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/whatsapp.svg index 6242d057c..5cde6f781 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/whatsapp.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/whatsapp.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-1.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-1.svg index 4d75ef524..5f3d3407b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-1.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-1.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-2.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-2.svg index 377c1fc9f..09d26c236 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-2.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-2.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-off.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-off.svg index 439986136..2f5e61ffb 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-off.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi-off.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi.svg index 8cb1f71d9..773e027da 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wifi.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wikipedia.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wikipedia.svg new file mode 100644 index 000000000..11f2fc6a8 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wikipedia.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wind.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wind.svg index d350ea404..2ac05cd8c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wind.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wind.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dash.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dash.svg index 191fbd72c..5e157af25 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dash.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dash.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-desktop.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-desktop.svg index a044521e2..fa1752337 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-desktop.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-desktop.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dock.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dock.svg index dbffecbc8..41cdf699b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dock.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-dock.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-fullscreen.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-fullscreen.svg index 22a8d2083..421c4c510 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-fullscreen.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-fullscreen.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-plus.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-plus.svg index 08444f3af..e24ce0c28 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-plus.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-plus.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-sidebar.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-sidebar.svg index 98476ce87..d020d135e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-sidebar.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-sidebar.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-split.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-split.svg index 21862f253..96bdd2415 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-split.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-split.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-stack.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-stack.svg index 592e5c85f..886297639 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-stack.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-stack.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-x.svg index e7a97dc60..c45e07888 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window-x.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window.svg index ad6166e69..9bd2a2af8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/window.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/windows.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/windows.svg index b28056079..af3b18c5c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/windows.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/windows.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wordpress.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wordpress.svg index 4c8cbc412..7d5808ce6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wordpress.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wordpress.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle-fill.svg index b723d7f66..33156c7e6 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle-fill.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle.svg index a5a6f0b79..381fb304b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable.svg index 4ec808244..e7456d7f5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench-adjustable.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench.svg index bef07136f..806cca089 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/wrench.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle-fill.svg index 448fdee46..4070fb35a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle.svg index ce37cdc36..0e8c641ac 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-circle.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond-fill.svg index 2de64033e..6ec461a65 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond.svg index 0ade53696..b93295e2a 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-diamond.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-lg.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-lg.svg index 53aec00d4..b689cbb4d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-lg.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-lg.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon-fill.svg index 7872889d5..dd9fc6a7b 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon.svg index 794afd942..181a39f5d 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-octagon.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square-fill.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square-fill.svg index ddfd727a0..5499578e4 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square-fill.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square-fill.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square.svg index 9d7852f6a..eb62b6173 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x-square.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x.svg index c865d888e..fdcc4e824 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/x.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/xbox.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/xbox.svg index 9d8497330..c0672b26c 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/xbox.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/xbox.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yelp.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yelp.svg index 08d346515..76e8884bd 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yelp.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yelp.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yin-yang.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yin-yang.svg index cf1da48fb..1f502754e 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yin-yang.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/yin-yang.svg @@ -1,4 +1,4 @@ - - + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/youtube.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/youtube.svg index 86fa4900a..3c9c0be38 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/youtube.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/youtube.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-in.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-in.svg index 6cde1a0b5..438e9bca3 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-in.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-in.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-out.svg b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-out.svg index b965f8e71..8be9f29a8 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-out.svg +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/icons/zoom-out.svg @@ -1,5 +1,5 @@ - - - + + + \ No newline at end of file diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/package.json b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/package.json index 0bac95cf0..937df93b5 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/package.json +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap-icons/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-icons", - "version": "1.9.1", + "version": "1.11.3", "description": "Official open source SVG icon library for Bootstrap", "author": "mdo", "license": "MIT", @@ -12,59 +12,80 @@ "bugs": { "url": "https://github.com/twbs/icons/issues" }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], "keywords": [ "bootstrap", - "icons" + "icons", + "svg", + "font", + "sprite", + "woff", + "woff2" + ], + "style": "font/bootstrap-icons.css", + "sass": "font/bootstrap-icons.scss", + "files": [ + "icons/*.svg", + "bootstrap-icons.svg", + "font", + "!.DS_Store" ], + "hugo-bin": { + "buildTags": "extended" + }, "scripts": { "start": "npm run docs-serve", "docs-serve": "hugo server --port 4000 --disableFastRender", - "docs-build": "hugo --cleanDestinationDir", - "docs-purge": "npm run docs-build && purgecss --css docs/static/assets/css/bootstrap.min.css --content \"_site/**/*.html\" \"_site/assets/js/**/*.js\" --keyframes --output docs/static/assets/css/", - "pages": "node build/build-pages.js", + "docs-build": "hugo --cleanDestinationDir --printUnusedTemplates", + "docs-test": "npm-run-all docs-build docs-test:vnu", + "docs-test:vnu": "node build/vnu-jar.mjs", + "pages": "node build/build-pages.mjs", "icons": "npm-run-all icons-main --aggregate-output --parallel icons-sprite icons-font", - "icons-main": "node build/build-svgs.js", - "icons-zip": "cross-env-shell \"rm -rf bootstrap-icons-$npm_package_version && cp -r icons/ bootstrap-icons-$npm_package_version && cp bootstrap-icons.svg bootstrap-icons-$npm_package_version && cp -r font/ bootstrap-icons-$npm_package_version && zip -r9 bootstrap-icons-$npm_package_version.zip bootstrap-icons-$npm_package_version && rm -rf bootstrap-icons-$npm_package_version\"", - "icons-sprite": "svg-sprite --config svg-sprite.json --log=info icons/*.svg", - "icons-font": "fantasticon", + "icons-main": "node build/build-svgs.mjs", + "icons-zip": "cross-env-shell \"rm -rf bootstrap-icons-$npm_package_version bootstrap-icons-$npm_package_version.zip && cp -r icons/ bootstrap-icons-$npm_package_version && cp bootstrap-icons.svg bootstrap-icons-$npm_package_version && cp -r font/ bootstrap-icons-$npm_package_version && zip -qr9 bootstrap-icons-$npm_package_version.zip bootstrap-icons-$npm_package_version && rm -rf bootstrap-icons-$npm_package_version\"", + "icons-sprite": "svg-sprite --config svg-sprite.json --log=info \"icons/*.svg\"", + "icons-font": "npm-run-all icons-font-*", + "icons-font-main": "fantasticon", + "icons-font-min": "cleancss -O1 --format breakWith=lf --with-rebase --output font/bootstrap-icons.min.css font/bootstrap-icons.css", "release": "npm-run-all icons docs-build icons-zip", - "netlify": "cross-env-shell HUGO_BASEURL=$DEPLOY_PRIME_URL npm-run-all icons docs-purge docs-build", + "release-version": "node build/bump-version.mjs", + "netlify": "cross-env-shell HUGO_BASEURL=$DEPLOY_PRIME_URL npm-run-all icons docs-build", "test:fusv": "fusv docs/assets/scss/", - "test:eslint": "eslint --cache --cache-location node_modules/.cache/.eslintcache --report-unused-disable-directives .", - "test:stylelint": "stylelint docs/assets/scss/ --cache --cache-location node_modules/.cache/.stylelintcache --rd", + "test:eslint": "eslint --cache --cache-location .cache/.eslintcache --report-unused-disable-directives --ext .js,.mjs .", + "test:stylelint": "stylelint docs/assets/scss/ --cache --cache-location .cache/.stylelintcache", "test:lockfile-lint": "lockfile-lint --allowed-hosts npm --allowed-schemes https: --empty-hostname false --type npm --path package-lock.json", - "test:vnu": "node build/vnu-jar.js", - "test": "npm-run-all docs-build --parallel --aggregate-output --continue-on-error test:*" + "test:check-icons": "node build/check-icons.mjs", + "test": "npm-run-all --parallel --aggregate-output --continue-on-error test:*" }, - "style": "font/bootstrap-icons.css", - "sass": "font/bootstrap-icons.scss", "devDependencies": { - "autoprefixer": "^10.4.7", - "bootstrap": "5.2.0-beta1", + "@twbs/fantasticon": "^2.7.2", + "autoprefixer": "^10.4.16", + "bootstrap": "^5.3.2", + "clean-css-cli": "^5.6.3", + "clipboard": "^2.0.11", "cross-env": "^7.0.3", - "eslint": "^8.19.0", - "fantasticon": "^1.2.3", - "find-unused-sass-variables": "^4.0.4", - "hugo-bin": "^0.89.0", - "lockfile-lint": "^4.7.6", - "npm-run-all": "^4.1.5", + "eslint": "^8.56.0", + "find-unused-sass-variables": "^5.0.0", + "fuse.js": "^7.0.0", + "hugo-bin": "^0.118.0", + "lockfile-lint": "^4.12.1", + "npm-run-all2": "^6.1.1", "picocolors": "^1.0.0", - "postcss": "^8.4.14", - "postcss-cli": "^10.0.0", - "purgecss": "^4.1.3", - "stylelint": "^14.9.1", - "stylelint-config-twbs-bootstrap": "^4.0.0", - "svg-sprite": "^2.0.0-beta7", - "svgo": "^2.8.0", - "vnu-jar": "21.10.12" - }, - "files": [ - "icons/*.svg", - "bootstrap-icons.svg", - "font", - "!.DS_Store" - ], - "hugo-bin": { - "buildTags": "extended" + "postcss": "^8.4.32", + "postcss-cli": "^11.0.0", + "stylelint": "^16.1.0", + "stylelint-config-twbs-bootstrap": "^13.0.0", + "svg-sprite": "^3.0.0-beta3", + "svgo": "^3.2.0", + "vnu-jar": "23.4.11" } } diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/LICENSE b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/LICENSE index 72dda234e..2a703f519 100644 --- a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/LICENSE +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/LICENSE @@ -1,7 +1,6 @@ The MIT License (MIT) -Copyright (c) 2011-2021 Twitter, Inc. -Copyright (c) 2011-2021 The Bootstrap Authors +Copyright (c) 2011-2024 The Bootstrap Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/README.md b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/README.md new file mode 100644 index 000000000..cb51a5617 --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/README.md @@ -0,0 +1,246 @@ +

+ + Bootstrap logo + +

+ +

Bootstrap

+ +

+ Sleek, intuitive, and powerful front-end framework for faster and easier web development. +
+ Explore Bootstrap docs » +
+
+ Report bug + · + Request feature + · + Themes + · + Blog +

+ + +## Bootstrap 5 + +Our default branch is for development of our Bootstrap 5 release. Head to the [`v4-dev` branch](https://github.com/twbs/bootstrap/tree/v4-dev) to view the readme, documentation, and source code for Bootstrap 4. + + +## Table of contents + +- [Quick start](#quick-start) +- [Status](#status) +- [What's included](#whats-included) +- [Bugs and feature requests](#bugs-and-feature-requests) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [Community](#community) +- [Versioning](#versioning) +- [Creators](#creators) +- [Thanks](#thanks) +- [Copyright and license](#copyright-and-license) + + +## Quick start + +Several quick start options are available: + +- [Download the latest release](https://github.com/twbs/bootstrap/archive/v5.3.3.zip) +- Clone the repo: `git clone https://github.com/twbs/bootstrap.git` +- Install with [npm](https://www.npmjs.com/): `npm install bootstrap@v5.3.3` +- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@v5.3.3` +- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:5.3.3` +- Install with [NuGet](https://www.nuget.org/): CSS: `Install-Package bootstrap` Sass: `Install-Package bootstrap.sass` + +Read the [Getting started page](https://getbootstrap.com/docs/5.3/getting-started/introduction/) for information on the framework contents, templates, examples, and more. + + +## Status + +[![Build Status](https://img.shields.io/github/actions/workflow/status/twbs/bootstrap/js.yml?branch=main&label=JS%20Tests&logo=github)](https://github.com/twbs/bootstrap/actions/workflows/js.yml?query=workflow%3AJS+branch%3Amain) +[![npm version](https://img.shields.io/npm/v/bootstrap?logo=npm&logoColor=fff)](https://www.npmjs.com/package/bootstrap) +[![Gem version](https://img.shields.io/gem/v/bootstrap?logo=rubygems&logoColor=fff)](https://rubygems.org/gems/bootstrap) +[![Meteor Atmosphere](https://img.shields.io/badge/meteor-twbs%3Abootstrap-blue?logo=meteor&logoColor=fff)](https://atmospherejs.com/twbs/bootstrap) +[![Packagist Prerelease](https://img.shields.io/packagist/vpre/twbs/bootstrap?logo=packagist&logoColor=fff)](https://packagist.org/packages/twbs/bootstrap) +[![NuGet](https://img.shields.io/nuget/vpre/bootstrap?logo=nuget&logoColor=fff)](https://www.nuget.org/packages/bootstrap/absoluteLatest) +[![Coverage Status](https://img.shields.io/coveralls/github/twbs/bootstrap/main?logo=coveralls&logoColor=fff)](https://coveralls.io/github/twbs/bootstrap?branch=main) +[![CSS gzip size](https://img.badgesize.io/twbs/bootstrap/main/dist/css/bootstrap.min.css?compression=gzip&label=CSS%20gzip%20size)](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css) +[![CSS Brotli size](https://img.badgesize.io/twbs/bootstrap/main/dist/css/bootstrap.min.css?compression=brotli&label=CSS%20Brotli%20size)](https://github.com/twbs/bootstrap/blob/main/dist/css/bootstrap.min.css) +[![JS gzip size](https://img.badgesize.io/twbs/bootstrap/main/dist/js/bootstrap.min.js?compression=gzip&label=JS%20gzip%20size)](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js) +[![JS Brotli size](https://img.badgesize.io/twbs/bootstrap/main/dist/js/bootstrap.min.js?compression=brotli&label=JS%20Brotli%20size)](https://github.com/twbs/bootstrap/blob/main/dist/js/bootstrap.min.js) +[![Backers on Open Collective](https://img.shields.io/opencollective/backers/bootstrap?logo=opencollective&logoColor=fff)](#backers) +[![Sponsors on Open Collective](https://img.shields.io/opencollective/sponsors/bootstrap?logo=opencollective&logoColor=fff)](#sponsors) + + +## What's included + +Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. + +
+ Download contents + + ```text + bootstrap/ + ├── css/ + │ ├── bootstrap-grid.css + │ ├── bootstrap-grid.css.map + │ ├── bootstrap-grid.min.css + │ ├── bootstrap-grid.min.css.map + │ ├── bootstrap-grid.rtl.css + │ ├── bootstrap-grid.rtl.css.map + │ ├── bootstrap-grid.rtl.min.css + │ ├── bootstrap-grid.rtl.min.css.map + │ ├── bootstrap-reboot.css + │ ├── bootstrap-reboot.css.map + │ ├── bootstrap-reboot.min.css + │ ├── bootstrap-reboot.min.css.map + │ ├── bootstrap-reboot.rtl.css + │ ├── bootstrap-reboot.rtl.css.map + │ ├── bootstrap-reboot.rtl.min.css + │ ├── bootstrap-reboot.rtl.min.css.map + │ ├── bootstrap-utilities.css + │ ├── bootstrap-utilities.css.map + │ ├── bootstrap-utilities.min.css + │ ├── bootstrap-utilities.min.css.map + │ ├── bootstrap-utilities.rtl.css + │ ├── bootstrap-utilities.rtl.css.map + │ ├── bootstrap-utilities.rtl.min.css + │ ├── bootstrap-utilities.rtl.min.css.map + │ ├── bootstrap.css + │ ├── bootstrap.css.map + │ ├── bootstrap.min.css + │ ├── bootstrap.min.css.map + │ ├── bootstrap.rtl.css + │ ├── bootstrap.rtl.css.map + │ ├── bootstrap.rtl.min.css + │ └── bootstrap.rtl.min.css.map + └── js/ + ├── bootstrap.bundle.js + ├── bootstrap.bundle.js.map + ├── bootstrap.bundle.min.js + ├── bootstrap.bundle.min.js.map + ├── bootstrap.esm.js + ├── bootstrap.esm.js.map + ├── bootstrap.esm.min.js + ├── bootstrap.esm.min.js.map + ├── bootstrap.js + ├── bootstrap.js.map + ├── bootstrap.min.js + └── bootstrap.min.js.map + ``` +
+ +We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). [Source maps](https://developers.google.com/web/tools/chrome-devtools/javascript/source-maps) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Bundled JS files (`bootstrap.bundle.js` and minified `bootstrap.bundle.min.js`) include [Popper](https://popper.js.org/). + + +## Bugs and feature requests + +Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/main/.github/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new/choose). + + +## Documentation + +Bootstrap's documentation, included in this repo in the root directory, is built with [Hugo](https://gohugo.io/) and publicly hosted on GitHub Pages at . The docs may also be run locally. + +Documentation search is powered by [Algolia's DocSearch](https://docsearch.algolia.com/). + +### Running documentation locally + +1. Run `npm install` to install the Node.js dependencies, including Hugo (the site builder). +2. Run `npm run test` (or a specific npm script) to rebuild distributed CSS and JavaScript files, as well as our docs assets. +3. From the root `/bootstrap` directory, run `npm run docs-serve` in the command line. +4. Open `http://localhost:9001/` in your browser, and voilà. + +Learn more about using Hugo by reading its [documentation](https://gohugo.io/documentation/). + +### Documentation for previous releases + +You can find all our previous releases docs on . + +[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download. + + +## Contributing + +Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/main/.github/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. + +Moreover, if your pull request contains JavaScript patches or features, you must include [relevant unit tests](https://github.com/twbs/bootstrap/tree/main/js/tests). All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo). + +Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/main/.editorconfig) for easy use in common text editors. Read more and download plugins at . + + +## Community + +Get updates on Bootstrap's development and chat with the project maintainers and community members. + +- Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap). +- Read and subscribe to [The Official Bootstrap Blog](https://blog.getbootstrap.com/). +- Ask questions and explore [our GitHub Discussions](https://github.com/twbs/bootstrap/discussions). +- Discuss, ask questions, and more on [the community Discord](https://discord.gg/bZUvakRU3M) or [Bootstrap subreddit](https://reddit.com/r/bootstrap). +- Chat with fellow Bootstrappers in IRC. On the `irc.libera.chat` server, in the `#bootstrap` channel. +- Implementation help may be found at Stack Overflow (tagged [`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5)). +- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability. + + +## Versioning + +For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](https://semver.org/). Sometimes we screw up, but we adhere to those rules whenever possible. + +See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap. Release announcement posts on [the official Bootstrap blog](https://blog.getbootstrap.com/) contain summaries of the most noteworthy changes made in each release. + + +## Creators + +**Mark Otto** + +- +- + +**Jacob Thornton** + +- +- + + +## Thanks + + + BrowserStack + + +Thanks to [BrowserStack](https://www.browserstack.com/) for providing the infrastructure that allows us to test in real browsers! + + + Netlify + + +Thanks to [Netlify](https://www.netlify.com/) for providing us with Deploy Previews! + + +## Sponsors + +Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/bootstrap#sponsor)] + +[![OC sponsor 0](https://opencollective.com/bootstrap/sponsor/0/avatar.svg)](https://opencollective.com/bootstrap/sponsor/0/website) +[![OC sponsor 1](https://opencollective.com/bootstrap/sponsor/1/avatar.svg)](https://opencollective.com/bootstrap/sponsor/1/website) +[![OC sponsor 2](https://opencollective.com/bootstrap/sponsor/2/avatar.svg)](https://opencollective.com/bootstrap/sponsor/2/website) +[![OC sponsor 3](https://opencollective.com/bootstrap/sponsor/3/avatar.svg)](https://opencollective.com/bootstrap/sponsor/3/website) +[![OC sponsor 4](https://opencollective.com/bootstrap/sponsor/4/avatar.svg)](https://opencollective.com/bootstrap/sponsor/4/website) +[![OC sponsor 5](https://opencollective.com/bootstrap/sponsor/5/avatar.svg)](https://opencollective.com/bootstrap/sponsor/5/website) +[![OC sponsor 6](https://opencollective.com/bootstrap/sponsor/6/avatar.svg)](https://opencollective.com/bootstrap/sponsor/6/website) +[![OC sponsor 7](https://opencollective.com/bootstrap/sponsor/7/avatar.svg)](https://opencollective.com/bootstrap/sponsor/7/website) +[![OC sponsor 8](https://opencollective.com/bootstrap/sponsor/8/avatar.svg)](https://opencollective.com/bootstrap/sponsor/8/website) +[![OC sponsor 9](https://opencollective.com/bootstrap/sponsor/9/avatar.svg)](https://opencollective.com/bootstrap/sponsor/9/website) + + +## Backers + +Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/bootstrap#backer)] + +[![Backers](https://opencollective.com/bootstrap/backers.svg?width=890)](https://opencollective.com/bootstrap#backers) + + +## Copyright and license + +Code and documentation copyright 2011–2024 the [Bootstrap Authors](https://github.com/twbs/bootstrap/graphs/contributors). Code released under the [MIT License](https://github.com/twbs/bootstrap/blob/main/LICENSE). Docs released under [Creative Commons](https://creativecommons.org/licenses/by/3.0/). diff --git a/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js new file mode 100644 index 000000000..6294dff3d --- /dev/null +++ b/src/dashboard/Synapse.Dashboard/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js @@ -0,0 +1,6314 @@ +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory()); +})(this, (function () { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/data.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + /** + * Constants + */ + + const elementMap = new Map(); + const Data = { + set(element, key, instance) { + if (!elementMap.has(element)) { + elementMap.set(element, new Map()); + } + const instanceMap = elementMap.get(element); + + // make it clear we only want one instance per element + // can be removed later when multiple key/instances are fine to be used + if (!instanceMap.has(key) && instanceMap.size !== 0) { + // eslint-disable-next-line no-console + console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); + return; + } + instanceMap.set(key, instance); + }, + get(element, key) { + if (elementMap.has(element)) { + return elementMap.get(element).get(key) || null; + } + return null; + }, + remove(element, key) { + if (!elementMap.has(element)) { + return; + } + const instanceMap = elementMap.get(element); + instanceMap.delete(key); + + // free up element references if there are no instances left for an element + if (instanceMap.size === 0) { + elementMap.delete(element); + } + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/index.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const MAX_UID = 1000000; + const MILLISECONDS_MULTIPLIER = 1000; + const TRANSITION_END = 'transitionend'; + + /** + * Properly escape IDs selectors to handle weird IDs + * @param {string} selector + * @returns {string} + */ + const parseSelector = selector => { + if (selector && window.CSS && window.CSS.escape) { + // document.querySelector needs escaping to handle IDs (html5+) containing for instance / + selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); + } + return selector; + }; + + // Shout-out Angus Croll (https://goo.gl/pxwQGp) + const toType = object => { + if (object === null || object === undefined) { + return `${object}`; + } + return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); + }; + + /** + * Public Util API + */ + + const getUID = prefix => { + do { + prefix += Math.floor(Math.random() * MAX_UID); + } while (document.getElementById(prefix)); + return prefix; + }; + const getTransitionDurationFromElement = element => { + if (!element) { + return 0; + } + + // Get transition-duration of the element + let { + transitionDuration, + transitionDelay + } = window.getComputedStyle(element); + const floatTransitionDuration = Number.parseFloat(transitionDuration); + const floatTransitionDelay = Number.parseFloat(transitionDelay); + + // Return 0 if element or transition duration is not found + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0; + } + + // If multiple durations are defined, take the first + transitionDuration = transitionDuration.split(',')[0]; + transitionDelay = transitionDelay.split(',')[0]; + return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; + }; + const triggerTransitionEnd = element => { + element.dispatchEvent(new Event(TRANSITION_END)); + }; + const isElement$1 = object => { + if (!object || typeof object !== 'object') { + return false; + } + if (typeof object.jquery !== 'undefined') { + object = object[0]; + } + return typeof object.nodeType !== 'undefined'; + }; + const getElement = object => { + // it's a jQuery object or a node element + if (isElement$1(object)) { + return object.jquery ? object[0] : object; + } + if (typeof object === 'string' && object.length > 0) { + return document.querySelector(parseSelector(object)); + } + return null; + }; + const isVisible = element => { + if (!isElement$1(element) || element.getClientRects().length === 0) { + return false; + } + const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; + // Handle `details` element as its content may falsie appear visible when it is closed + const closedDetails = element.closest('details:not([open])'); + if (!closedDetails) { + return elementIsVisible; + } + if (closedDetails !== element) { + const summary = element.closest('summary'); + if (summary && summary.parentNode !== closedDetails) { + return false; + } + if (summary === null) { + return false; + } + } + return elementIsVisible; + }; + const isDisabled = element => { + if (!element || element.nodeType !== Node.ELEMENT_NODE) { + return true; + } + if (element.classList.contains('disabled')) { + return true; + } + if (typeof element.disabled !== 'undefined') { + return element.disabled; + } + return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; + }; + const findShadowRoot = element => { + if (!document.documentElement.attachShadow) { + return null; + } + + // Can find the shadow root otherwise it'll return the document + if (typeof element.getRootNode === 'function') { + const root = element.getRootNode(); + return root instanceof ShadowRoot ? root : null; + } + if (element instanceof ShadowRoot) { + return element; + } + + // when we don't find a shadow root + if (!element.parentNode) { + return null; + } + return findShadowRoot(element.parentNode); + }; + const noop = () => {}; + + /** + * Trick to restart an element's animation + * + * @param {HTMLElement} element + * @return void + * + * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation + */ + const reflow = element => { + element.offsetHeight; // eslint-disable-line no-unused-expressions + }; + const getjQuery = () => { + if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { + return window.jQuery; + } + return null; + }; + const DOMContentLoadedCallbacks = []; + const onDOMContentLoaded = callback => { + if (document.readyState === 'loading') { + // add listener on the first call when the document is in loading state + if (!DOMContentLoadedCallbacks.length) { + document.addEventListener('DOMContentLoaded', () => { + for (const callback of DOMContentLoadedCallbacks) { + callback(); + } + }); + } + DOMContentLoadedCallbacks.push(callback); + } else { + callback(); + } + }; + const isRTL = () => document.documentElement.dir === 'rtl'; + const defineJQueryPlugin = plugin => { + onDOMContentLoaded(() => { + const $ = getjQuery(); + /* istanbul ignore if */ + if ($) { + const name = plugin.NAME; + const JQUERY_NO_CONFLICT = $.fn[name]; + $.fn[name] = plugin.jQueryInterface; + $.fn[name].Constructor = plugin; + $.fn[name].noConflict = () => { + $.fn[name] = JQUERY_NO_CONFLICT; + return plugin.jQueryInterface; + }; + } + }); + }; + const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { + return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue; + }; + const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { + if (!waitForTransition) { + execute(callback); + return; + } + const durationPadding = 5; + const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; + let called = false; + const handler = ({ + target + }) => { + if (target !== transitionElement) { + return; + } + called = true; + transitionElement.removeEventListener(TRANSITION_END, handler); + execute(callback); + }; + transitionElement.addEventListener(TRANSITION_END, handler); + setTimeout(() => { + if (!called) { + triggerTransitionEnd(transitionElement); + } + }, emulatedDuration); + }; + + /** + * Return the previous/next element of a list. + * + * @param {array} list The list of elements + * @param activeElement The active element + * @param shouldGetNext Choose to get next or previous element + * @param isCycleAllowed + * @return {Element|elem} The proper element + */ + const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { + const listLength = list.length; + let index = list.indexOf(activeElement); + + // if the element does not exist in the list return an element + // depending on the direction and if cycle is allowed + if (index === -1) { + return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; + } + index += shouldGetNext ? 1 : -1; + if (isCycleAllowed) { + index = (index + listLength) % listLength; + } + return list[Math.max(0, Math.min(index, listLength - 1))]; + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/event-handler.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const namespaceRegex = /[^.]*(?=\..*)\.|.*/; + const stripNameRegex = /\..*/; + const stripUidRegex = /::\d+$/; + const eventRegistry = {}; // Events storage + let uidEvent = 1; + const customEvents = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); + + /** + * Private methods + */ + + function makeEventUid(element, uid) { + return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; + } + function getElementEvents(element) { + const uid = makeEventUid(element); + element.uidEvent = uid; + eventRegistry[uid] = eventRegistry[uid] || {}; + return eventRegistry[uid]; + } + function bootstrapHandler(element, fn) { + return function handler(event) { + hydrateObj(event, { + delegateTarget: element + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, fn); + } + return fn.apply(element, [event]); + }; + } + function bootstrapDelegationHandler(element, selector, fn) { + return function handler(event) { + const domElements = element.querySelectorAll(selector); + for (let { + target + } = event; target && target !== this; target = target.parentNode) { + for (const domElement of domElements) { + if (domElement !== target) { + continue; + } + hydrateObj(event, { + delegateTarget: target + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, selector, fn); + } + return fn.apply(target, [event]); + } + } + }; + } + function findHandler(events, callable, delegationSelector = null) { + return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); + } + function normalizeParameters(originalTypeEvent, handler, delegationFunction) { + const isDelegated = typeof handler === 'string'; + // TODO: tooltip passes `false` instead of selector, so we need to check + const callable = isDelegated ? delegationFunction : handler || delegationFunction; + let typeEvent = getTypeEvent(originalTypeEvent); + if (!nativeEvents.has(typeEvent)) { + typeEvent = originalTypeEvent; + } + return [isDelegated, callable, typeEvent]; + } + function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + + // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position + // this prevents the handler from being dispatched the same way as mouseover or mouseout does + if (originalTypeEvent in customEvents) { + const wrapFunction = fn => { + return function (event) { + if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { + return fn.call(this, event); + } + }; + }; + callable = wrapFunction(callable); + } + const events = getElementEvents(element); + const handlers = events[typeEvent] || (events[typeEvent] = {}); + const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); + if (previousFunction) { + previousFunction.oneOff = previousFunction.oneOff && oneOff; + return; + } + const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); + const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); + fn.delegationSelector = isDelegated ? handler : null; + fn.callable = callable; + fn.oneOff = oneOff; + fn.uidEvent = uid; + handlers[uid] = fn; + element.addEventListener(typeEvent, fn, isDelegated); + } + function removeHandler(element, events, typeEvent, handler, delegationSelector) { + const fn = findHandler(events[typeEvent], handler, delegationSelector); + if (!fn) { + return; + } + element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); + delete events[typeEvent][fn.uidEvent]; + } + function removeNamespacedHandlers(element, events, typeEvent, namespace) { + const storeElementEvent = events[typeEvent] || {}; + for (const [handlerKey, event] of Object.entries(storeElementEvent)) { + if (handlerKey.includes(namespace)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + } + function getTypeEvent(event) { + // allow to get the native events from namespaced events ('click.bs.button' --> 'click') + event = event.replace(stripNameRegex, ''); + return customEvents[event] || event; + } + const EventHandler = { + on(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, false); + }, + one(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, true); + }, + off(element, originalTypeEvent, handler, delegationFunction) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + const inNamespace = typeEvent !== originalTypeEvent; + const events = getElementEvents(element); + const storeElementEvent = events[typeEvent] || {}; + const isNamespace = originalTypeEvent.startsWith('.'); + if (typeof callable !== 'undefined') { + // Simplest case: handler is passed, remove that listener ONLY. + if (!Object.keys(storeElementEvent).length) { + return; + } + removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); + return; + } + if (isNamespace) { + for (const elementEvent of Object.keys(events)) { + removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); + } + } + for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { + const handlerKey = keyHandlers.replace(stripUidRegex, ''); + if (!inNamespace || originalTypeEvent.includes(handlerKey)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + }, + trigger(element, event, args) { + if (typeof event !== 'string' || !element) { + return null; + } + const $ = getjQuery(); + const typeEvent = getTypeEvent(event); + const inNamespace = event !== typeEvent; + let jQueryEvent = null; + let bubbles = true; + let nativeDispatch = true; + let defaultPrevented = false; + if (inNamespace && $) { + jQueryEvent = $.Event(event, args); + $(element).trigger(jQueryEvent); + bubbles = !jQueryEvent.isPropagationStopped(); + nativeDispatch = !jQueryEvent.isImmediatePropagationStopped(); + defaultPrevented = jQueryEvent.isDefaultPrevented(); + } + const evt = hydrateObj(new Event(event, { + bubbles, + cancelable: true + }), args); + if (defaultPrevented) { + evt.preventDefault(); + } + if (nativeDispatch) { + element.dispatchEvent(evt); + } + if (evt.defaultPrevented && jQueryEvent) { + jQueryEvent.preventDefault(); + } + return evt; + } + }; + function hydrateObj(obj, meta = {}) { + for (const [key, value] of Object.entries(meta)) { + try { + obj[key] = value; + } catch (_unused) { + Object.defineProperty(obj, key, { + configurable: true, + get() { + return value; + } + }); + } + } + return obj; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/manipulator.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + function normalizeData(value) { + if (value === 'true') { + return true; + } + if (value === 'false') { + return false; + } + if (value === Number(value).toString()) { + return Number(value); + } + if (value === '' || value === 'null') { + return null; + } + if (typeof value !== 'string') { + return value; + } + try { + return JSON.parse(decodeURIComponent(value)); + } catch (_unused) { + return value; + } + } + function normalizeDataKey(key) { + return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); + } + const Manipulator = { + setDataAttribute(element, key, value) { + element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); + }, + removeDataAttribute(element, key) { + element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); + }, + getDataAttributes(element) { + if (!element) { + return {}; + } + const attributes = {}; + const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig')); + for (const key of bsKeys) { + let pureKey = key.replace(/^bs/, ''); + pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length); + attributes[pureKey] = normalizeData(element.dataset[key]); + } + return attributes; + }, + getDataAttribute(element, key) { + return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/config.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Class definition + */ + + class Config { + // Getters + static get Default() { + return {}; + } + static get DefaultType() { + return {}; + } + static get NAME() { + throw new Error('You have to implement the static method "NAME", for each component!'); + } + _getConfig(config) { + config = this._mergeConfigObj(config); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + _configAfterMerge(config) { + return config; + } + _mergeConfigObj(config, element) { + const jsonConfig = isElement$1(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse + + return { + ...this.constructor.Default, + ...(typeof jsonConfig === 'object' ? jsonConfig : {}), + ...(isElement$1(element) ? Manipulator.getDataAttributes(element) : {}), + ...(typeof config === 'object' ? config : {}) + }; + } + _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { + for (const [property, expectedTypes] of Object.entries(configTypes)) { + const value = config[property]; + const valueType = isElement$1(value) ? 'element' : toType(value); + if (!new RegExp(expectedTypes).test(valueType)) { + throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); + } + } + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap base-component.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const VERSION = '5.3.3'; + + /** + * Class definition + */ + + class BaseComponent extends Config { + constructor(element, config) { + super(); + element = getElement(element); + if (!element) { + return; + } + this._element = element; + this._config = this._getConfig(config); + Data.set(this._element, this.constructor.DATA_KEY, this); + } + + // Public + dispose() { + Data.remove(this._element, this.constructor.DATA_KEY); + EventHandler.off(this._element, this.constructor.EVENT_KEY); + for (const propertyName of Object.getOwnPropertyNames(this)) { + this[propertyName] = null; + } + } + _queueCallback(callback, element, isAnimated = true) { + executeAfterTransition(callback, element, isAnimated); + } + _getConfig(config) { + config = this._mergeConfigObj(config, this._element); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + + // Static + static getInstance(element) { + return Data.get(getElement(element), this.DATA_KEY); + } + static getOrCreateInstance(element, config = {}) { + return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); + } + static get VERSION() { + return VERSION; + } + static get DATA_KEY() { + return `bs.${this.NAME}`; + } + static get EVENT_KEY() { + return `.${this.DATA_KEY}`; + } + static eventName(name) { + return `${name}${this.EVENT_KEY}`; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/selector-engine.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const getSelector = element => { + let selector = element.getAttribute('data-bs-target'); + if (!selector || selector === '#') { + let hrefAttribute = element.getAttribute('href'); + + // The only valid content that could double as a selector are IDs or classes, + // so everything starting with `#` or `.`. If a "real" URL is used as the selector, + // `document.querySelector` will rightfully complain it is invalid. + // See https://github.com/twbs/bootstrap/issues/32273 + if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { + return null; + } + + // Just in case some CMS puts out a full URL with the anchor appended + if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { + hrefAttribute = `#${hrefAttribute.split('#')[1]}`; + } + selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; + } + return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null; + }; + const SelectorEngine = { + find(selector, element = document.documentElement) { + return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); + }, + findOne(selector, element = document.documentElement) { + return Element.prototype.querySelector.call(element, selector); + }, + children(element, selector) { + return [].concat(...element.children).filter(child => child.matches(selector)); + }, + parents(element, selector) { + const parents = []; + let ancestor = element.parentNode.closest(selector); + while (ancestor) { + parents.push(ancestor); + ancestor = ancestor.parentNode.closest(selector); + } + return parents; + }, + prev(element, selector) { + let previous = element.previousElementSibling; + while (previous) { + if (previous.matches(selector)) { + return [previous]; + } + previous = previous.previousElementSibling; + } + return []; + }, + // TODO: this is now unused; remove later along with prev() + next(element, selector) { + let next = element.nextElementSibling; + while (next) { + if (next.matches(selector)) { + return [next]; + } + next = next.nextElementSibling; + } + return []; + }, + focusableChildren(element) { + const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); + return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el)); + }, + getSelectorFromElement(element) { + const selector = getSelector(element); + if (selector) { + return SelectorEngine.findOne(selector) ? selector : null; + } + return null; + }, + getElementFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.findOne(selector) : null; + }, + getMultipleElementsFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.find(selector) : []; + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/component-functions.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const enableDismissTrigger = (component, method = 'hide') => { + const clickEvent = `click.dismiss${component.EVENT_KEY}`; + const name = component.NAME; + EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + if (isDisabled(this)) { + return; + } + const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`); + const instance = component.getOrCreateInstance(target); + + // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method + instance[method](); + }); + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap alert.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$f = 'alert'; + const DATA_KEY$a = 'bs.alert'; + const EVENT_KEY$b = `.${DATA_KEY$a}`; + const EVENT_CLOSE = `close${EVENT_KEY$b}`; + const EVENT_CLOSED = `closed${EVENT_KEY$b}`; + const CLASS_NAME_FADE$5 = 'fade'; + const CLASS_NAME_SHOW$8 = 'show'; + + /** + * Class definition + */ + + class Alert extends BaseComponent { + // Getters + static get NAME() { + return NAME$f; + } + + // Public + close() { + const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE); + if (closeEvent.defaultPrevented) { + return; + } + this._element.classList.remove(CLASS_NAME_SHOW$8); + const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5); + this._queueCallback(() => this._destroyElement(), this._element, isAnimated); + } + + // Private + _destroyElement() { + this._element.remove(); + EventHandler.trigger(this._element, EVENT_CLOSED); + this.dispose(); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Alert.getOrCreateInstance(this); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](this); + }); + } + } + + /** + * Data API implementation + */ + + enableDismissTrigger(Alert, 'close'); + + /** + * jQuery + */ + + defineJQueryPlugin(Alert); + + /** + * -------------------------------------------------------------------------- + * Bootstrap button.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$e = 'button'; + const DATA_KEY$9 = 'bs.button'; + const EVENT_KEY$a = `.${DATA_KEY$9}`; + const DATA_API_KEY$6 = '.data-api'; + const CLASS_NAME_ACTIVE$3 = 'active'; + const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle="button"]'; + const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`; + + /** + * Class definition + */ + + class Button extends BaseComponent { + // Getters + static get NAME() { + return NAME$e; + } + + // Public + toggle() { + // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method + this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3)); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Button.getOrCreateInstance(this); + if (config === 'toggle') { + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => { + event.preventDefault(); + const button = event.target.closest(SELECTOR_DATA_TOGGLE$5); + const data = Button.getOrCreateInstance(button); + data.toggle(); + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Button); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/swipe.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$d = 'swipe'; + const EVENT_KEY$9 = '.bs.swipe'; + const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`; + const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`; + const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`; + const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`; + const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`; + const POINTER_TYPE_TOUCH = 'touch'; + const POINTER_TYPE_PEN = 'pen'; + const CLASS_NAME_POINTER_EVENT = 'pointer-event'; + const SWIPE_THRESHOLD = 40; + const Default$c = { + endCallback: null, + leftCallback: null, + rightCallback: null + }; + const DefaultType$c = { + endCallback: '(function|null)', + leftCallback: '(function|null)', + rightCallback: '(function|null)' + }; + + /** + * Class definition + */ + + class Swipe extends Config { + constructor(element, config) { + super(); + this._element = element; + if (!element || !Swipe.isSupported()) { + return; + } + this._config = this._getConfig(config); + this._deltaX = 0; + this._supportPointerEvents = Boolean(window.PointerEvent); + this._initEvents(); + } + + // Getters + static get Default() { + return Default$c; + } + static get DefaultType() { + return DefaultType$c; + } + static get NAME() { + return NAME$d; + } + + // Public + dispose() { + EventHandler.off(this._element, EVENT_KEY$9); + } + + // Private + _start(event) { + if (!this._supportPointerEvents) { + this._deltaX = event.touches[0].clientX; + return; + } + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX; + } + } + _end(event) { + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX - this._deltaX; + } + this._handleSwipe(); + execute(this._config.endCallback); + } + _move(event) { + this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX; + } + _handleSwipe() { + const absDeltaX = Math.abs(this._deltaX); + if (absDeltaX <= SWIPE_THRESHOLD) { + return; + } + const direction = absDeltaX / this._deltaX; + this._deltaX = 0; + if (!direction) { + return; + } + execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback); + } + _initEvents() { + if (this._supportPointerEvents) { + EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event)); + EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event)); + this._element.classList.add(CLASS_NAME_POINTER_EVENT); + } else { + EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event)); + EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event)); + EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event)); + } + } + _eventIsPointerPenTouch(event) { + return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH); + } + + // Static + static isSupported() { + return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap carousel.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$c = 'carousel'; + const DATA_KEY$8 = 'bs.carousel'; + const EVENT_KEY$8 = `.${DATA_KEY$8}`; + const DATA_API_KEY$5 = '.data-api'; + const ARROW_LEFT_KEY$1 = 'ArrowLeft'; + const ARROW_RIGHT_KEY$1 = 'ArrowRight'; + const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch + + const ORDER_NEXT = 'next'; + const ORDER_PREV = 'prev'; + const DIRECTION_LEFT = 'left'; + const DIRECTION_RIGHT = 'right'; + const EVENT_SLIDE = `slide${EVENT_KEY$8}`; + const EVENT_SLID = `slid${EVENT_KEY$8}`; + const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`; + const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`; + const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`; + const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`; + const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`; + const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`; + const CLASS_NAME_CAROUSEL = 'carousel'; + const CLASS_NAME_ACTIVE$2 = 'active'; + const CLASS_NAME_SLIDE = 'slide'; + const CLASS_NAME_END = 'carousel-item-end'; + const CLASS_NAME_START = 'carousel-item-start'; + const CLASS_NAME_NEXT = 'carousel-item-next'; + const CLASS_NAME_PREV = 'carousel-item-prev'; + const SELECTOR_ACTIVE = '.active'; + const SELECTOR_ITEM = '.carousel-item'; + const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM; + const SELECTOR_ITEM_IMG = '.carousel-item img'; + const SELECTOR_INDICATORS = '.carousel-indicators'; + const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'; + const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'; + const KEY_TO_DIRECTION = { + [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT, + [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT + }; + const Default$b = { + interval: 5000, + keyboard: true, + pause: 'hover', + ride: false, + touch: true, + wrap: true + }; + const DefaultType$b = { + interval: '(number|boolean)', + // TODO:v6 remove boolean support + keyboard: 'boolean', + pause: '(string|boolean)', + ride: '(boolean|string)', + touch: 'boolean', + wrap: 'boolean' + }; + + /** + * Class definition + */ + + class Carousel extends BaseComponent { + constructor(element, config) { + super(element, config); + this._interval = null; + this._activeElement = null; + this._isSliding = false; + this.touchTimeout = null; + this._swipeHelper = null; + this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element); + this._addEventListeners(); + if (this._config.ride === CLASS_NAME_CAROUSEL) { + this.cycle(); + } + } + + // Getters + static get Default() { + return Default$b; + } + static get DefaultType() { + return DefaultType$b; + } + static get NAME() { + return NAME$c; + } + + // Public + next() { + this._slide(ORDER_NEXT); + } + nextWhenVisible() { + // FIXME TODO use `document.visibilityState` + // Don't call next when the page isn't visible + // or the carousel or its parent isn't visible + if (!document.hidden && isVisible(this._element)) { + this.next(); + } + } + prev() { + this._slide(ORDER_PREV); + } + pause() { + if (this._isSliding) { + triggerTransitionEnd(this._element); + } + this._clearInterval(); + } + cycle() { + this._clearInterval(); + this._updateInterval(); + this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval); + } + _maybeEnableCycle() { + if (!this._config.ride) { + return; + } + if (this._isSliding) { + EventHandler.one(this._element, EVENT_SLID, () => this.cycle()); + return; + } + this.cycle(); + } + to(index) { + const items = this._getItems(); + if (index > items.length - 1 || index < 0) { + return; + } + if (this._isSliding) { + EventHandler.one(this._element, EVENT_SLID, () => this.to(index)); + return; + } + const activeIndex = this._getItemIndex(this._getActive()); + if (activeIndex === index) { + return; + } + const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV; + this._slide(order, items[index]); + } + dispose() { + if (this._swipeHelper) { + this._swipeHelper.dispose(); + } + super.dispose(); + } + + // Private + _configAfterMerge(config) { + config.defaultInterval = config.interval; + return config; + } + _addEventListeners() { + if (this._config.keyboard) { + EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event)); + } + if (this._config.pause === 'hover') { + EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause()); + EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle()); + } + if (this._config.touch && Swipe.isSupported()) { + this._addTouchEventListeners(); + } + } + _addTouchEventListeners() { + for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { + EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault()); + } + const endCallBack = () => { + if (this._config.pause !== 'hover') { + return; + } + + // If it's a touch-enabled device, mouseenter/leave are fired as + // part of the mouse compatibility events on first tap - the carousel + // would stop cycling until user tapped out of it; + // here, we listen for touchend, explicitly pause the carousel + // (as if it's the second time we tap on it, mouseenter compat event + // is NOT fired) and after a timeout (to allow for mouse compatibility + // events to fire) we explicitly restart cycling + + this.pause(); + if (this.touchTimeout) { + clearTimeout(this.touchTimeout); + } + this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval); + }; + const swipeConfig = { + leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)), + rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)), + endCallback: endCallBack + }; + this._swipeHelper = new Swipe(this._element, swipeConfig); + } + _keydown(event) { + if (/input|textarea/i.test(event.target.tagName)) { + return; + } + const direction = KEY_TO_DIRECTION[event.key]; + if (direction) { + event.preventDefault(); + this._slide(this._directionToOrder(direction)); + } + } + _getItemIndex(element) { + return this._getItems().indexOf(element); + } + _setActiveIndicatorElement(index) { + if (!this._indicatorsElement) { + return; + } + const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement); + activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2); + activeIndicator.removeAttribute('aria-current'); + const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement); + if (newActiveIndicator) { + newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2); + newActiveIndicator.setAttribute('aria-current', 'true'); + } + } + _updateInterval() { + const element = this._activeElement || this._getActive(); + if (!element) { + return; + } + const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10); + this._config.interval = elementInterval || this._config.defaultInterval; + } + _slide(order, element = null) { + if (this._isSliding) { + return; + } + const activeElement = this._getActive(); + const isNext = order === ORDER_NEXT; + const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap); + if (nextElement === activeElement) { + return; + } + const nextElementIndex = this._getItemIndex(nextElement); + const triggerEvent = eventName => { + return EventHandler.trigger(this._element, eventName, { + relatedTarget: nextElement, + direction: this._orderToDirection(order), + from: this._getItemIndex(activeElement), + to: nextElementIndex + }); + }; + const slideEvent = triggerEvent(EVENT_SLIDE); + if (slideEvent.defaultPrevented) { + return; + } + if (!activeElement || !nextElement) { + // Some weirdness is happening, so we bail + // TODO: change tests that use empty divs to avoid this check + return; + } + const isCycling = Boolean(this._interval); + this.pause(); + this._isSliding = true; + this._setActiveIndicatorElement(nextElementIndex); + this._activeElement = nextElement; + const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END; + const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV; + nextElement.classList.add(orderClassName); + reflow(nextElement); + activeElement.classList.add(directionalClassName); + nextElement.classList.add(directionalClassName); + const completeCallBack = () => { + nextElement.classList.remove(directionalClassName, orderClassName); + nextElement.classList.add(CLASS_NAME_ACTIVE$2); + activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName); + this._isSliding = false; + triggerEvent(EVENT_SLID); + }; + this._queueCallback(completeCallBack, activeElement, this._isAnimated()); + if (isCycling) { + this.cycle(); + } + } + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_SLIDE); + } + _getActive() { + return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element); + } + _getItems() { + return SelectorEngine.find(SELECTOR_ITEM, this._element); + } + _clearInterval() { + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + } + _directionToOrder(direction) { + if (isRTL()) { + return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT; + } + return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV; + } + _orderToDirection(order) { + if (isRTL()) { + return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT; + } + return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT; + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Carousel.getOrCreateInstance(this, config); + if (typeof config === 'number') { + data.to(config); + return; + } + if (typeof config === 'string') { + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { + return; + } + event.preventDefault(); + const carousel = Carousel.getOrCreateInstance(target); + const slideIndex = this.getAttribute('data-bs-slide-to'); + if (slideIndex) { + carousel.to(slideIndex); + carousel._maybeEnableCycle(); + return; + } + if (Manipulator.getDataAttribute(this, 'slide') === 'next') { + carousel.next(); + carousel._maybeEnableCycle(); + return; + } + carousel.prev(); + carousel._maybeEnableCycle(); + }); + EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => { + const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE); + for (const carousel of carousels) { + Carousel.getOrCreateInstance(carousel); + } + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Carousel); + + /** + * -------------------------------------------------------------------------- + * Bootstrap collapse.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$b = 'collapse'; + const DATA_KEY$7 = 'bs.collapse'; + const EVENT_KEY$7 = `.${DATA_KEY$7}`; + const DATA_API_KEY$4 = '.data-api'; + const EVENT_SHOW$6 = `show${EVENT_KEY$7}`; + const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`; + const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`; + const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`; + const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`; + const CLASS_NAME_SHOW$7 = 'show'; + const CLASS_NAME_COLLAPSE = 'collapse'; + const CLASS_NAME_COLLAPSING = 'collapsing'; + const CLASS_NAME_COLLAPSED = 'collapsed'; + const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`; + const CLASS_NAME_HORIZONTAL = 'collapse-horizontal'; + const WIDTH = 'width'; + const HEIGHT = 'height'; + const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'; + const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle="collapse"]'; + const Default$a = { + parent: null, + toggle: true + }; + const DefaultType$a = { + parent: '(null|element)', + toggle: 'boolean' + }; + + /** + * Class definition + */ + + class Collapse extends BaseComponent { + constructor(element, config) { + super(element, config); + this._isTransitioning = false; + this._triggerArray = []; + const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4); + for (const elem of toggleList) { + const selector = SelectorEngine.getSelectorFromElement(elem); + const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element); + if (selector !== null && filterElement.length) { + this._triggerArray.push(elem); + } + } + this._initializeChildren(); + if (!this._config.parent) { + this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()); + } + if (this._config.toggle) { + this.toggle(); + } + } + + // Getters + static get Default() { + return Default$a; + } + static get DefaultType() { + return DefaultType$a; + } + static get NAME() { + return NAME$b; + } + + // Public + toggle() { + if (this._isShown()) { + this.hide(); + } else { + this.show(); + } + } + show() { + if (this._isTransitioning || this._isShown()) { + return; + } + let activeChildren = []; + + // find active children + if (this._config.parent) { + activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, { + toggle: false + })); + } + if (activeChildren.length && activeChildren[0]._isTransitioning) { + return; + } + const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6); + if (startEvent.defaultPrevented) { + return; + } + for (const activeInstance of activeChildren) { + activeInstance.hide(); + } + const dimension = this._getDimension(); + this._element.classList.remove(CLASS_NAME_COLLAPSE); + this._element.classList.add(CLASS_NAME_COLLAPSING); + this._element.style[dimension] = 0; + this._addAriaAndCollapsedClass(this._triggerArray, true); + this._isTransitioning = true; + const complete = () => { + this._isTransitioning = false; + this._element.classList.remove(CLASS_NAME_COLLAPSING); + this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + this._element.style[dimension] = ''; + EventHandler.trigger(this._element, EVENT_SHOWN$6); + }; + const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); + const scrollSize = `scroll${capitalizedDimension}`; + this._queueCallback(complete, this._element, true); + this._element.style[dimension] = `${this._element[scrollSize]}px`; + } + hide() { + if (this._isTransitioning || !this._isShown()) { + return; + } + const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6); + if (startEvent.defaultPrevented) { + return; + } + const dimension = this._getDimension(); + this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`; + reflow(this._element); + this._element.classList.add(CLASS_NAME_COLLAPSING); + this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + for (const trigger of this._triggerArray) { + const element = SelectorEngine.getElementFromSelector(trigger); + if (element && !this._isShown(element)) { + this._addAriaAndCollapsedClass([trigger], false); + } + } + this._isTransitioning = true; + const complete = () => { + this._isTransitioning = false; + this._element.classList.remove(CLASS_NAME_COLLAPSING); + this._element.classList.add(CLASS_NAME_COLLAPSE); + EventHandler.trigger(this._element, EVENT_HIDDEN$6); + }; + this._element.style[dimension] = ''; + this._queueCallback(complete, this._element, true); + } + _isShown(element = this._element) { + return element.classList.contains(CLASS_NAME_SHOW$7); + } + + // Private + _configAfterMerge(config) { + config.toggle = Boolean(config.toggle); // Coerce string values + config.parent = getElement(config.parent); + return config; + } + _getDimension() { + return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT; + } + _initializeChildren() { + if (!this._config.parent) { + return; + } + const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4); + for (const element of children) { + const selected = SelectorEngine.getElementFromSelector(element); + if (selected) { + this._addAriaAndCollapsedClass([element], this._isShown(selected)); + } + } + } + _getFirstLevelChildren(selector) { + const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); + // remove children if greater depth + return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element)); + } + _addAriaAndCollapsedClass(triggerArray, isOpen) { + if (!triggerArray.length) { + return; + } + for (const element of triggerArray) { + element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen); + element.setAttribute('aria-expanded', isOpen); + } + } + + // Static + static jQueryInterface(config) { + const _config = {}; + if (typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false; + } + return this.each(function () { + const data = Collapse.getOrCreateInstance(this, _config); + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) { + // preventDefault only for elements (which change the URL) not inside the collapsible element + if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') { + event.preventDefault(); + } + for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) { + Collapse.getOrCreateInstance(element, { + toggle: false + }).toggle(); + } + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Collapse); + + var top = 'top'; + var bottom = 'bottom'; + var right = 'right'; + var left = 'left'; + var auto = 'auto'; + var basePlacements = [top, bottom, right, left]; + var start = 'start'; + var end = 'end'; + var clippingParents = 'clippingParents'; + var viewport = 'viewport'; + var popper = 'popper'; + var reference = 'reference'; + var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) { + return acc.concat([placement + "-" + start, placement + "-" + end]); + }, []); + var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) { + return acc.concat([placement, placement + "-" + start, placement + "-" + end]); + }, []); // modifiers that need to read the DOM + + var beforeRead = 'beforeRead'; + var read = 'read'; + var afterRead = 'afterRead'; // pure-logic modifiers + + var beforeMain = 'beforeMain'; + var main = 'main'; + var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state) + + var beforeWrite = 'beforeWrite'; + var write = 'write'; + var afterWrite = 'afterWrite'; + var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite]; + + function getNodeName(element) { + return element ? (element.nodeName || '').toLowerCase() : null; + } + + function getWindow(node) { + if (node == null) { + return window; + } + + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + return ownerDocument ? ownerDocument.defaultView || window : window; + } + + return node; + } + + function isElement(node) { + var OwnElement = getWindow(node).Element; + return node instanceof OwnElement || node instanceof Element; + } + + function isHTMLElement(node) { + var OwnElement = getWindow(node).HTMLElement; + return node instanceof OwnElement || node instanceof HTMLElement; + } + + function isShadowRoot(node) { + // IE 11 has no ShadowRoot + if (typeof ShadowRoot === 'undefined') { + return false; + } + + var OwnElement = getWindow(node).ShadowRoot; + return node instanceof OwnElement || node instanceof ShadowRoot; + } + + // and applies them to the HTMLElements such as popper and arrow + + function applyStyles(_ref) { + var state = _ref.state; + Object.keys(state.elements).forEach(function (name) { + var style = state.styles[name] || {}; + var attributes = state.attributes[name] || {}; + var element = state.elements[name]; // arrow is optional + virtual elements + + if (!isHTMLElement(element) || !getNodeName(element)) { + return; + } // Flow doesn't support to extend this property, but it's the most + // effective way to apply styles to an HTMLElement + // $FlowFixMe[cannot-write] + + + Object.assign(element.style, style); + Object.keys(attributes).forEach(function (name) { + var value = attributes[name]; + + if (value === false) { + element.removeAttribute(name); + } else { + element.setAttribute(name, value === true ? '' : value); + } + }); + }); + } + + function effect$2(_ref2) { + var state = _ref2.state; + var initialStyles = { + popper: { + position: state.options.strategy, + left: '0', + top: '0', + margin: '0' + }, + arrow: { + position: 'absolute' + }, + reference: {} + }; + Object.assign(state.elements.popper.style, initialStyles.popper); + state.styles = initialStyles; + + if (state.elements.arrow) { + Object.assign(state.elements.arrow.style, initialStyles.arrow); + } + + return function () { + Object.keys(state.elements).forEach(function (name) { + var element = state.elements[name]; + var attributes = state.attributes[name] || {}; + var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them + + var style = styleProperties.reduce(function (style, property) { + style[property] = ''; + return style; + }, {}); // arrow is optional + virtual elements + + if (!isHTMLElement(element) || !getNodeName(element)) { + return; + } + + Object.assign(element.style, style); + Object.keys(attributes).forEach(function (attribute) { + element.removeAttribute(attribute); + }); + }); + }; + } // eslint-disable-next-line import/no-unused-modules + + + const applyStyles$1 = { + name: 'applyStyles', + enabled: true, + phase: 'write', + fn: applyStyles, + effect: effect$2, + requires: ['computeStyles'] + }; + + function getBasePlacement(placement) { + return placement.split('-')[0]; + } + + var max = Math.max; + var min = Math.min; + var round = Math.round; + + function getUAString() { + var uaData = navigator.userAgentData; + + if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) { + return uaData.brands.map(function (item) { + return item.brand + "/" + item.version; + }).join(' '); + } + + return navigator.userAgent; + } + + function isLayoutViewport() { + return !/^((?!chrome|android).)*safari/i.test(getUAString()); + } + + function getBoundingClientRect(element, includeScale, isFixedStrategy) { + if (includeScale === void 0) { + includeScale = false; + } + + if (isFixedStrategy === void 0) { + isFixedStrategy = false; + } + + var clientRect = element.getBoundingClientRect(); + var scaleX = 1; + var scaleY = 1; + + if (includeScale && isHTMLElement(element)) { + scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1; + scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1; + } + + var _ref = isElement(element) ? getWindow(element) : window, + visualViewport = _ref.visualViewport; + + var addVisualOffsets = !isLayoutViewport() && isFixedStrategy; + var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX; + var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY; + var width = clientRect.width / scaleX; + var height = clientRect.height / scaleY; + return { + width: width, + height: height, + top: y, + right: x + width, + bottom: y + height, + left: x, + x: x, + y: y + }; + } + + // means it doesn't take into account transforms. + + function getLayoutRect(element) { + var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed. + // Fixes https://github.com/popperjs/popper-core/issues/1223 + + var width = element.offsetWidth; + var height = element.offsetHeight; + + if (Math.abs(clientRect.width - width) <= 1) { + width = clientRect.width; + } + + if (Math.abs(clientRect.height - height) <= 1) { + height = clientRect.height; + } + + return { + x: element.offsetLeft, + y: element.offsetTop, + width: width, + height: height + }; + } + + function contains(parent, child) { + var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method + + if (parent.contains(child)) { + return true; + } // then fallback to custom implementation with Shadow DOM support + else if (rootNode && isShadowRoot(rootNode)) { + var next = child; + + do { + if (next && parent.isSameNode(next)) { + return true; + } // $FlowFixMe[prop-missing]: need a better way to handle this... + + + next = next.parentNode || next.host; + } while (next); + } // Give up, the result is false + + + return false; + } + + function getComputedStyle$1(element) { + return getWindow(element).getComputedStyle(element); + } + + function isTableElement(element) { + return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0; + } + + function getDocumentElement(element) { + // $FlowFixMe[incompatible-return]: assume body is always available + return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing] + element.document) || window.document).documentElement; + } + + function getParentNode(element) { + if (getNodeName(element) === 'html') { + return element; + } + + return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle + // $FlowFixMe[incompatible-return] + // $FlowFixMe[prop-missing] + element.assignedSlot || // step into the shadow DOM of the parent of a slotted node + element.parentNode || ( // DOM Element detected + isShadowRoot(element) ? element.host : null) || // ShadowRoot detected + // $FlowFixMe[incompatible-call]: HTMLElement is a Node + getDocumentElement(element) // fallback + + ); + } + + function getTrueOffsetParent(element) { + if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837 + getComputedStyle$1(element).position === 'fixed') { + return null; + } + + return element.offsetParent; + } // `.offsetParent` reports `null` for fixed elements, while absolute elements + // return the containing block + + + function getContainingBlock(element) { + var isFirefox = /firefox/i.test(getUAString()); + var isIE = /Trident/i.test(getUAString()); + + if (isIE && isHTMLElement(element)) { + // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport + var elementCss = getComputedStyle$1(element); + + if (elementCss.position === 'fixed') { + return null; + } + } + + var currentNode = getParentNode(element); + + if (isShadowRoot(currentNode)) { + currentNode = currentNode.host; + } + + while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) { + var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that + // create a containing block. + // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block + + if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') { + return currentNode; + } else { + currentNode = currentNode.parentNode; + } + } + + return null; + } // Gets the closest ancestor positioned element. Handles some edge cases, + // such as table ancestors and cross browser bugs. + + + function getOffsetParent(element) { + var window = getWindow(element); + var offsetParent = getTrueOffsetParent(element); + + while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') { + offsetParent = getTrueOffsetParent(offsetParent); + } + + if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static')) { + return window; + } + + return offsetParent || getContainingBlock(element) || window; + } + + function getMainAxisFromPlacement(placement) { + return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y'; + } + + function within(min$1, value, max$1) { + return max(min$1, min(value, max$1)); + } + function withinMaxClamp(min, value, max) { + var v = within(min, value, max); + return v > max ? max : v; + } + + function getFreshSideObject() { + return { + top: 0, + right: 0, + bottom: 0, + left: 0 + }; + } + + function mergePaddingObject(paddingObject) { + return Object.assign({}, getFreshSideObject(), paddingObject); + } + + function expandToHashMap(value, keys) { + return keys.reduce(function (hashMap, key) { + hashMap[key] = value; + return hashMap; + }, {}); + } + + var toPaddingObject = function toPaddingObject(padding, state) { + padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, { + placement: state.placement + })) : padding; + return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); + }; + + function arrow(_ref) { + var _state$modifiersData$; + + var state = _ref.state, + name = _ref.name, + options = _ref.options; + var arrowElement = state.elements.arrow; + var popperOffsets = state.modifiersData.popperOffsets; + var basePlacement = getBasePlacement(state.placement); + var axis = getMainAxisFromPlacement(basePlacement); + var isVertical = [left, right].indexOf(basePlacement) >= 0; + var len = isVertical ? 'height' : 'width'; + + if (!arrowElement || !popperOffsets) { + return; + } + + var paddingObject = toPaddingObject(options.padding, state); + var arrowRect = getLayoutRect(arrowElement); + var minProp = axis === 'y' ? top : left; + var maxProp = axis === 'y' ? bottom : right; + var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len]; + var startDiff = popperOffsets[axis] - state.rects.reference[axis]; + var arrowOffsetParent = getOffsetParent(arrowElement); + var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0; + var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is + // outside of the popper bounds + + var min = paddingObject[minProp]; + var max = clientSize - arrowRect[len] - paddingObject[maxProp]; + var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference; + var offset = within(min, center, max); // Prevents breaking syntax highlighting... + + var axisProp = axis; + state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$); + } + + function effect$1(_ref2) { + var state = _ref2.state, + options = _ref2.options; + var _options$element = options.element, + arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element; + + if (arrowElement == null) { + return; + } // CSS selector + + + if (typeof arrowElement === 'string') { + arrowElement = state.elements.popper.querySelector(arrowElement); + + if (!arrowElement) { + return; + } + } + + if (!contains(state.elements.popper, arrowElement)) { + return; + } + + state.elements.arrow = arrowElement; + } // eslint-disable-next-line import/no-unused-modules + + + const arrow$1 = { + name: 'arrow', + enabled: true, + phase: 'main', + fn: arrow, + effect: effect$1, + requires: ['popperOffsets'], + requiresIfExists: ['preventOverflow'] + }; + + function getVariation(placement) { + return placement.split('-')[1]; + } + + var unsetSides = { + top: 'auto', + right: 'auto', + bottom: 'auto', + left: 'auto' + }; // Round the offsets to the nearest suitable subpixel based on the DPR. + // Zooming can change the DPR, but it seems to report a value that will + // cleanly divide the values into the appropriate subpixels. + + function roundOffsetsByDPR(_ref, win) { + var x = _ref.x, + y = _ref.y; + var dpr = win.devicePixelRatio || 1; + return { + x: round(x * dpr) / dpr || 0, + y: round(y * dpr) / dpr || 0 + }; + } + + function mapToStyles(_ref2) { + var _Object$assign2; + + var popper = _ref2.popper, + popperRect = _ref2.popperRect, + placement = _ref2.placement, + variation = _ref2.variation, + offsets = _ref2.offsets, + position = _ref2.position, + gpuAcceleration = _ref2.gpuAcceleration, + adaptive = _ref2.adaptive, + roundOffsets = _ref2.roundOffsets, + isFixed = _ref2.isFixed; + var _offsets$x = offsets.x, + x = _offsets$x === void 0 ? 0 : _offsets$x, + _offsets$y = offsets.y, + y = _offsets$y === void 0 ? 0 : _offsets$y; + + var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({ + x: x, + y: y + }) : { + x: x, + y: y + }; + + x = _ref3.x; + y = _ref3.y; + var hasX = offsets.hasOwnProperty('x'); + var hasY = offsets.hasOwnProperty('y'); + var sideX = left; + var sideY = top; + var win = window; + + if (adaptive) { + var offsetParent = getOffsetParent(popper); + var heightProp = 'clientHeight'; + var widthProp = 'clientWidth'; + + if (offsetParent === getWindow(popper)) { + offsetParent = getDocumentElement(popper); + + if (getComputedStyle$1(offsetParent).position !== 'static' && position === 'absolute') { + heightProp = 'scrollHeight'; + widthProp = 'scrollWidth'; + } + } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it + + + offsetParent = offsetParent; + + if (placement === top || (placement === left || placement === right) && variation === end) { + sideY = bottom; + var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing] + offsetParent[heightProp]; + y -= offsetY - popperRect.height; + y *= gpuAcceleration ? 1 : -1; + } + + if (placement === left || (placement === top || placement === bottom) && variation === end) { + sideX = right; + var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing] + offsetParent[widthProp]; + x -= offsetX - popperRect.width; + x *= gpuAcceleration ? 1 : -1; + } + } + + var commonStyles = Object.assign({ + position: position + }, adaptive && unsetSides); + + var _ref4 = roundOffsets === true ? roundOffsetsByDPR({ + x: x, + y: y + }, getWindow(popper)) : { + x: x, + y: y + }; + + x = _ref4.x; + y = _ref4.y; + + if (gpuAcceleration) { + var _Object$assign; + + return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign)); + } + + return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2)); + } + + function computeStyles(_ref5) { + var state = _ref5.state, + options = _ref5.options; + var _options$gpuAccelerat = options.gpuAcceleration, + gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat, + _options$adaptive = options.adaptive, + adaptive = _options$adaptive === void 0 ? true : _options$adaptive, + _options$roundOffsets = options.roundOffsets, + roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets; + var commonStyles = { + placement: getBasePlacement(state.placement), + variation: getVariation(state.placement), + popper: state.elements.popper, + popperRect: state.rects.popper, + gpuAcceleration: gpuAcceleration, + isFixed: state.options.strategy === 'fixed' + }; + + if (state.modifiersData.popperOffsets != null) { + state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, { + offsets: state.modifiersData.popperOffsets, + position: state.options.strategy, + adaptive: adaptive, + roundOffsets: roundOffsets + }))); + } + + if (state.modifiersData.arrow != null) { + state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, { + offsets: state.modifiersData.arrow, + position: 'absolute', + adaptive: false, + roundOffsets: roundOffsets + }))); + } + + state.attributes.popper = Object.assign({}, state.attributes.popper, { + 'data-popper-placement': state.placement + }); + } // eslint-disable-next-line import/no-unused-modules + + + const computeStyles$1 = { + name: 'computeStyles', + enabled: true, + phase: 'beforeWrite', + fn: computeStyles, + data: {} + }; + + var passive = { + passive: true + }; + + function effect(_ref) { + var state = _ref.state, + instance = _ref.instance, + options = _ref.options; + var _options$scroll = options.scroll, + scroll = _options$scroll === void 0 ? true : _options$scroll, + _options$resize = options.resize, + resize = _options$resize === void 0 ? true : _options$resize; + var window = getWindow(state.elements.popper); + var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper); + + if (scroll) { + scrollParents.forEach(function (scrollParent) { + scrollParent.addEventListener('scroll', instance.update, passive); + }); + } + + if (resize) { + window.addEventListener('resize', instance.update, passive); + } + + return function () { + if (scroll) { + scrollParents.forEach(function (scrollParent) { + scrollParent.removeEventListener('scroll', instance.update, passive); + }); + } + + if (resize) { + window.removeEventListener('resize', instance.update, passive); + } + }; + } // eslint-disable-next-line import/no-unused-modules + + + const eventListeners = { + name: 'eventListeners', + enabled: true, + phase: 'write', + fn: function fn() {}, + effect: effect, + data: {} + }; + + var hash$1 = { + left: 'right', + right: 'left', + bottom: 'top', + top: 'bottom' + }; + function getOppositePlacement(placement) { + return placement.replace(/left|right|bottom|top/g, function (matched) { + return hash$1[matched]; + }); + } + + var hash = { + start: 'end', + end: 'start' + }; + function getOppositeVariationPlacement(placement) { + return placement.replace(/start|end/g, function (matched) { + return hash[matched]; + }); + } + + function getWindowScroll(node) { + var win = getWindow(node); + var scrollLeft = win.pageXOffset; + var scrollTop = win.pageYOffset; + return { + scrollLeft: scrollLeft, + scrollTop: scrollTop + }; + } + + function getWindowScrollBarX(element) { + // If has a CSS width greater than the viewport, then this will be + // incorrect for RTL. + // Popper 1 is broken in this case and never had a bug report so let's assume + // it's not an issue. I don't think anyone ever specifies width on + // anyway. + // Browsers where the left scrollbar doesn't cause an issue report `0` for + // this (e.g. Edge 2019, IE11, Safari) + return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft; + } + + function getViewportRect(element, strategy) { + var win = getWindow(element); + var html = getDocumentElement(element); + var visualViewport = win.visualViewport; + var width = html.clientWidth; + var height = html.clientHeight; + var x = 0; + var y = 0; + + if (visualViewport) { + width = visualViewport.width; + height = visualViewport.height; + var layoutViewport = isLayoutViewport(); + + if (layoutViewport || !layoutViewport && strategy === 'fixed') { + x = visualViewport.offsetLeft; + y = visualViewport.offsetTop; + } + } + + return { + width: width, + height: height, + x: x + getWindowScrollBarX(element), + y: y + }; + } + + // of the `` and `` rect bounds if horizontally scrollable + + function getDocumentRect(element) { + var _element$ownerDocumen; + + var html = getDocumentElement(element); + var winScroll = getWindowScroll(element); + var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body; + var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0); + var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0); + var x = -winScroll.scrollLeft + getWindowScrollBarX(element); + var y = -winScroll.scrollTop; + + if (getComputedStyle$1(body || html).direction === 'rtl') { + x += max(html.clientWidth, body ? body.clientWidth : 0) - width; + } + + return { + width: width, + height: height, + x: x, + y: y + }; + } + + function isScrollParent(element) { + // Firefox wants us to check `-x` and `-y` variations as well + var _getComputedStyle = getComputedStyle$1(element), + overflow = _getComputedStyle.overflow, + overflowX = _getComputedStyle.overflowX, + overflowY = _getComputedStyle.overflowY; + + return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX); + } + + function getScrollParent(node) { + if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) { + // $FlowFixMe[incompatible-return]: assume body is always available + return node.ownerDocument.body; + } + + if (isHTMLElement(node) && isScrollParent(node)) { + return node; + } + + return getScrollParent(getParentNode(node)); + } + + /* + given a DOM element, return the list of all scroll parents, up the list of ancesors + until we get to the top window object. This list is what we attach scroll listeners + to, because if any of these parent elements scroll, we'll need to re-calculate the + reference element's position. + */ + + function listScrollParents(element, list) { + var _element$ownerDocumen; + + if (list === void 0) { + list = []; + } + + var scrollParent = getScrollParent(element); + var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body); + var win = getWindow(scrollParent); + var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent; + var updatedList = list.concat(target); + return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here + updatedList.concat(listScrollParents(getParentNode(target))); + } + + function rectToClientRect(rect) { + return Object.assign({}, rect, { + left: rect.x, + top: rect.y, + right: rect.x + rect.width, + bottom: rect.y + rect.height + }); + } + + function getInnerBoundingClientRect(element, strategy) { + var rect = getBoundingClientRect(element, false, strategy === 'fixed'); + rect.top = rect.top + element.clientTop; + rect.left = rect.left + element.clientLeft; + rect.bottom = rect.top + element.clientHeight; + rect.right = rect.left + element.clientWidth; + rect.width = element.clientWidth; + rect.height = element.clientHeight; + rect.x = rect.left; + rect.y = rect.top; + return rect; + } + + function getClientRectFromMixedType(element, clippingParent, strategy) { + return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element))); + } // A "clipping parent" is an overflowable container with the characteristic of + // clipping (or hiding) overflowing elements with a position different from + // `initial` + + + function getClippingParents(element) { + var clippingParents = listScrollParents(getParentNode(element)); + var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle$1(element).position) >= 0; + var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element; + + if (!isElement(clipperElement)) { + return []; + } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414 + + + return clippingParents.filter(function (clippingParent) { + return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body'; + }); + } // Gets the maximum area that the element is visible in due to any number of + // clipping parents + + + function getClippingRect(element, boundary, rootBoundary, strategy) { + var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary); + var clippingParents = [].concat(mainClippingParents, [rootBoundary]); + var firstClippingParent = clippingParents[0]; + var clippingRect = clippingParents.reduce(function (accRect, clippingParent) { + var rect = getClientRectFromMixedType(element, clippingParent, strategy); + accRect.top = max(rect.top, accRect.top); + accRect.right = min(rect.right, accRect.right); + accRect.bottom = min(rect.bottom, accRect.bottom); + accRect.left = max(rect.left, accRect.left); + return accRect; + }, getClientRectFromMixedType(element, firstClippingParent, strategy)); + clippingRect.width = clippingRect.right - clippingRect.left; + clippingRect.height = clippingRect.bottom - clippingRect.top; + clippingRect.x = clippingRect.left; + clippingRect.y = clippingRect.top; + return clippingRect; + } + + function computeOffsets(_ref) { + var reference = _ref.reference, + element = _ref.element, + placement = _ref.placement; + var basePlacement = placement ? getBasePlacement(placement) : null; + var variation = placement ? getVariation(placement) : null; + var commonX = reference.x + reference.width / 2 - element.width / 2; + var commonY = reference.y + reference.height / 2 - element.height / 2; + var offsets; + + switch (basePlacement) { + case top: + offsets = { + x: commonX, + y: reference.y - element.height + }; + break; + + case bottom: + offsets = { + x: commonX, + y: reference.y + reference.height + }; + break; + + case right: + offsets = { + x: reference.x + reference.width, + y: commonY + }; + break; + + case left: + offsets = { + x: reference.x - element.width, + y: commonY + }; + break; + + default: + offsets = { + x: reference.x, + y: reference.y + }; + } + + var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null; + + if (mainAxis != null) { + var len = mainAxis === 'y' ? 'height' : 'width'; + + switch (variation) { + case start: + offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2); + break; + + case end: + offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2); + break; + } + } + + return offsets; + } + + function detectOverflow(state, options) { + if (options === void 0) { + options = {}; + } + + var _options = options, + _options$placement = _options.placement, + placement = _options$placement === void 0 ? state.placement : _options$placement, + _options$strategy = _options.strategy, + strategy = _options$strategy === void 0 ? state.strategy : _options$strategy, + _options$boundary = _options.boundary, + boundary = _options$boundary === void 0 ? clippingParents : _options$boundary, + _options$rootBoundary = _options.rootBoundary, + rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary, + _options$elementConte = _options.elementContext, + elementContext = _options$elementConte === void 0 ? popper : _options$elementConte, + _options$altBoundary = _options.altBoundary, + altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary, + _options$padding = _options.padding, + padding = _options$padding === void 0 ? 0 : _options$padding; + var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); + var altContext = elementContext === popper ? reference : popper; + var popperRect = state.rects.popper; + var element = state.elements[altBoundary ? altContext : elementContext]; + var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy); + var referenceClientRect = getBoundingClientRect(state.elements.reference); + var popperOffsets = computeOffsets({ + reference: referenceClientRect, + element: popperRect, + strategy: 'absolute', + placement: placement + }); + var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets)); + var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect + // 0 or negative = within the clipping rect + + var overflowOffsets = { + top: clippingClientRect.top - elementClientRect.top + paddingObject.top, + bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom, + left: clippingClientRect.left - elementClientRect.left + paddingObject.left, + right: elementClientRect.right - clippingClientRect.right + paddingObject.right + }; + var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element + + if (elementContext === popper && offsetData) { + var offset = offsetData[placement]; + Object.keys(overflowOffsets).forEach(function (key) { + var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1; + var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x'; + overflowOffsets[key] += offset[axis] * multiply; + }); + } + + return overflowOffsets; + } + + function computeAutoPlacement(state, options) { + if (options === void 0) { + options = {}; + } + + var _options = options, + placement = _options.placement, + boundary = _options.boundary, + rootBoundary = _options.rootBoundary, + padding = _options.padding, + flipVariations = _options.flipVariations, + _options$allowedAutoP = _options.allowedAutoPlacements, + allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP; + var variation = getVariation(placement); + var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) { + return getVariation(placement) === variation; + }) : basePlacements; + var allowedPlacements = placements$1.filter(function (placement) { + return allowedAutoPlacements.indexOf(placement) >= 0; + }); + + if (allowedPlacements.length === 0) { + allowedPlacements = placements$1; + } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions... + + + var overflows = allowedPlacements.reduce(function (acc, placement) { + acc[placement] = detectOverflow(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding + })[getBasePlacement(placement)]; + return acc; + }, {}); + return Object.keys(overflows).sort(function (a, b) { + return overflows[a] - overflows[b]; + }); + } + + function getExpandedFallbackPlacements(placement) { + if (getBasePlacement(placement) === auto) { + return []; + } + + var oppositePlacement = getOppositePlacement(placement); + return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)]; + } + + function flip(_ref) { + var state = _ref.state, + options = _ref.options, + name = _ref.name; + + if (state.modifiersData[name]._skip) { + return; + } + + var _options$mainAxis = options.mainAxis, + checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, + _options$altAxis = options.altAxis, + checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis, + specifiedFallbackPlacements = options.fallbackPlacements, + padding = options.padding, + boundary = options.boundary, + rootBoundary = options.rootBoundary, + altBoundary = options.altBoundary, + _options$flipVariatio = options.flipVariations, + flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio, + allowedAutoPlacements = options.allowedAutoPlacements; + var preferredPlacement = state.options.placement; + var basePlacement = getBasePlacement(preferredPlacement); + var isBasePlacement = basePlacement === preferredPlacement; + var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement)); + var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) { + return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding, + flipVariations: flipVariations, + allowedAutoPlacements: allowedAutoPlacements + }) : placement); + }, []); + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var checksMap = new Map(); + var makeFallbackChecks = true; + var firstFittingPlacement = placements[0]; + + for (var i = 0; i < placements.length; i++) { + var placement = placements[i]; + + var _basePlacement = getBasePlacement(placement); + + var isStartVariation = getVariation(placement) === start; + var isVertical = [top, bottom].indexOf(_basePlacement) >= 0; + var len = isVertical ? 'width' : 'height'; + var overflow = detectOverflow(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + altBoundary: altBoundary, + padding: padding + }); + var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top; + + if (referenceRect[len] > popperRect[len]) { + mainVariationSide = getOppositePlacement(mainVariationSide); + } + + var altVariationSide = getOppositePlacement(mainVariationSide); + var checks = []; + + if (checkMainAxis) { + checks.push(overflow[_basePlacement] <= 0); + } + + if (checkAltAxis) { + checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0); + } + + if (checks.every(function (check) { + return check; + })) { + firstFittingPlacement = placement; + makeFallbackChecks = false; + break; + } + + checksMap.set(placement, checks); + } + + if (makeFallbackChecks) { + // `2` may be desired in some cases – research later + var numberOfChecks = flipVariations ? 3 : 1; + + var _loop = function _loop(_i) { + var fittingPlacement = placements.find(function (placement) { + var checks = checksMap.get(placement); + + if (checks) { + return checks.slice(0, _i).every(function (check) { + return check; + }); + } + }); + + if (fittingPlacement) { + firstFittingPlacement = fittingPlacement; + return "break"; + } + }; + + for (var _i = numberOfChecks; _i > 0; _i--) { + var _ret = _loop(_i); + + if (_ret === "break") break; + } + } + + if (state.placement !== firstFittingPlacement) { + state.modifiersData[name]._skip = true; + state.placement = firstFittingPlacement; + state.reset = true; + } + } // eslint-disable-next-line import/no-unused-modules + + + const flip$1 = { + name: 'flip', + enabled: true, + phase: 'main', + fn: flip, + requiresIfExists: ['offset'], + data: { + _skip: false + } + }; + + function getSideOffsets(overflow, rect, preventedOffsets) { + if (preventedOffsets === void 0) { + preventedOffsets = { + x: 0, + y: 0 + }; + } + + return { + top: overflow.top - rect.height - preventedOffsets.y, + right: overflow.right - rect.width + preventedOffsets.x, + bottom: overflow.bottom - rect.height + preventedOffsets.y, + left: overflow.left - rect.width - preventedOffsets.x + }; + } + + function isAnySideFullyClipped(overflow) { + return [top, right, bottom, left].some(function (side) { + return overflow[side] >= 0; + }); + } + + function hide(_ref) { + var state = _ref.state, + name = _ref.name; + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var preventedOffsets = state.modifiersData.preventOverflow; + var referenceOverflow = detectOverflow(state, { + elementContext: 'reference' + }); + var popperAltOverflow = detectOverflow(state, { + altBoundary: true + }); + var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect); + var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets); + var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets); + var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets); + state.modifiersData[name] = { + referenceClippingOffsets: referenceClippingOffsets, + popperEscapeOffsets: popperEscapeOffsets, + isReferenceHidden: isReferenceHidden, + hasPopperEscaped: hasPopperEscaped + }; + state.attributes.popper = Object.assign({}, state.attributes.popper, { + 'data-popper-reference-hidden': isReferenceHidden, + 'data-popper-escaped': hasPopperEscaped + }); + } // eslint-disable-next-line import/no-unused-modules + + + const hide$1 = { + name: 'hide', + enabled: true, + phase: 'main', + requiresIfExists: ['preventOverflow'], + fn: hide + }; + + function distanceAndSkiddingToXY(placement, rects, offset) { + var basePlacement = getBasePlacement(placement); + var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1; + + var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, { + placement: placement + })) : offset, + skidding = _ref[0], + distance = _ref[1]; + + skidding = skidding || 0; + distance = (distance || 0) * invertDistance; + return [left, right].indexOf(basePlacement) >= 0 ? { + x: distance, + y: skidding + } : { + x: skidding, + y: distance + }; + } + + function offset(_ref2) { + var state = _ref2.state, + options = _ref2.options, + name = _ref2.name; + var _options$offset = options.offset, + offset = _options$offset === void 0 ? [0, 0] : _options$offset; + var data = placements.reduce(function (acc, placement) { + acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset); + return acc; + }, {}); + var _data$state$placement = data[state.placement], + x = _data$state$placement.x, + y = _data$state$placement.y; + + if (state.modifiersData.popperOffsets != null) { + state.modifiersData.popperOffsets.x += x; + state.modifiersData.popperOffsets.y += y; + } + + state.modifiersData[name] = data; + } // eslint-disable-next-line import/no-unused-modules + + + const offset$1 = { + name: 'offset', + enabled: true, + phase: 'main', + requires: ['popperOffsets'], + fn: offset + }; + + function popperOffsets(_ref) { + var state = _ref.state, + name = _ref.name; + // Offsets are the actual position the popper needs to have to be + // properly positioned near its reference element + // This is the most basic placement, and will be adjusted by + // the modifiers in the next step + state.modifiersData[name] = computeOffsets({ + reference: state.rects.reference, + element: state.rects.popper, + strategy: 'absolute', + placement: state.placement + }); + } // eslint-disable-next-line import/no-unused-modules + + + const popperOffsets$1 = { + name: 'popperOffsets', + enabled: true, + phase: 'read', + fn: popperOffsets, + data: {} + }; + + function getAltAxis(axis) { + return axis === 'x' ? 'y' : 'x'; + } + + function preventOverflow(_ref) { + var state = _ref.state, + options = _ref.options, + name = _ref.name; + var _options$mainAxis = options.mainAxis, + checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, + _options$altAxis = options.altAxis, + checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis, + boundary = options.boundary, + rootBoundary = options.rootBoundary, + altBoundary = options.altBoundary, + padding = options.padding, + _options$tether = options.tether, + tether = _options$tether === void 0 ? true : _options$tether, + _options$tetherOffset = options.tetherOffset, + tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset; + var overflow = detectOverflow(state, { + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding, + altBoundary: altBoundary + }); + var basePlacement = getBasePlacement(state.placement); + var variation = getVariation(state.placement); + var isBasePlacement = !variation; + var mainAxis = getMainAxisFromPlacement(basePlacement); + var altAxis = getAltAxis(mainAxis); + var popperOffsets = state.modifiersData.popperOffsets; + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, { + placement: state.placement + })) : tetherOffset; + var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? { + mainAxis: tetherOffsetValue, + altAxis: tetherOffsetValue + } : Object.assign({ + mainAxis: 0, + altAxis: 0 + }, tetherOffsetValue); + var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null; + var data = { + x: 0, + y: 0 + }; + + if (!popperOffsets) { + return; + } + + if (checkMainAxis) { + var _offsetModifierState$; + + var mainSide = mainAxis === 'y' ? top : left; + var altSide = mainAxis === 'y' ? bottom : right; + var len = mainAxis === 'y' ? 'height' : 'width'; + var offset = popperOffsets[mainAxis]; + var min$1 = offset + overflow[mainSide]; + var max$1 = offset - overflow[altSide]; + var additive = tether ? -popperRect[len] / 2 : 0; + var minLen = variation === start ? referenceRect[len] : popperRect[len]; + var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go + // outside the reference bounds + + var arrowElement = state.elements.arrow; + var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : { + width: 0, + height: 0 + }; + var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject(); + var arrowPaddingMin = arrowPaddingObject[mainSide]; + var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want + // to include its full size in the calculation. If the reference is small + // and near the edge of a boundary, the popper can overflow even if the + // reference is not overflowing as well (e.g. virtual elements with no + // width or height) + + var arrowLen = within(0, referenceRect[len], arrowRect[len]); + var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis; + var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis; + var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow); + var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0; + var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0; + var tetherMin = offset + minOffset - offsetModifierValue - clientOffset; + var tetherMax = offset + maxOffset - offsetModifierValue; + var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1); + popperOffsets[mainAxis] = preventedOffset; + data[mainAxis] = preventedOffset - offset; + } + + if (checkAltAxis) { + var _offsetModifierState$2; + + var _mainSide = mainAxis === 'x' ? top : left; + + var _altSide = mainAxis === 'x' ? bottom : right; + + var _offset = popperOffsets[altAxis]; + + var _len = altAxis === 'y' ? 'height' : 'width'; + + var _min = _offset + overflow[_mainSide]; + + var _max = _offset - overflow[_altSide]; + + var isOriginSide = [top, left].indexOf(basePlacement) !== -1; + + var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0; + + var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis; + + var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max; + + var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max); + + popperOffsets[altAxis] = _preventedOffset; + data[altAxis] = _preventedOffset - _offset; + } + + state.modifiersData[name] = data; + } // eslint-disable-next-line import/no-unused-modules + + + const preventOverflow$1 = { + name: 'preventOverflow', + enabled: true, + phase: 'main', + fn: preventOverflow, + requiresIfExists: ['offset'] + }; + + function getHTMLElementScroll(element) { + return { + scrollLeft: element.scrollLeft, + scrollTop: element.scrollTop + }; + } + + function getNodeScroll(node) { + if (node === getWindow(node) || !isHTMLElement(node)) { + return getWindowScroll(node); + } else { + return getHTMLElementScroll(node); + } + } + + function isElementScaled(element) { + var rect = element.getBoundingClientRect(); + var scaleX = round(rect.width) / element.offsetWidth || 1; + var scaleY = round(rect.height) / element.offsetHeight || 1; + return scaleX !== 1 || scaleY !== 1; + } // Returns the composite rect of an element relative to its offsetParent. + // Composite means it takes into account transforms as well as layout. + + + function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) { + if (isFixed === void 0) { + isFixed = false; + } + + var isOffsetParentAnElement = isHTMLElement(offsetParent); + var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent); + var documentElement = getDocumentElement(offsetParent); + var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed); + var scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + var offsets = { + x: 0, + y: 0 + }; + + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078 + isScrollParent(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + + if (isHTMLElement(offsetParent)) { + offsets = getBoundingClientRect(offsetParent, true); + offsets.x += offsetParent.clientLeft; + offsets.y += offsetParent.clientTop; + } else if (documentElement) { + offsets.x = getWindowScrollBarX(documentElement); + } + } + + return { + x: rect.left + scroll.scrollLeft - offsets.x, + y: rect.top + scroll.scrollTop - offsets.y, + width: rect.width, + height: rect.height + }; + } + + function order(modifiers) { + var map = new Map(); + var visited = new Set(); + var result = []; + modifiers.forEach(function (modifier) { + map.set(modifier.name, modifier); + }); // On visiting object, check for its dependencies and visit them recursively + + function sort(modifier) { + visited.add(modifier.name); + var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []); + requires.forEach(function (dep) { + if (!visited.has(dep)) { + var depModifier = map.get(dep); + + if (depModifier) { + sort(depModifier); + } + } + }); + result.push(modifier); + } + + modifiers.forEach(function (modifier) { + if (!visited.has(modifier.name)) { + // check for visited object + sort(modifier); + } + }); + return result; + } + + function orderModifiers(modifiers) { + // order based on dependencies + var orderedModifiers = order(modifiers); // order based on phase + + return modifierPhases.reduce(function (acc, phase) { + return acc.concat(orderedModifiers.filter(function (modifier) { + return modifier.phase === phase; + })); + }, []); + } + + function debounce(fn) { + var pending; + return function () { + if (!pending) { + pending = new Promise(function (resolve) { + Promise.resolve().then(function () { + pending = undefined; + resolve(fn()); + }); + }); + } + + return pending; + }; + } + + function mergeByName(modifiers) { + var merged = modifiers.reduce(function (merged, current) { + var existing = merged[current.name]; + merged[current.name] = existing ? Object.assign({}, existing, current, { + options: Object.assign({}, existing.options, current.options), + data: Object.assign({}, existing.data, current.data) + }) : current; + return merged; + }, {}); // IE11 does not support Object.values + + return Object.keys(merged).map(function (key) { + return merged[key]; + }); + } + + var DEFAULT_OPTIONS = { + placement: 'bottom', + modifiers: [], + strategy: 'absolute' + }; + + function areValidElements() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return !args.some(function (element) { + return !(element && typeof element.getBoundingClientRect === 'function'); + }); + } + + function popperGenerator(generatorOptions) { + if (generatorOptions === void 0) { + generatorOptions = {}; + } + + var _generatorOptions = generatorOptions, + _generatorOptions$def = _generatorOptions.defaultModifiers, + defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def, + _generatorOptions$def2 = _generatorOptions.defaultOptions, + defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2; + return function createPopper(reference, popper, options) { + if (options === void 0) { + options = defaultOptions; + } + + var state = { + placement: 'bottom', + orderedModifiers: [], + options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions), + modifiersData: {}, + elements: { + reference: reference, + popper: popper + }, + attributes: {}, + styles: {} + }; + var effectCleanupFns = []; + var isDestroyed = false; + var instance = { + state: state, + setOptions: function setOptions(setOptionsAction) { + var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction; + cleanupModifierEffects(); + state.options = Object.assign({}, defaultOptions, state.options, options); + state.scrollParents = { + reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [], + popper: listScrollParents(popper) + }; // Orders the modifiers based on their dependencies and `phase` + // properties + + var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers + + state.orderedModifiers = orderedModifiers.filter(function (m) { + return m.enabled; + }); + runModifierEffects(); + return instance.update(); + }, + // Sync update – it will always be executed, even if not necessary. This + // is useful for low frequency updates where sync behavior simplifies the + // logic. + // For high frequency updates (e.g. `resize` and `scroll` events), always + // prefer the async Popper#update method + forceUpdate: function forceUpdate() { + if (isDestroyed) { + return; + } + + var _state$elements = state.elements, + reference = _state$elements.reference, + popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements + // anymore + + if (!areValidElements(reference, popper)) { + return; + } // Store the reference and popper rects to be read by modifiers + + + state.rects = { + reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'), + popper: getLayoutRect(popper) + }; // Modifiers have the ability to reset the current update cycle. The + // most common use case for this is the `flip` modifier changing the + // placement, which then needs to re-run all the modifiers, because the + // logic was previously ran for the previous placement and is therefore + // stale/incorrect + + state.reset = false; + state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier + // is filled with the initial data specified by the modifier. This means + // it doesn't persist and is fresh on each update. + // To ensure persistent data, use `${name}#persistent` + + state.orderedModifiers.forEach(function (modifier) { + return state.modifiersData[modifier.name] = Object.assign({}, modifier.data); + }); + + for (var index = 0; index < state.orderedModifiers.length; index++) { + if (state.reset === true) { + state.reset = false; + index = -1; + continue; + } + + var _state$orderedModifie = state.orderedModifiers[index], + fn = _state$orderedModifie.fn, + _state$orderedModifie2 = _state$orderedModifie.options, + _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2, + name = _state$orderedModifie.name; + + if (typeof fn === 'function') { + state = fn({ + state: state, + options: _options, + name: name, + instance: instance + }) || state; + } + } + }, + // Async and optimistically optimized update – it will not be executed if + // not necessary (debounced to run at most once-per-tick) + update: debounce(function () { + return new Promise(function (resolve) { + instance.forceUpdate(); + resolve(state); + }); + }), + destroy: function destroy() { + cleanupModifierEffects(); + isDestroyed = true; + } + }; + + if (!areValidElements(reference, popper)) { + return instance; + } + + instance.setOptions(options).then(function (state) { + if (!isDestroyed && options.onFirstUpdate) { + options.onFirstUpdate(state); + } + }); // Modifiers have the ability to execute arbitrary code before the first + // update cycle runs. They will be executed in the same order as the update + // cycle. This is useful when a modifier adds some persistent data that + // other modifiers need to use, but the modifier is run after the dependent + // one. + + function runModifierEffects() { + state.orderedModifiers.forEach(function (_ref) { + var name = _ref.name, + _ref$options = _ref.options, + options = _ref$options === void 0 ? {} : _ref$options, + effect = _ref.effect; + + if (typeof effect === 'function') { + var cleanupFn = effect({ + state: state, + name: name, + instance: instance, + options: options + }); + + var noopFn = function noopFn() {}; + + effectCleanupFns.push(cleanupFn || noopFn); + } + }); + } + + function cleanupModifierEffects() { + effectCleanupFns.forEach(function (fn) { + return fn(); + }); + effectCleanupFns = []; + } + + return instance; + }; + } + var createPopper$2 = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules + + var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1]; + var createPopper$1 = /*#__PURE__*/popperGenerator({ + defaultModifiers: defaultModifiers$1 + }); // eslint-disable-next-line import/no-unused-modules + + var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1]; + var createPopper = /*#__PURE__*/popperGenerator({ + defaultModifiers: defaultModifiers + }); // eslint-disable-next-line import/no-unused-modules + + const Popper = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ + __proto__: null, + afterMain, + afterRead, + afterWrite, + applyStyles: applyStyles$1, + arrow: arrow$1, + auto, + basePlacements, + beforeMain, + beforeRead, + beforeWrite, + bottom, + clippingParents, + computeStyles: computeStyles$1, + createPopper, + createPopperBase: createPopper$2, + createPopperLite: createPopper$1, + detectOverflow, + end, + eventListeners, + flip: flip$1, + hide: hide$1, + left, + main, + modifierPhases, + offset: offset$1, + placements, + popper, + popperGenerator, + popperOffsets: popperOffsets$1, + preventOverflow: preventOverflow$1, + read, + reference, + right, + start, + top, + variationPlacements, + viewport, + write + }, Symbol.toStringTag, { value: 'Module' })); + + /** + * -------------------------------------------------------------------------- + * Bootstrap dropdown.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$a = 'dropdown'; + const DATA_KEY$6 = 'bs.dropdown'; + const EVENT_KEY$6 = `.${DATA_KEY$6}`; + const DATA_API_KEY$3 = '.data-api'; + const ESCAPE_KEY$2 = 'Escape'; + const TAB_KEY$1 = 'Tab'; + const ARROW_UP_KEY$1 = 'ArrowUp'; + const ARROW_DOWN_KEY$1 = 'ArrowDown'; + const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button + + const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`; + const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`; + const EVENT_SHOW$5 = `show${EVENT_KEY$6}`; + const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`; + const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`; + const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`; + const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`; + const CLASS_NAME_SHOW$6 = 'show'; + const CLASS_NAME_DROPUP = 'dropup'; + const CLASS_NAME_DROPEND = 'dropend'; + const CLASS_NAME_DROPSTART = 'dropstart'; + const CLASS_NAME_DROPUP_CENTER = 'dropup-center'; + const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'; + const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'; + const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`; + const SELECTOR_MENU = '.dropdown-menu'; + const SELECTOR_NAVBAR = '.navbar'; + const SELECTOR_NAVBAR_NAV = '.navbar-nav'; + const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; + const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'; + const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'; + const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'; + const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'; + const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'; + const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'; + const PLACEMENT_TOPCENTER = 'top'; + const PLACEMENT_BOTTOMCENTER = 'bottom'; + const Default$9 = { + autoClose: true, + boundary: 'clippingParents', + display: 'dynamic', + offset: [0, 2], + popperConfig: null, + reference: 'toggle' + }; + const DefaultType$9 = { + autoClose: '(boolean|string)', + boundary: '(string|element)', + display: 'string', + offset: '(array|string|function)', + popperConfig: '(null|object|function)', + reference: '(string|element|object)' + }; + + /** + * Class definition + */ + + class Dropdown extends BaseComponent { + constructor(element, config) { + super(element, config); + this._popper = null; + this._parent = this._element.parentNode; // dropdown wrapper + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent); + this._inNavbar = this._detectNavbar(); + } + + // Getters + static get Default() { + return Default$9; + } + static get DefaultType() { + return DefaultType$9; + } + static get NAME() { + return NAME$a; + } + + // Public + toggle() { + return this._isShown() ? this.hide() : this.show(); + } + show() { + if (isDisabled(this._element) || this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget); + if (showEvent.defaultPrevented) { + return; + } + this._createPopper(); + + // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) { + for (const element of [].concat(...document.body.children)) { + EventHandler.on(element, 'mouseover', noop); + } + } + this._element.focus(); + this._element.setAttribute('aria-expanded', true); + this._menu.classList.add(CLASS_NAME_SHOW$6); + this._element.classList.add(CLASS_NAME_SHOW$6); + EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget); + } + hide() { + if (isDisabled(this._element) || !this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + this._completeHide(relatedTarget); + } + dispose() { + if (this._popper) { + this._popper.destroy(); + } + super.dispose(); + } + update() { + this._inNavbar = this._detectNavbar(); + if (this._popper) { + this._popper.update(); + } + } + + // Private + _completeHide(relatedTarget) { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget); + if (hideEvent.defaultPrevented) { + return; + } + + // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.off(element, 'mouseover', noop); + } + } + if (this._popper) { + this._popper.destroy(); + } + this._menu.classList.remove(CLASS_NAME_SHOW$6); + this._element.classList.remove(CLASS_NAME_SHOW$6); + this._element.setAttribute('aria-expanded', 'false'); + Manipulator.removeDataAttribute(this._menu, 'popper'); + EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget); + } + _getConfig(config) { + config = super._getConfig(config); + if (typeof config.reference === 'object' && !isElement$1(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { + // Popper virtual elements require a getBoundingClientRect method + throw new TypeError(`${NAME$a.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); + } + return config; + } + _createPopper() { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)'); + } + let referenceElement = this._element; + if (this._config.reference === 'parent') { + referenceElement = this._parent; + } else if (isElement$1(this._config.reference)) { + referenceElement = getElement(this._config.reference); + } else if (typeof this._config.reference === 'object') { + referenceElement = this._config.reference; + } + const popperConfig = this._getPopperConfig(); + this._popper = createPopper(referenceElement, this._menu, popperConfig); + } + _isShown() { + return this._menu.classList.contains(CLASS_NAME_SHOW$6); + } + _getPlacement() { + const parentDropdown = this._parent; + if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { + return PLACEMENT_RIGHT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { + return PLACEMENT_LEFT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) { + return PLACEMENT_TOPCENTER; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) { + return PLACEMENT_BOTTOMCENTER; + } + + // We need to trim the value because custom properties can also include spaces + const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'; + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { + return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP; + } + return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM; + } + _detectNavbar() { + return this._element.closest(SELECTOR_NAVBAR) !== null; + } + _getOffset() { + const { + offset + } = this._config; + if (typeof offset === 'string') { + return offset.split(',').map(value => Number.parseInt(value, 10)); + } + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element); + } + return offset; + } + _getPopperConfig() { + const defaultBsPopperConfig = { + placement: this._getPlacement(), + modifiers: [{ + name: 'preventOverflow', + options: { + boundary: this._config.boundary + } + }, { + name: 'offset', + options: { + offset: this._getOffset() + } + }] + }; + + // Disable Popper if we have a static display or Dropdown is in Navbar + if (this._inNavbar || this._config.display === 'static') { + Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove + defaultBsPopperConfig.modifiers = [{ + name: 'applyStyles', + enabled: false + }]; + } + return { + ...defaultBsPopperConfig, + ...execute(this._config.popperConfig, [defaultBsPopperConfig]) + }; + } + _selectMenuItem({ + key, + target + }) { + const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element)); + if (!items.length) { + return; + } + + // if target isn't included in items (e.g. when expanding the dropdown) + // allow cycling to get the last item in case key equals ARROW_UP_KEY + getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus(); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Dropdown.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + static clearMenus(event) { + if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) { + return; + } + const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN); + for (const toggle of openToggles) { + const context = Dropdown.getInstance(toggle); + if (!context || context._config.autoClose === false) { + continue; + } + const composedPath = event.composedPath(); + const isMenuTarget = composedPath.includes(context._menu); + if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { + continue; + } + + // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu + if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) { + continue; + } + const relatedTarget = { + relatedTarget: context._element + }; + if (event.type === 'click') { + relatedTarget.clickEvent = event; + } + context._completeHide(relatedTarget); + } + } + static dataApiKeydownHandler(event) { + // If not an UP | DOWN | ESCAPE key => not a dropdown command + // If input/textarea && if key is other than ESCAPE => not a dropdown command + + const isInput = /input|textarea/i.test(event.target.tagName); + const isEscapeEvent = event.key === ESCAPE_KEY$2; + const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key); + if (!isUpOrDownEvent && !isEscapeEvent) { + return; + } + if (isInput && !isEscapeEvent) { + return; + } + event.preventDefault(); + + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode); + const instance = Dropdown.getOrCreateInstance(getToggleButton); + if (isUpOrDownEvent) { + event.stopPropagation(); + instance.show(); + instance._selectMenuItem(event); + return; + } + if (instance._isShown()) { + // else is escape and we check if it is shown + event.stopPropagation(); + instance.hide(); + getToggleButton.focus(); + } + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus); + EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus); + EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) { + event.preventDefault(); + Dropdown.getOrCreateInstance(this).toggle(); + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Dropdown); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/backdrop.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$9 = 'backdrop'; + const CLASS_NAME_FADE$4 = 'fade'; + const CLASS_NAME_SHOW$5 = 'show'; + const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`; + const Default$8 = { + className: 'modal-backdrop', + clickCallback: null, + isAnimated: false, + isVisible: true, + // if false, we use the backdrop helper without adding any element to the dom + rootElement: 'body' // give the choice to place backdrop under different elements + }; + const DefaultType$8 = { + className: 'string', + clickCallback: '(function|null)', + isAnimated: 'boolean', + isVisible: 'boolean', + rootElement: '(element|string)' + }; + + /** + * Class definition + */ + + class Backdrop extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + this._isAppended = false; + this._element = null; + } + + // Getters + static get Default() { + return Default$8; + } + static get DefaultType() { + return DefaultType$8; + } + static get NAME() { + return NAME$9; + } + + // Public + show(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + this._append(); + const element = this._getElement(); + if (this._config.isAnimated) { + reflow(element); + } + element.classList.add(CLASS_NAME_SHOW$5); + this._emulateAnimation(() => { + execute(callback); + }); + } + hide(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + this._getElement().classList.remove(CLASS_NAME_SHOW$5); + this._emulateAnimation(() => { + this.dispose(); + execute(callback); + }); + } + dispose() { + if (!this._isAppended) { + return; + } + EventHandler.off(this._element, EVENT_MOUSEDOWN); + this._element.remove(); + this._isAppended = false; + } + + // Private + _getElement() { + if (!this._element) { + const backdrop = document.createElement('div'); + backdrop.className = this._config.className; + if (this._config.isAnimated) { + backdrop.classList.add(CLASS_NAME_FADE$4); + } + this._element = backdrop; + } + return this._element; + } + _configAfterMerge(config) { + // use getElement() with the default "body" to get a fresh Element on each instantiation + config.rootElement = getElement(config.rootElement); + return config; + } + _append() { + if (this._isAppended) { + return; + } + const element = this._getElement(); + this._config.rootElement.append(element); + EventHandler.on(element, EVENT_MOUSEDOWN, () => { + execute(this._config.clickCallback); + }); + this._isAppended = true; + } + _emulateAnimation(callback) { + executeAfterTransition(callback, this._getElement(), this._config.isAnimated); + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/focustrap.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$8 = 'focustrap'; + const DATA_KEY$5 = 'bs.focustrap'; + const EVENT_KEY$5 = `.${DATA_KEY$5}`; + const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`; + const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`; + const TAB_KEY = 'Tab'; + const TAB_NAV_FORWARD = 'forward'; + const TAB_NAV_BACKWARD = 'backward'; + const Default$7 = { + autofocus: true, + trapElement: null // The element to trap focus inside of + }; + const DefaultType$7 = { + autofocus: 'boolean', + trapElement: 'element' + }; + + /** + * Class definition + */ + + class FocusTrap extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + this._isActive = false; + this._lastTabNavDirection = null; + } + + // Getters + static get Default() { + return Default$7; + } + static get DefaultType() { + return DefaultType$7; + } + static get NAME() { + return NAME$8; + } + + // Public + activate() { + if (this._isActive) { + return; + } + if (this._config.autofocus) { + this._config.trapElement.focus(); + } + EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop + EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event)); + EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); + this._isActive = true; + } + deactivate() { + if (!this._isActive) { + return; + } + this._isActive = false; + EventHandler.off(document, EVENT_KEY$5); + } + + // Private + _handleFocusin(event) { + const { + trapElement + } = this._config; + if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { + return; + } + const elements = SelectorEngine.focusableChildren(trapElement); + if (elements.length === 0) { + trapElement.focus(); + } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { + elements[elements.length - 1].focus(); + } else { + elements[0].focus(); + } + } + _handleKeydown(event) { + if (event.key !== TAB_KEY) { + return; + } + this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/scrollBar.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; + const SELECTOR_STICKY_CONTENT = '.sticky-top'; + const PROPERTY_PADDING = 'padding-right'; + const PROPERTY_MARGIN = 'margin-right'; + + /** + * Class definition + */ + + class ScrollBarHelper { + constructor() { + this._element = document.body; + } + + // Public + getWidth() { + // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes + const documentWidth = document.documentElement.clientWidth; + return Math.abs(window.innerWidth - documentWidth); + } + hide() { + const width = this.getWidth(); + this._disableOverFlow(); + // give padding to element to balance the hidden scrollbar width + this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); + // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth + this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width); + this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width); + } + reset() { + this._resetElementAttributes(this._element, 'overflow'); + this._resetElementAttributes(this._element, PROPERTY_PADDING); + this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING); + this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN); + } + isOverflowing() { + return this.getWidth() > 0; + } + + // Private + _disableOverFlow() { + this._saveInitialAttribute(this._element, 'overflow'); + this._element.style.overflow = 'hidden'; + } + _setElementAttributes(selector, styleProperty, callback) { + const scrollbarWidth = this.getWidth(); + const manipulationCallBack = element => { + if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { + return; + } + this._saveInitialAttribute(element, styleProperty); + const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty); + element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`); + }; + this._applyManipulationCallback(selector, manipulationCallBack); + } + _saveInitialAttribute(element, styleProperty) { + const actualValue = element.style.getPropertyValue(styleProperty); + if (actualValue) { + Manipulator.setDataAttribute(element, styleProperty, actualValue); + } + } + _resetElementAttributes(selector, styleProperty) { + const manipulationCallBack = element => { + const value = Manipulator.getDataAttribute(element, styleProperty); + // We only want to remove the property if the value is `null`; the value can also be zero + if (value === null) { + element.style.removeProperty(styleProperty); + return; + } + Manipulator.removeDataAttribute(element, styleProperty); + element.style.setProperty(styleProperty, value); + }; + this._applyManipulationCallback(selector, manipulationCallBack); + } + _applyManipulationCallback(selector, callBack) { + if (isElement$1(selector)) { + callBack(selector); + return; + } + for (const sel of SelectorEngine.find(selector, this._element)) { + callBack(sel); + } + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap modal.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$7 = 'modal'; + const DATA_KEY$4 = 'bs.modal'; + const EVENT_KEY$4 = `.${DATA_KEY$4}`; + const DATA_API_KEY$2 = '.data-api'; + const ESCAPE_KEY$1 = 'Escape'; + const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`; + const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`; + const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`; + const EVENT_SHOW$4 = `show${EVENT_KEY$4}`; + const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`; + const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`; + const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`; + const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`; + const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`; + const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`; + const CLASS_NAME_OPEN = 'modal-open'; + const CLASS_NAME_FADE$3 = 'fade'; + const CLASS_NAME_SHOW$4 = 'show'; + const CLASS_NAME_STATIC = 'modal-static'; + const OPEN_SELECTOR$1 = '.modal.show'; + const SELECTOR_DIALOG = '.modal-dialog'; + const SELECTOR_MODAL_BODY = '.modal-body'; + const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle="modal"]'; + const Default$6 = { + backdrop: true, + focus: true, + keyboard: true + }; + const DefaultType$6 = { + backdrop: '(boolean|string)', + focus: 'boolean', + keyboard: 'boolean' + }; + + /** + * Class definition + */ + + class Modal extends BaseComponent { + constructor(element, config) { + super(element, config); + this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); + this._backdrop = this._initializeBackDrop(); + this._focustrap = this._initializeFocusTrap(); + this._isShown = false; + this._isTransitioning = false; + this._scrollBar = new ScrollBarHelper(); + this._addEventListeners(); + } + + // Getters + static get Default() { + return Default$6; + } + static get DefaultType() { + return DefaultType$6; + } + static get NAME() { + return NAME$7; + } + + // Public + toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + } + show(relatedTarget) { + if (this._isShown || this._isTransitioning) { + return; + } + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, { + relatedTarget + }); + if (showEvent.defaultPrevented) { + return; + } + this._isShown = true; + this._isTransitioning = true; + this._scrollBar.hide(); + document.body.classList.add(CLASS_NAME_OPEN); + this._adjustDialog(); + this._backdrop.show(() => this._showElement(relatedTarget)); + } + hide() { + if (!this._isShown || this._isTransitioning) { + return; + } + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4); + if (hideEvent.defaultPrevented) { + return; + } + this._isShown = false; + this._isTransitioning = true; + this._focustrap.deactivate(); + this._element.classList.remove(CLASS_NAME_SHOW$4); + this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()); + } + dispose() { + EventHandler.off(window, EVENT_KEY$4); + EventHandler.off(this._dialog, EVENT_KEY$4); + this._backdrop.dispose(); + this._focustrap.deactivate(); + super.dispose(); + } + handleUpdate() { + this._adjustDialog(); + } + + // Private + _initializeBackDrop() { + return new Backdrop({ + isVisible: Boolean(this._config.backdrop), + // 'static' option will be translated to true, and booleans will keep their value, + isAnimated: this._isAnimated() + }); + } + _initializeFocusTrap() { + return new FocusTrap({ + trapElement: this._element + }); + } + _showElement(relatedTarget) { + // try to append dynamic modal + if (!document.body.contains(this._element)) { + document.body.append(this._element); + } + this._element.style.display = 'block'; + this._element.removeAttribute('aria-hidden'); + this._element.setAttribute('aria-modal', true); + this._element.setAttribute('role', 'dialog'); + this._element.scrollTop = 0; + const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog); + if (modalBody) { + modalBody.scrollTop = 0; + } + reflow(this._element); + this._element.classList.add(CLASS_NAME_SHOW$4); + const transitionComplete = () => { + if (this._config.focus) { + this._focustrap.activate(); + } + this._isTransitioning = false; + EventHandler.trigger(this._element, EVENT_SHOWN$4, { + relatedTarget + }); + }; + this._queueCallback(transitionComplete, this._dialog, this._isAnimated()); + } + _addEventListeners() { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => { + if (event.key !== ESCAPE_KEY$1) { + return; + } + if (this._config.keyboard) { + this.hide(); + return; + } + this._triggerBackdropTransition(); + }); + EventHandler.on(window, EVENT_RESIZE$1, () => { + if (this._isShown && !this._isTransitioning) { + this._adjustDialog(); + } + }); + EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { + // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks + EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => { + if (this._element !== event.target || this._element !== event2.target) { + return; + } + if (this._config.backdrop === 'static') { + this._triggerBackdropTransition(); + return; + } + if (this._config.backdrop) { + this.hide(); + } + }); + }); + } + _hideModal() { + this._element.style.display = 'none'; + this._element.setAttribute('aria-hidden', true); + this._element.removeAttribute('aria-modal'); + this._element.removeAttribute('role'); + this._isTransitioning = false; + this._backdrop.hide(() => { + document.body.classList.remove(CLASS_NAME_OPEN); + this._resetAdjustments(); + this._scrollBar.reset(); + EventHandler.trigger(this._element, EVENT_HIDDEN$4); + }); + } + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_FADE$3); + } + _triggerBackdropTransition() { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1); + if (hideEvent.defaultPrevented) { + return; + } + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + const initialOverflowY = this._element.style.overflowY; + // return if the following background transition hasn't yet completed + if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { + return; + } + if (!isModalOverflowing) { + this._element.style.overflowY = 'hidden'; + } + this._element.classList.add(CLASS_NAME_STATIC); + this._queueCallback(() => { + this._element.classList.remove(CLASS_NAME_STATIC); + this._queueCallback(() => { + this._element.style.overflowY = initialOverflowY; + }, this._dialog); + }, this._dialog); + this._element.focus(); + } + + /** + * The following methods are used to handle overflowing modals + */ + + _adjustDialog() { + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + const scrollbarWidth = this._scrollBar.getWidth(); + const isBodyOverflowing = scrollbarWidth > 0; + if (isBodyOverflowing && !isModalOverflowing) { + const property = isRTL() ? 'paddingLeft' : 'paddingRight'; + this._element.style[property] = `${scrollbarWidth}px`; + } + if (!isBodyOverflowing && isModalOverflowing) { + const property = isRTL() ? 'paddingRight' : 'paddingLeft'; + this._element.style[property] = `${scrollbarWidth}px`; + } + } + _resetAdjustments() { + this._element.style.paddingLeft = ''; + this._element.style.paddingRight = ''; + } + + // Static + static jQueryInterface(config, relatedTarget) { + return this.each(function () { + const data = Modal.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](relatedTarget); + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + EventHandler.one(target, EVENT_SHOW$4, showEvent => { + if (showEvent.defaultPrevented) { + // only register focus restorer if modal will actually get shown + return; + } + EventHandler.one(target, EVENT_HIDDEN$4, () => { + if (isVisible(this)) { + this.focus(); + } + }); + }); + + // avoid conflict when clicking modal toggler while another one is open + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1); + if (alreadyOpen) { + Modal.getInstance(alreadyOpen).hide(); + } + const data = Modal.getOrCreateInstance(target); + data.toggle(this); + }); + enableDismissTrigger(Modal); + + /** + * jQuery + */ + + defineJQueryPlugin(Modal); + + /** + * -------------------------------------------------------------------------- + * Bootstrap offcanvas.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$6 = 'offcanvas'; + const DATA_KEY$3 = 'bs.offcanvas'; + const EVENT_KEY$3 = `.${DATA_KEY$3}`; + const DATA_API_KEY$1 = '.data-api'; + const EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`; + const ESCAPE_KEY = 'Escape'; + const CLASS_NAME_SHOW$3 = 'show'; + const CLASS_NAME_SHOWING$1 = 'showing'; + const CLASS_NAME_HIDING = 'hiding'; + const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'; + const OPEN_SELECTOR = '.offcanvas.show'; + const EVENT_SHOW$3 = `show${EVENT_KEY$3}`; + const EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`; + const EVENT_HIDE$3 = `hide${EVENT_KEY$3}`; + const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`; + const EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`; + const EVENT_RESIZE = `resize${EVENT_KEY$3}`; + const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`; + const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`; + const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle="offcanvas"]'; + const Default$5 = { + backdrop: true, + keyboard: true, + scroll: false + }; + const DefaultType$5 = { + backdrop: '(boolean|string)', + keyboard: 'boolean', + scroll: 'boolean' + }; + + /** + * Class definition + */ + + class Offcanvas extends BaseComponent { + constructor(element, config) { + super(element, config); + this._isShown = false; + this._backdrop = this._initializeBackDrop(); + this._focustrap = this._initializeFocusTrap(); + this._addEventListeners(); + } + + // Getters + static get Default() { + return Default$5; + } + static get DefaultType() { + return DefaultType$5; + } + static get NAME() { + return NAME$6; + } + + // Public + toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + } + show(relatedTarget) { + if (this._isShown) { + return; + } + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, { + relatedTarget + }); + if (showEvent.defaultPrevented) { + return; + } + this._isShown = true; + this._backdrop.show(); + if (!this._config.scroll) { + new ScrollBarHelper().hide(); + } + this._element.setAttribute('aria-modal', true); + this._element.setAttribute('role', 'dialog'); + this._element.classList.add(CLASS_NAME_SHOWING$1); + const completeCallBack = () => { + if (!this._config.scroll || this._config.backdrop) { + this._focustrap.activate(); + } + this._element.classList.add(CLASS_NAME_SHOW$3); + this._element.classList.remove(CLASS_NAME_SHOWING$1); + EventHandler.trigger(this._element, EVENT_SHOWN$3, { + relatedTarget + }); + }; + this._queueCallback(completeCallBack, this._element, true); + } + hide() { + if (!this._isShown) { + return; + } + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3); + if (hideEvent.defaultPrevented) { + return; + } + this._focustrap.deactivate(); + this._element.blur(); + this._isShown = false; + this._element.classList.add(CLASS_NAME_HIDING); + this._backdrop.hide(); + const completeCallback = () => { + this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING); + this._element.removeAttribute('aria-modal'); + this._element.removeAttribute('role'); + if (!this._config.scroll) { + new ScrollBarHelper().reset(); + } + EventHandler.trigger(this._element, EVENT_HIDDEN$3); + }; + this._queueCallback(completeCallback, this._element, true); + } + dispose() { + this._backdrop.dispose(); + this._focustrap.deactivate(); + super.dispose(); + } + + // Private + _initializeBackDrop() { + const clickCallback = () => { + if (this._config.backdrop === 'static') { + EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); + return; + } + this.hide(); + }; + + // 'static' option will be translated to true, and booleans will keep their value + const isVisible = Boolean(this._config.backdrop); + return new Backdrop({ + className: CLASS_NAME_BACKDROP, + isVisible, + isAnimated: true, + rootElement: this._element.parentNode, + clickCallback: isVisible ? clickCallback : null + }); + } + _initializeFocusTrap() { + return new FocusTrap({ + trapElement: this._element + }); + } + _addEventListeners() { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { + if (event.key !== ESCAPE_KEY) { + return; + } + if (this._config.keyboard) { + this.hide(); + return; + } + EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); + }); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Offcanvas.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](this); + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + if (isDisabled(this)) { + return; + } + EventHandler.one(target, EVENT_HIDDEN$3, () => { + // focus on trigger when it is closed + if (isVisible(this)) { + this.focus(); + } + }); + + // avoid conflict when clicking a toggler of an offcanvas, while another is open + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR); + if (alreadyOpen && alreadyOpen !== target) { + Offcanvas.getInstance(alreadyOpen).hide(); + } + const data = Offcanvas.getOrCreateInstance(target); + data.toggle(this); + }); + EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => { + for (const selector of SelectorEngine.find(OPEN_SELECTOR)) { + Offcanvas.getOrCreateInstance(selector).show(); + } + }); + EventHandler.on(window, EVENT_RESIZE, () => { + for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) { + if (getComputedStyle(element).position !== 'fixed') { + Offcanvas.getOrCreateInstance(element).hide(); + } + } + }); + enableDismissTrigger(Offcanvas); + + /** + * jQuery + */ + + defineJQueryPlugin(Offcanvas); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/sanitizer.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + // js-docs-start allow-list + const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + const DefaultAllowlist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + dd: [], + div: [], + dl: [], + dt: [], + em: [], + hr: [], + h1: [], + h2: [], + h3: [], + h4: [], + h5: [], + h6: [], + i: [], + img: ['src', 'srcset', 'alt', 'title', 'width', 'height'], + li: [], + ol: [], + p: [], + pre: [], + s: [], + small: [], + span: [], + sub: [], + sup: [], + strong: [], + u: [], + ul: [] + }; + // js-docs-end allow-list + + const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']); + + /** + * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation + * contexts. + * + * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38 + */ + // eslint-disable-next-line unicorn/better-regex + const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i; + const allowedAttribute = (attribute, allowedAttributeList) => { + const attributeName = attribute.nodeName.toLowerCase(); + if (allowedAttributeList.includes(attributeName)) { + if (uriAttributes.has(attributeName)) { + return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue)); + } + return true; + } + + // Check if a regular expression validates the attribute. + return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName)); + }; + function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) { + if (!unsafeHtml.length) { + return unsafeHtml; + } + if (sanitizeFunction && typeof sanitizeFunction === 'function') { + return sanitizeFunction(unsafeHtml); + } + const domParser = new window.DOMParser(); + const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); + const elements = [].concat(...createdDocument.body.querySelectorAll('*')); + for (const element of elements) { + const elementName = element.nodeName.toLowerCase(); + if (!Object.keys(allowList).includes(elementName)) { + element.remove(); + continue; + } + const attributeList = [].concat(...element.attributes); + const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []); + for (const attribute of attributeList) { + if (!allowedAttribute(attribute, allowedAttributes)) { + element.removeAttribute(attribute.nodeName); + } + } + } + return createdDocument.body.innerHTML; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/template-factory.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$5 = 'TemplateFactory'; + const Default$4 = { + allowList: DefaultAllowlist, + content: {}, + // { selector : text , selector2 : text2 , } + extraClass: '', + html: false, + sanitize: true, + sanitizeFn: null, + template: '
' + }; + const DefaultType$4 = { + allowList: 'object', + content: 'object', + extraClass: '(string|function)', + html: 'boolean', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + template: 'string' + }; + const DefaultContentType = { + entry: '(string|element|function|null)', + selector: '(string|element)' + }; + + /** + * Class definition + */ + + class TemplateFactory extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + } + + // Getters + static get Default() { + return Default$4; + } + static get DefaultType() { + return DefaultType$4; + } + static get NAME() { + return NAME$5; + } + + // Public + getContent() { + return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean); + } + hasContent() { + return this.getContent().length > 0; + } + changeContent(content) { + this._checkContent(content); + this._config.content = { + ...this._config.content, + ...content + }; + return this; + } + toHtml() { + const templateWrapper = document.createElement('div'); + templateWrapper.innerHTML = this._maybeSanitize(this._config.template); + for (const [selector, text] of Object.entries(this._config.content)) { + this._setContent(templateWrapper, text, selector); + } + const template = templateWrapper.children[0]; + const extraClass = this._resolvePossibleFunction(this._config.extraClass); + if (extraClass) { + template.classList.add(...extraClass.split(' ')); + } + return template; + } + + // Private + _typeCheckConfig(config) { + super._typeCheckConfig(config); + this._checkContent(config.content); + } + _checkContent(arg) { + for (const [selector, content] of Object.entries(arg)) { + super._typeCheckConfig({ + selector, + entry: content + }, DefaultContentType); + } + } + _setContent(template, content, selector) { + const templateElement = SelectorEngine.findOne(selector, template); + if (!templateElement) { + return; + } + content = this._resolvePossibleFunction(content); + if (!content) { + templateElement.remove(); + return; + } + if (isElement$1(content)) { + this._putElementInTemplate(getElement(content), templateElement); + return; + } + if (this._config.html) { + templateElement.innerHTML = this._maybeSanitize(content); + return; + } + templateElement.textContent = content; + } + _maybeSanitize(arg) { + return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg; + } + _resolvePossibleFunction(arg) { + return execute(arg, [this]); + } + _putElementInTemplate(element, templateElement) { + if (this._config.html) { + templateElement.innerHTML = ''; + templateElement.append(element); + return; + } + templateElement.textContent = element.textContent; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap tooltip.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$4 = 'tooltip'; + const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']); + const CLASS_NAME_FADE$2 = 'fade'; + const CLASS_NAME_MODAL = 'modal'; + const CLASS_NAME_SHOW$2 = 'show'; + const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'; + const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`; + const EVENT_MODAL_HIDE = 'hide.bs.modal'; + const TRIGGER_HOVER = 'hover'; + const TRIGGER_FOCUS = 'focus'; + const TRIGGER_CLICK = 'click'; + const TRIGGER_MANUAL = 'manual'; + const EVENT_HIDE$2 = 'hide'; + const EVENT_HIDDEN$2 = 'hidden'; + const EVENT_SHOW$2 = 'show'; + const EVENT_SHOWN$2 = 'shown'; + const EVENT_INSERTED = 'inserted'; + const EVENT_CLICK$1 = 'click'; + const EVENT_FOCUSIN$1 = 'focusin'; + const EVENT_FOCUSOUT$1 = 'focusout'; + const EVENT_MOUSEENTER = 'mouseenter'; + const EVENT_MOUSELEAVE = 'mouseleave'; + const AttachmentMap = { + AUTO: 'auto', + TOP: 'top', + RIGHT: isRTL() ? 'left' : 'right', + BOTTOM: 'bottom', + LEFT: isRTL() ? 'right' : 'left' + }; + const Default$3 = { + allowList: DefaultAllowlist, + animation: true, + boundary: 'clippingParents', + container: false, + customClass: '', + delay: 0, + fallbackPlacements: ['top', 'right', 'bottom', 'left'], + html: false, + offset: [0, 6], + placement: 'top', + popperConfig: null, + sanitize: true, + sanitizeFn: null, + selector: false, + template: '', + title: '', + trigger: 'hover focus' + }; + const DefaultType$3 = { + allowList: 'object', + animation: 'boolean', + boundary: '(string|element)', + container: '(string|element|boolean)', + customClass: '(string|function)', + delay: '(number|object)', + fallbackPlacements: 'array', + html: 'boolean', + offset: '(array|string|function)', + placement: '(string|function)', + popperConfig: '(null|object|function)', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + selector: '(string|boolean)', + template: 'string', + title: '(string|element|function)', + trigger: 'string' + }; + + /** + * Class definition + */ + + class Tooltip extends BaseComponent { + constructor(element, config) { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)'); + } + super(element, config); + + // Private + this._isEnabled = true; + this._timeout = 0; + this._isHovered = null; + this._activeTrigger = {}; + this._popper = null; + this._templateFactory = null; + this._newContent = null; + + // Protected + this.tip = null; + this._setListeners(); + if (!this._config.selector) { + this._fixTitle(); + } + } + + // Getters + static get Default() { + return Default$3; + } + static get DefaultType() { + return DefaultType$3; + } + static get NAME() { + return NAME$4; + } + + // Public + enable() { + this._isEnabled = true; + } + disable() { + this._isEnabled = false; + } + toggleEnabled() { + this._isEnabled = !this._isEnabled; + } + toggle() { + if (!this._isEnabled) { + return; + } + this._activeTrigger.click = !this._activeTrigger.click; + if (this._isShown()) { + this._leave(); + return; + } + this._enter(); + } + dispose() { + clearTimeout(this._timeout); + EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); + if (this._element.getAttribute('data-bs-original-title')) { + this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title')); + } + this._disposePopper(); + super.dispose(); + } + show() { + if (this._element.style.display === 'none') { + throw new Error('Please use show on visible elements'); + } + if (!(this._isWithContent() && this._isEnabled)) { + return; + } + const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2)); + const shadowRoot = findShadowRoot(this._element); + const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element); + if (showEvent.defaultPrevented || !isInTheDom) { + return; + } + + // TODO: v6 remove this or make it optional + this._disposePopper(); + const tip = this._getTipElement(); + this._element.setAttribute('aria-describedby', tip.getAttribute('id')); + const { + container + } = this._config; + if (!this._element.ownerDocument.documentElement.contains(this.tip)) { + container.append(tip); + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED)); + } + this._popper = this._createPopper(tip); + tip.classList.add(CLASS_NAME_SHOW$2); + + // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.on(element, 'mouseover', noop); + } + } + const complete = () => { + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2)); + if (this._isHovered === false) { + this._leave(); + } + this._isHovered = false; + }; + this._queueCallback(complete, this.tip, this._isAnimated()); + } + hide() { + if (!this._isShown()) { + return; + } + const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2)); + if (hideEvent.defaultPrevented) { + return; + } + const tip = this._getTipElement(); + tip.classList.remove(CLASS_NAME_SHOW$2); + + // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.off(element, 'mouseover', noop); + } + } + this._activeTrigger[TRIGGER_CLICK] = false; + this._activeTrigger[TRIGGER_FOCUS] = false; + this._activeTrigger[TRIGGER_HOVER] = false; + this._isHovered = null; // it is a trick to support manual triggering + + const complete = () => { + if (this._isWithActiveTrigger()) { + return; + } + if (!this._isHovered) { + this._disposePopper(); + } + this._element.removeAttribute('aria-describedby'); + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2)); + }; + this._queueCallback(complete, this.tip, this._isAnimated()); + } + update() { + if (this._popper) { + this._popper.update(); + } + } + + // Protected + _isWithContent() { + return Boolean(this._getTitle()); + } + _getTipElement() { + if (!this.tip) { + this.tip = this._createTipElement(this._newContent || this._getContentForTemplate()); + } + return this.tip; + } + _createTipElement(content) { + const tip = this._getTemplateFactory(content).toHtml(); + + // TODO: remove this check in v6 + if (!tip) { + return null; + } + tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); + // TODO: v6 the following can be achieved with CSS only + tip.classList.add(`bs-${this.constructor.NAME}-auto`); + const tipId = getUID(this.constructor.NAME).toString(); + tip.setAttribute('id', tipId); + if (this._isAnimated()) { + tip.classList.add(CLASS_NAME_FADE$2); + } + return tip; + } + setContent(content) { + this._newContent = content; + if (this._isShown()) { + this._disposePopper(); + this.show(); + } + } + _getTemplateFactory(content) { + if (this._templateFactory) { + this._templateFactory.changeContent(content); + } else { + this._templateFactory = new TemplateFactory({ + ...this._config, + // the `content` var has to be after `this._config` + // to override config.content in case of popover + content, + extraClass: this._resolvePossibleFunction(this._config.customClass) + }); + } + return this._templateFactory; + } + _getContentForTemplate() { + return { + [SELECTOR_TOOLTIP_INNER]: this._getTitle() + }; + } + _getTitle() { + return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title'); + } + + // Private + _initializeOnDelegatedTarget(event) { + return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig()); + } + _isAnimated() { + return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2); + } + _isShown() { + return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2); + } + _createPopper(tip) { + const placement = execute(this._config.placement, [this, tip, this._element]); + const attachment = AttachmentMap[placement.toUpperCase()]; + return createPopper(this._element, tip, this._getPopperConfig(attachment)); + } + _getOffset() { + const { + offset + } = this._config; + if (typeof offset === 'string') { + return offset.split(',').map(value => Number.parseInt(value, 10)); + } + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element); + } + return offset; + } + _resolvePossibleFunction(arg) { + return execute(arg, [this._element]); + } + _getPopperConfig(attachment) { + const defaultBsPopperConfig = { + placement: attachment, + modifiers: [{ + name: 'flip', + options: { + fallbackPlacements: this._config.fallbackPlacements + } + }, { + name: 'offset', + options: { + offset: this._getOffset() + } + }, { + name: 'preventOverflow', + options: { + boundary: this._config.boundary + } + }, { + name: 'arrow', + options: { + element: `.${this.constructor.NAME}-arrow` + } + }, { + name: 'preSetPlacement', + enabled: true, + phase: 'beforeMain', + fn: data => { + // Pre-set Popper's placement attribute in order to read the arrow sizes properly. + // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement + this._getTipElement().setAttribute('data-popper-placement', data.state.placement); + } + }] + }; + return { + ...defaultBsPopperConfig, + ...execute(this._config.popperConfig, [defaultBsPopperConfig]) + }; + } + _setListeners() { + const triggers = this._config.trigger.split(' '); + for (const trigger of triggers) { + if (trigger === 'click') { + EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context.toggle(); + }); + } else if (trigger !== TRIGGER_MANUAL) { + const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1); + const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1); + EventHandler.on(this._element, eventIn, this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true; + context._enter(); + }); + EventHandler.on(this._element, eventOut, this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget); + context._leave(); + }); + } + } + this._hideModalHandler = () => { + if (this._element) { + this.hide(); + } + }; + EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); + } + _fixTitle() { + const title = this._element.getAttribute('title'); + if (!title) { + return; + } + if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) { + this._element.setAttribute('aria-label', title); + } + this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility + this._element.removeAttribute('title'); + } + _enter() { + if (this._isShown() || this._isHovered) { + this._isHovered = true; + return; + } + this._isHovered = true; + this._setTimeout(() => { + if (this._isHovered) { + this.show(); + } + }, this._config.delay.show); + } + _leave() { + if (this._isWithActiveTrigger()) { + return; + } + this._isHovered = false; + this._setTimeout(() => { + if (!this._isHovered) { + this.hide(); + } + }, this._config.delay.hide); + } + _setTimeout(handler, timeout) { + clearTimeout(this._timeout); + this._timeout = setTimeout(handler, timeout); + } + _isWithActiveTrigger() { + return Object.values(this._activeTrigger).includes(true); + } + _getConfig(config) { + const dataAttributes = Manipulator.getDataAttributes(this._element); + for (const dataAttribute of Object.keys(dataAttributes)) { + if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) { + delete dataAttributes[dataAttribute]; + } + } + config = { + ...dataAttributes, + ...(typeof config === 'object' && config ? config : {}) + }; + config = this._mergeConfigObj(config); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + _configAfterMerge(config) { + config.container = config.container === false ? document.body : getElement(config.container); + if (typeof config.delay === 'number') { + config.delay = { + show: config.delay, + hide: config.delay + }; + } + if (typeof config.title === 'number') { + config.title = config.title.toString(); + } + if (typeof config.content === 'number') { + config.content = config.content.toString(); + } + return config; + } + _getDelegateConfig() { + const config = {}; + for (const [key, value] of Object.entries(this._config)) { + if (this.constructor.Default[key] !== value) { + config[key] = value; + } + } + config.selector = false; + config.trigger = 'manual'; + + // In the future can be replaced with: + // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]]) + // `Object.fromEntries(keysWithDifferentValues)` + return config; + } + _disposePopper() { + if (this._popper) { + this._popper.destroy(); + this._popper = null; + } + if (this.tip) { + this.tip.remove(); + this.tip = null; + } + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Tooltip.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + } + + /** + * jQuery + */ + + defineJQueryPlugin(Tooltip); + + /** + * -------------------------------------------------------------------------- + * Bootstrap popover.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$3 = 'popover'; + const SELECTOR_TITLE = '.popover-header'; + const SELECTOR_CONTENT = '.popover-body'; + const Default$2 = { + ...Tooltip.Default, + content: '', + offset: [0, 8], + placement: 'right', + template: '', + trigger: 'click' + }; + const DefaultType$2 = { + ...Tooltip.DefaultType, + content: '(null|string|element|function)' + }; + + /** + * Class definition + */ + + class Popover extends Tooltip { + // Getters + static get Default() { + return Default$2; + } + static get DefaultType() { + return DefaultType$2; + } + static get NAME() { + return NAME$3; + } + + // Overrides + _isWithContent() { + return this._getTitle() || this._getContent(); + } + + // Private + _getContentForTemplate() { + return { + [SELECTOR_TITLE]: this._getTitle(), + [SELECTOR_CONTENT]: this._getContent() + }; + } + _getContent() { + return this._resolvePossibleFunction(this._config.content); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Popover.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + } + + /** + * jQuery + */ + + defineJQueryPlugin(Popover); + + /** + * -------------------------------------------------------------------------- + * Bootstrap scrollspy.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$2 = 'scrollspy'; + const DATA_KEY$2 = 'bs.scrollspy'; + const EVENT_KEY$2 = `.${DATA_KEY$2}`; + const DATA_API_KEY = '.data-api'; + const EVENT_ACTIVATE = `activate${EVENT_KEY$2}`; + const EVENT_CLICK = `click${EVENT_KEY$2}`; + const EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`; + const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; + const CLASS_NAME_ACTIVE$1 = 'active'; + const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]'; + const SELECTOR_TARGET_LINKS = '[href]'; + const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; + const SELECTOR_NAV_LINKS = '.nav-link'; + const SELECTOR_NAV_ITEMS = '.nav-item'; + const SELECTOR_LIST_ITEMS = '.list-group-item'; + const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`; + const SELECTOR_DROPDOWN = '.dropdown'; + const SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle'; + const Default$1 = { + offset: null, + // TODO: v6 @deprecated, keep it for backwards compatibility reasons + rootMargin: '0px 0px -25%', + smoothScroll: false, + target: null, + threshold: [0.1, 0.5, 1] + }; + const DefaultType$1 = { + offset: '(number|null)', + // TODO v6 @deprecated, keep it for backwards compatibility reasons + rootMargin: 'string', + smoothScroll: 'boolean', + target: 'element', + threshold: 'array' + }; + + /** + * Class definition + */ + + class ScrollSpy extends BaseComponent { + constructor(element, config) { + super(element, config); + + // this._element is the observablesContainer and config.target the menu links wrapper + this._targetLinks = new Map(); + this._observableSections = new Map(); + this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element; + this._activeTarget = null; + this._observer = null; + this._previousScrollData = { + visibleEntryTop: 0, + parentScrollTop: 0 + }; + this.refresh(); // initialize + } + + // Getters + static get Default() { + return Default$1; + } + static get DefaultType() { + return DefaultType$1; + } + static get NAME() { + return NAME$2; + } + + // Public + refresh() { + this._initializeTargetsAndObservables(); + this._maybeEnableSmoothScroll(); + if (this._observer) { + this._observer.disconnect(); + } else { + this._observer = this._getNewObserver(); + } + for (const section of this._observableSections.values()) { + this._observer.observe(section); + } + } + dispose() { + this._observer.disconnect(); + super.dispose(); + } + + // Private + _configAfterMerge(config) { + // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case + config.target = getElement(config.target) || document.body; + + // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only + config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin; + if (typeof config.threshold === 'string') { + config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value)); + } + return config; + } + _maybeEnableSmoothScroll() { + if (!this._config.smoothScroll) { + return; + } + + // unregister any previous listeners + EventHandler.off(this._config.target, EVENT_CLICK); + EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => { + const observableSection = this._observableSections.get(event.target.hash); + if (observableSection) { + event.preventDefault(); + const root = this._rootElement || window; + const height = observableSection.offsetTop - this._element.offsetTop; + if (root.scrollTo) { + root.scrollTo({ + top: height, + behavior: 'smooth' + }); + return; + } + + // Chrome 60 doesn't support `scrollTo` + root.scrollTop = height; + } + }); + } + _getNewObserver() { + const options = { + root: this._rootElement, + threshold: this._config.threshold, + rootMargin: this._config.rootMargin + }; + return new IntersectionObserver(entries => this._observerCallback(entries), options); + } + + // The logic of selection + _observerCallback(entries) { + const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`); + const activate = entry => { + this._previousScrollData.visibleEntryTop = entry.target.offsetTop; + this._process(targetElement(entry)); + }; + const parentScrollTop = (this._rootElement || document.documentElement).scrollTop; + const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop; + this._previousScrollData.parentScrollTop = parentScrollTop; + for (const entry of entries) { + if (!entry.isIntersecting) { + this._activeTarget = null; + this._clearActiveClass(targetElement(entry)); + continue; + } + const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; + // if we are scrolling down, pick the bigger offsetTop + if (userScrollsDown && entryIsLowerThanPrevious) { + activate(entry); + // if parent isn't scrolled, let's keep the first visible item, breaking the iteration + if (!parentScrollTop) { + return; + } + continue; + } + + // if we are scrolling up, pick the smallest offsetTop + if (!userScrollsDown && !entryIsLowerThanPrevious) { + activate(entry); + } + } + } + _initializeTargetsAndObservables() { + this._targetLinks = new Map(); + this._observableSections = new Map(); + const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target); + for (const anchor of targetLinks) { + // ensure that the anchor has an id and is not disabled + if (!anchor.hash || isDisabled(anchor)) { + continue; + } + const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element); + + // ensure that the observableSection exists & is visible + if (isVisible(observableSection)) { + this._targetLinks.set(decodeURI(anchor.hash), anchor); + this._observableSections.set(anchor.hash, observableSection); + } + } + } + _process(target) { + if (this._activeTarget === target) { + return; + } + this._clearActiveClass(this._config.target); + this._activeTarget = target; + target.classList.add(CLASS_NAME_ACTIVE$1); + this._activateParents(target); + EventHandler.trigger(this._element, EVENT_ACTIVATE, { + relatedTarget: target + }); + } + _activateParents(target) { + // Activate dropdown parents + if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { + SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1); + return; + } + for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) { + // Set triggered links parents as active + // With both