Blog

  • GCode-Parser-and-Viz

    G-code parsing and visualization in Blender

    This repository contains scripts for Blender (checked with 3.4.1 version) that allow you to parse and visualize single-material .gcode files generated with PrusaSlicer.

    Render Viewport

    *Model: “Cyberpunk Robots x6” by CharlieVet


    PrusaSlicer setup

    The sample g-code was created with PrusaSlicer-2.6.1. Put the ;LAYER_Z_HEIGHT=[layer_z] line in the “Before layer change G-code” in PrusaSlicer.

    G-code parsing

    PrusaSlicer 2.6.1 supports G-Code parser supports 12 types of nozzle movements:

    • (1) Perimeter
    • (2) External perimeter
    • (3) Overhang perimeter
    • (4) Internal infill
    • (5) Solid infill
    • (6) Top solid infill
    • (7) Bridge infill
    • (8) Support material
    • (9) Support material interface
    • (10) Skirt/Brim
    • (11) Wipe tower
    • (12) Custom

    The developed parser combines all perimeters as Shell, all infills – as Fill, and all supports – as Support.

    Setup Blender environment

    You can manually set light sources, camera settings, background images, HDRI maps, and materials. Alternatively, you can run certain scripts (“scene_setup_scripts” folder) to automate some parts of this process.

    Blender scene

    The script-based scene genetator picks the local “print_bed.jpg” file and scales it to real 210×210 mm Prusa Printer dimensions.

    Run in Blender

    To run it in Blender: (1) copy the code from the “blender_scripts” folder, (2) paste in a new script created in the “Scripting” tab, (3) update the “TODO” sections in the code, and (4) hit the “Run” button.


    When you run a script, Blender becomes unresponsive. Therefore, switch the system console window to be able to “Ctrl+C” (break) processing in case of any errors.

    Toggle system console Console window

    Add automatic animation

    Uncomment the animate_layers() function in the script.

    Visit original content creator repository https://github.com/apetsiuk/GCode-Parser-and-Viz
  • FeedR

    FeedR

    FeedR

    Sample (and rather simple) .NET6 microservices solution which acts as the data aggregator for the different feeds. The purpose of this solution is to explore the latest framework and play with different patterns, tools & libraries that can be useful when building distributed applications (but not only). The overall repository structure consists of the following projects located under src directory:

    • Gateway – API gateway providing a public facade for the underlying, internal services
    • Aggregator – core service responsible for aggregating the data from different feeds
    • Notifier – supporting service responsible for sending the notifications
    • Feeds
      • News – sample feed providing the worldwide news
      • Quotes – sample feed providing the quotes (e.g. currency)
      • Weather – sample feed providing the weather related data

    Episodes

    The implementation process can be found on our DevMentors YouTube channel, where we publish the videos about building this project. You can navigate through this repository via specific episode tags which are related to the videos.

    Requirements

    To start the infrastructure via Docker, type the following command at the compose directory:

    docker compose -f infrastructure.yml up -d

    Each application can be started separately using dotnet CLI or your favorite IDE.

    Visit original content creator repository https://github.com/devmentors/FeedR
  • Rx2Animations

    Rx2Animations

    Download API

    RxAnimations is a library with the main goal to make android animations more solid and cohesive.

    Download

      compile 'oxim.digital:rx2anim:0.9.1'
        
      compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
      compile 'io.reactivex.rxjava2:rxjava:2.0.6'

    RxJava version compatibility:

    This Rx2Animations library is only compatible with rxJava 2.

    If you are searching for the one compatible with rxJava 1, take a look over here.

    Usage

    A sample project which provides code examples that demonstrate uses of the classes in this project is available in the sample-app/ folder.

    True power of this library is the ability to use animations as an observable asynchronous action.
    That enables us to apply regular rxJava APIs on the animations.

    It provides a couple of classes such as RxValueAnimator, RxValueObservable and RxAnimationBuilder.
    Moreover, it also provides many regulary used animation methods (static import) such as fadeIn(), fadeOut(), slideIn(), leave() etc.

    Examples:

    • Animating multiple views together
            animateTogether(fadeIn(firstView),
                            fadeIn(secondView));
    • Chaining multiple animations seamlessly
            fadeIn(firstView)
                .concatWith(fadeIn(secondView));
    • Simple ValueAnimator usage with RxValueAnimator
            final ValueAnimator opacityAnimator = ValueAnimator.ofFloat(0.f, 1.f);
            RxValueAnimator.from(opacityAnimator, animator -> view.setAlpha((float)animator.getAnimatedValue()))
    • Animating multiple values together with RxValueObservable
            xObservableAnimator = RxValueObservable.from(xAnimator);
            yObservableAnimator = RxValueObservable.from(yAnimator);
            
            Observable.combineLatest(xObservableAnimator.subscribe(),
                                     yObservableAnimator.subscribe(),
                                     (first, second) -> new Pair(first, second))
                      .subscribe(this::updateView);
    • Defining custom animations with RxAnimationBuilder
            RxAnimationBuilder.animate(view, DURATION)
                              .interpolator(INTERPOLATOR)
                              .fadeIn()
                              .rotate(DEGREES)
                              .translateBy(X, Y)
                              ...
                              .schedule([ | false]);

    Animation created with RxAnimationBuilder automatically pretransforms the view, if not set otherwise.
    I.e. if fadeIn() is called, views opacity will be set to 0.f before animation starts.

    Managing animations

    Starting animation – Every animation chain is started when we subscribe to it.
    Ending animation – Animations can be easily stopped by unsubscribing animation subscription.

    LICENSE

    Copyright 2016 Mihael Franceković
    
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    
        http://www.apache.org/licenses/LICENSE-2.0
    
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
    
    Visit original content creator repository https://github.com/0ximDigital/Rx2Animations
  • CICD-in-RedHat-OpenShift

    archi

    Red Hat® OpenShift® is an enterprise-ready Kubernetes container platform with full-stack automated operations to manage hybrid cloud and multicloud deployments. Red Hat OpenShift is optimized to improve developer productivity and promote innovation

    CI/CD is one of the popular use-cases for OpenShift Container Platform. OpenShift scales the pipeline execution through on-demand provisioning in containers. OpenShift provides an end-to-end solution for building complete deployment pipelines and removes the wait time for running builds in large projects

    Steps

    1. Important Terminologies
    2. Understand CICD Workflow in RedHat OpenShift
    3. Fork the GitHub repository
    4. Get RedHat OpenShift CodeLab
    5. Deploy an application using S2I Method
    6. Update some code to check continuous Integration and Delivery

    Step 1. Important Terminologies

    Source-to-Image: Developers do not need to be experts in Dockerfiles or either operating system commands. S2I is responsible to create resources for developers, we only need to pass our central repository URL.

    Build Config: The build config is responsible to fetch the code from central repo and build an image in OpenShift and push them in the internal Docker registry.

    Deploy Config: Once the image is successfully built then deploy config is responsible for deploying the application in pods into OpenShift.

    Step 2. Understand CICD Workflow in RedHat OpenShift

    workflow

    1. First, when we pass oc new-app it will create a Build Configuration.
    2. It will create a Builder pod that clones the application from the central repo.
    3. Once the source code successfully cloned it will create an image
    4. Builder pod pushes the image to the external and internal registry.
    5. Check if the image is created from the available images in OpenShift or need to create a fresh image layer.
    6. S2I image stream will check if there are any changes in the image.
    7. The change will be notified to Build Config
    8. Once the complete process of Build is done then comes the responsibility of Deployment Configuration to create the pods in OpenShift depending on the image specifications.

    Step 3. Fork the GitHub repository

    1. Create IBM Account (optional)
    2. Open this repo
    3. And then click on Fork on the top right.

    fork

    NOTE : This is a important step because in lab you will provide your fork repo or else change in the code will not trigger.

    Step 4. Get RedHat OpenShift CodeLab

    1. Get a free lab from here
    2. Click on Access the OpenShift Lab and you will be redirected to the lab page (wait it will take some time to provision the lab resources)
    3. Once it is done you can see the below page.

    lab

    Step 5. Deploy an application using S2I Method

    1. Click on Terminal on top.
    2. Run command oc status to check cluster is provisioned successfully, the current project as sn-labs-mahsankhan .(in your case it will be different)
    3. Run command oc new-app --name=bankapplication https://github.com/mahsankhaan/CICD-in-RedHat-OpenShift.git to deploy the application (Please note GitHub URL will be your fork URL done in Step 3)
    4. Build will start and you can see the logs by oc logs -f bc/bankapplication
    5. Run command oc get pods -w and can verify build and deploy stages are successfully complete and one pod is running bankapplication-1-n6ddx (in your case it will be different)
    6. Pass command oc expose svc/bankapplication to expose the application to the world.
    7. Now get the route of the application oc get routes. Open the host (bankapplication-sn-labs-mahsankhan.labs-user-sandbox-…) in the browser and check bank application is up and running.

    admin

    Step 6. Update some code to check continuous Integration and Delivery

    1. Open the repo (in your case kindly open your Fork repo) this is important because you can’t make a change in this repo directly.
    2. Open Folder views -> admin_login.ejs (Open File at line 141 make it WELCOME ADMIN LOGIN), Click commit changes.
    3. Now again open the terminal and pass command oc start-build bankapplication now complete workflow will take place as discussed in Step 2 .
    4. Again run the command oc get pods and now can check a new build-2 is running. A newly updated pod would be created bankapplication-2-vb8rg and the previous one would be deleted.
    5. Open the same URL again and can verify the update.
    Visit original content creator repository https://github.com/mahsankhaan/CICD-in-RedHat-OpenShift
  • terraform-azure-cli

    lint-dockerfile build-test push-latest release

    Update Docker Hub Description License Docker Pulls

    Terraform and Azure CLI Docker image

    📦 Supported tags and respective Dockerfile links

    Available image tags can be found on the Docker Hub registry: zenika/terraform-azure-cli

    Supported versions are listed in the supported_versions.json file in project root folder.

    The following image tag strategy is applied:

    • zenika/terraform-azure-cli:latest – build from master
    • zenika/terraform-azure-cli:release-S.T_terraform-UU.VV.WW_azcli-XX.YY.ZZ – build from releases
      • release-S.T is the release tag
      • terraform-UU.VV.WWW is the included Terraform CLI version
      • azcli-XX.YY.ZZ is the included Azure CLI version

    Please report to the releases page for the changelogs.

    Any other tags are not supported even if available.

    💡 Motivation

    The goal is to create a minimalist and lightweight image with these tools in order to reduce network and storage impact.

    This image gives you the flexibility to be used either for development or as a base image as you see fits.

    🔧 What’s inside ?

    🚀 Usage

    🐚 Launch the CLI

    Simply launch the container and use the CLI as you would on any other platform, for instance using the latest image:

    docker container run -it --rm --mount type=bind,source="$(pwd)",target=/workspace zenika/terraform-azure-cli:latest

    The --rm flag will completely destroy the container and its data on exit.

    ⚙️ Build the image

    You can build the image locally directly from the Dockerfile, using the build script.

    It will :

    # launch dev script using latest supported versions for both Azure and Terraform CLI
    ./dev.sh

    Optionally, it is possible to choose the tools desired versions:

    # Set desired tool versions
    AZURE_CLI_VERSION=2.24.2
    TERRAFORM_VERSION=0.15.5
    
    # launch dev script with parameters
    ./dev.sh $AZURE_CLI_VERSION $TERRAFORM_VERSION

    🙏 Roadmap & Contributions

    Please refer to the github project to track new features.

    Do not hesitate to contribute by filling an issue or opening a PR !

    ⬆️ Dependencies upgrades checklist

    🚩 Similar repositories

    📖 License

    This project is under the Apache License 2.0

    with love by zenika

    Visit original content creator repository https://github.com/zenika-open-source/terraform-azure-cli
  • status

    HTTP PhpSlides\Status

    Installation

    After creating your PhpSlides project, navigate to the project directory and install PhpSlides-Status package using this command below:

    composer require phpslides/status

    Or download the zip file directly from the released version, ectract the file and add it to a folder 📂 in your PhpSlides project.

    Download phpslides\status zip

    Quick Start

    Using Status() class function

    Create a Status instance for API response. Which is used in PhpSlides API Controller Class

    <?php
    
    namespace App\Controller\Api;
    
    use PhpSlides\Status;
    use PhpSlides\Http\Interface\ApiController;
    
    final class UserController implements ApiController
    {
       public function index() {
          $status = new Status();
       }
    }
    
    ?>

    You can pass a string value to the Status() function parameter which is by default ResponseType::JSON using the Response namespace PhpSlides\Enums\ResponseType

    In returning value in JSON format. The parameter includes this enum value type:

    use PhpSlides\Enums\ResponseType;
    
    ResponseType::JSON;
    ResponseType::HTML;
    ResponseType::CSV;
    ResponseType::XML;
    
    new Status(ResponseType::JSON);

    If the parameters contain any value apart from the enum ResponseType value it’ll return default value an array form, which isn’t recommended.

    Some Functions & Methods

    success() method

    Returning a default success message, using the success() method.

    <?php
    
    namespace App\Controller\Api;
    
    use PhpSlides\Status;
    use PhpSlides\Enums\ResponseType;
    use PhpSlides\Http\Interface\ApiController;
    
    final class UserController implements ApiController {
       public function index() {
          $status = new Status(Response::JSON);
          return $status->success();
       }
    }
    
    ?>

    The success() method takes 2 parameters, $data to render and $status which is the status code.

    The first parameter can be either Array or String and the second parameter is an Integer from StatusCode static class. It returns ResponseType::<type> which is passed as a parameter in the Status() function.

    use PhpSlides\StatusCode;
    
    $user = [
        "name": "John Doe",
        "email": "john@doe.com"
    ];
    return (new Status())->success($user, StatusCode::OK);

    error() method

    Returning an error Api message using the error() method It also takes 2 parameters, the first is either an Array or String and the second which is interger for setting http_response_code, it has default value of StatusCode::INTERNAL_SERVER_ERROR which is 500

    It also returns ResponseType::<type>

    return (new Status(ResponseType::JSON))->error('User not Found', StatusCode::NOT_FOUND);

    Full code for success() & error() methods

    If no parameter is specified in the Status(), by default it’s returning ResponseType::JSON for returning response in JSON format

    <?php
    
    namespace App\Controller\Api;
    
    use PhpSlides\Http\Interface\ApiController;
    use PhpSlides\Http\ResponseType;
    use PhpSlides\StatusCode;
    use PhpSlides\Status;
    
    final class UserController extends Controller
    {
       public function index(int $user_id) {
          $status = new Status();
    
          if ($user_id === 1) {
             $user = [
                 'name': 'John Doe',
                 'email': 'john@doe.com',
                 'user_id': $user_id
             ];
    
             $response = $status->success($user); // by default the second parameter is `StatusCode::OK`
          } else {
             // not found message
             $response = $status->error("User user_id=$user_id is not found", StatusCode::NOT_FOUND);
          }
    
          return $response; // return message as a JSON format `Response::JSON`
       }
    }
    
    ?>

    Namespace and Status Interface

    namespace

    \PhpSlides\Status()

    \PhpSlides\StatusCode

    \PhpSlides\StatusText

    \PhpSlides\Http\Response

    \PhpSlides\Enums\ResponseType

    \PhpSlides\Interface\StatusInterface

    \PhpSlides\Interface\ResponseInterface

    \PhpSlides\Status\Exception\ApiException()

    \PhpSlides\Status\Exception\ExceptionInterface

    Status() Interface Methods

    __construct(string $response = ResponseType::JSON)

    public function getStatus (): int;

    public function getStatusText (): string;

    public function getMessage (): mixed;

    public function get (): string|array;

    public function getJson (): string;

    public function set (mixed $data, int $status = StatusCode::NO_CONTENT, string $statusText = StatusText::NO_CONTENT ): void;

    public function setStatus (int $status): void;

    public function setStatusText (string $statusText): void;

    public function setMessage (mixed $message): void;

    public function error (array|string $data, int $status = StatusCode::INTERNAL_SERVER_ERROR): string|array;

    public function success (array|string $data, int $status = StatusCode::OK): string|array;

    Response{} Interface Method

    public static function json(array $data = [], int $status = StatusCode::OK): string;

    public static function html(array $data = [], int $status = StatusCode::OK): string;

    public static function csv(array $data = [], int $status = StatusCode::OK): string;

    public static function xml(array $data = [], int $status = StatusCode::OK): string;

    public static function array(array $data = [], int $status = StatusCode::UNSUPPORTED_MEDIA_TYPE): array;

    enum ResponseType{} Interface

    const JSON = 'JSON';

    const HTML = 'HTML';

    const CSV = 'CSV';

    const XML = 'XML';

    DOCUMENTATION

    Status Class Methods

    The Status class provides several methods to manage and format API responses in different formats such as JSON, HTML, CSV, or XML. Here’s a quick guide on how to use them.

    __construct(string $response = ResponseType::JSON)

    The constructor initializes a new Status instance. You can specify the response format by passing a ResponseType enum value. If no value is passed, it defaults to ResponseType::JSON.

    $status = new Status(); // Defaults to JSON
    $status = new Status(ResponseType::HTML); // Initializes with HTML response type

    getStatus(): int

    This method retrieves the current HTTP status code.

    $httpStatus = $status->getStatus(); // Returns the current HTTP status code

    getStatusText(): string

    Retrieves the status text corresponding to the HTTP status code.

    $statusText = $status->getStatusText(); // Returns status text, e.g., "OK" for 200

    getMessage(): mixed

    Gets the message set for the response. This could be an array, string, or any data type depending on the response.

    $message = $status->getMessage(); // Returns the message set in the response

    get(): string|array

    Retrieves the response data in its raw form, either as a string or an array.

    $response = $status->get(); // Returns the raw response data

    getJson(): string

    Converts the response data to a JSON string. This method is useful when you need to explicitly get the response in JSON format.

    $jsonResponse = $status->getJson(); // Returns the response as a JSON string

    set(mixed $data, int $status = StatusCode::NO_CONTENT, string $statusText = StatusText::NO_CONTENT): void

    Sets the response data, status code, and status text. This method is used to manually define the response properties.

    $status->set(['key' => 'value'], StatusCode::OK, StatusText::OK); // Sets custom response data

    setStatus(int $status): void

    Sets the HTTP status code.

    $status->setStatus(StatusCode::OK); // Manually set the status code

    setStatusText(string $statusText): void

    Sets the status text corresponding to the HTTP status code.

    $status->setStatusText(StatusText::OK); // Manually set the status text

    setMessage(mixed $message): void

    Sets the message for the response. This message can be an array, string, or any other data type.

    $status->setMessage('Success message'); // Manually set the response message

    error(array|string $data, int $status = StatusCode::INTERNAL_SERVER_ERROR): string|array

    Creates an error response. You can pass the error message and an optional status code. By default, it sets the status code to 500 (Internal Server Error).

    $errorResponse = $status->error('An error occurred', StatusCode::BAD_REQUEST); // Returns error response in JSON

    success(array|string $data, int $status = StatusCode::OK): string|array

    Creates a success response. You can pass the response data and an optional status code. By default, it sets the status code to 200 (OK).

    $successResponse = $status->success(['message' => 'Operation successful']); // Returns success response in JSON

    Response Class Methods

    The Response class provides static methods to directly return responses in different formats. These methods are especially useful when you want to quickly output data without creating a Status instance.

    json(array $data = [], int $status = StatusCode::OK): string

    Returns the data in JSON format with the specified HTTP status code.

    $response = Response::json(['key' => 'value'], StatusCode::CREATED); // Outputs JSON response

    html(array $data = [], int $status = StatusCode::OK): string

    Returns the data formatted as HTML.

    $response = Response::html(['key' => 'value'], StatusCode::OK); // Outputs HTML response

    csv(array $data = [], int $status = StatusCode::OK): string

    Returns the data formatted as CSV.

    $response = Response::csv(['key' => 'value'], StatusCode::OK); // Outputs CSV response

    xml(array $data = [], int $status = StatusCode::OK): string

    Returns the data formatted as XML.

    $response = Response::xml(['key' => 'value'], StatusCode::OK); // Outputs XML response

    array(array $data = [], int $status = StatusCode::UNSUPPORTED_MEDIA_TYPE): array

    Returns the data as a PHP array. This is a fallback option if none of the other formats is specified.

    $response = Response::array(['key' => 'value']); // Outputs data as an array

    enum ResponseType

    The ResponseType enum provides predefined constants for the supported response formats. These include:

    • ResponseType::JSON – For JSON format
    • ResponseType::HTML – For HTML format
    • ResponseType::CSV – For CSV format
    • ResponseType::XML – For XML format

    Use these constants when specifying the response type in the Status class.

    $status = new Status(ResponseType::XML); // Initializes the Status class with XML response type
    Visit original content creator repository https://github.com/PhpSlides/status
  • neoforum

    Neoforum

    Basic validation

    This API is inspired by Neoboards from Neopets. It provides endpoints to:

    • Consult Boards: Retrieve information about various boards;
    • Consult Topics: Access details of topics within boards;
    • Consult Answers: View responses to topics and answers;
    • Update Author Information: Modify author profiles and related data;
    • And much more!

    Index

    Stack

    • Spring Boot 3
    • Flywaydb
    • JPA
    • Mysql 9
    • Redis
    • Junit 5
    • Mockk
    • Rest
    • Actuator
    • Prometheus

    Prerequisites

    • Java 21
    • Kotlin 1.9
    • Maven
    • Docker

    Setting Up the Environment

    You need to run Docker containers for Redis and MySQL. Follow the instructions below to run each container.

    Run MySQL Container

    To start a MySQL container, run the following command:

    docker run --name mysql -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=neoforum -p 3306:3306 -d mysql:9

    Run Redis Container

    To start a Redis container, run the following command:

    docker run --name redis -p 6379:6379 -d redis:latest

    Build the Project

    To build the project, run the following Maven command:

    ./mvnw clean install

    Running the Application

    To run the Spring Boot application, use the following command:

    ./mvnw spring-boot:run

    Tests

    To run tests, you must use Maven as well. To execute all unit tests, run:

    ./mvnw test

    Viewing Documentation

    The Swagger API documentation is available once the application is running. By default, the Swagger UI can be accessed at:

    http://localhost:8080/swagger-ui/index.html

    Using Docker

    To run the application using Docker, follow these steps:

    1. Pull the Docker Image

    Pull the Docker image from Docker Hub using the following command:

    Heroku

    docker pull matheusmagal/neoforum:v2

    Openshift

    docker pull matheusmagal/neoforum-dev:v1

    2. Run the Docker Container

    Run the Docker container using the following command:

    Heroku

    docker run -p 8080:8080 matheusmagal/neoforum

    Openshift

    docker run -p 8080:8080 matheusmagal/neoforum-dev

    Accessing the Application in Production

    The application is hosted and accessible online. You can view the live version of the application at: Neoforum API.

    Visit original content creator repository https://github.com/MatheusMFranco/neoforum
  • neoforum

    Neoforum

    Basic validation

    This API is inspired by Neoboards from Neopets. It provides endpoints to:

    • Consult Boards: Retrieve information about various boards;
    • Consult Topics: Access details of topics within boards;
    • Consult Answers: View responses to topics and answers;
    • Update Author Information: Modify author profiles and related data;
    • And much more!

    Index

    Stack

    • Spring Boot 3
    • Flywaydb
    • JPA
    • Mysql 9
    • Redis
    • Junit 5
    • Mockk
    • Rest
    • Actuator
    • Prometheus

    Prerequisites

    • Java 21
    • Kotlin 1.9
    • Maven
    • Docker

    Setting Up the Environment

    You need to run Docker containers for Redis and MySQL. Follow the instructions below to run each container.

    Run MySQL Container

    To start a MySQL container, run the following command:

    docker run --name mysql -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=neoforum -p 3306:3306 -d mysql:9

    Run Redis Container

    To start a Redis container, run the following command:

    docker run --name redis -p 6379:6379 -d redis:latest

    Build the Project

    To build the project, run the following Maven command:

    ./mvnw clean install

    Running the Application

    To run the Spring Boot application, use the following command:

    ./mvnw spring-boot:run

    Tests

    To run tests, you must use Maven as well. To execute all unit tests, run:

    ./mvnw test

    Viewing Documentation

    The Swagger API documentation is available once the application is running. By default, the Swagger UI can be accessed at:

    http://localhost:8080/swagger-ui/index.html

    Using Docker

    To run the application using Docker, follow these steps:

    1. Pull the Docker Image

    Pull the Docker image from Docker Hub using the following command:

    Heroku

    docker pull matheusmagal/neoforum:v2

    Openshift

    docker pull matheusmagal/neoforum-dev:v1

    2. Run the Docker Container

    Run the Docker container using the following command:

    Heroku

    docker run -p 8080:8080 matheusmagal/neoforum

    Openshift

    docker run -p 8080:8080 matheusmagal/neoforum-dev

    Accessing the Application in Production

    The application is hosted and accessible online. You can view the live version of the application at: Neoforum API.

    Visit original content creator repository https://github.com/MatheusMFranco/neoforum
  • reflective-dependency-injection

    Reflective Dependency Injection

    Dependency injection based on TypeScript’s experimental support for stage 2 decorators and metadata.

    • Constructor-based injection
    • Asynchronous support
    • Error detection
      • Circular dependency
      • Lifetime mismatch

    Getting Started

    If you are unfamiliar with decorators and metadata, I recommend checking out the TypeScript documentation and TC39’s decorator proposal documentation.

    Prerequisites

    The following versions of Node.js and TypeScript are required:

    • Node.js 20 or higher
    • TypeScript 4.7 or higher

    This package is pure ESM, and you must configure your project to use the ESM package.

    Installation

    1. Install the package using npm

    npm install reflective-dependency-injection

    2. Set compiler options in your tsconfig.json to enable experimental support for stage 2 decorators and metadata

    {
        "compilerOptions": {
            "experimentalDecorators": true,
            "emitDecoratorMetadata": true
        }
    }

    Usage

    Let’s take a look at the basic usage through the following steps.

    1. Declare dependencies

    First you need to declare your dependencies.

    Decorate a class with the @Injectable decorator to make the class injectable.

    Use the InjectionToken class as the identifier for non-class dependencies, and decorate the @Inject decorator to specify the dependency identifier.

    const GREETING: InjectionToken<string> = new InjectionToken('greeting');
    
    @Injectable()
    class Greeter {
        public constructor(
            @Inject(GREETING)
            public readonly greeting: string,
        ) { }
    
        public greet(): string {
            return `Hello, ${this.greeting}`;
        }
    }

    2. Create an injector

    Create an injector with providers that provide the declared dependencies.

    Use the provide function for type safety.

    const injector: Injector = new Injector([
        provide({
            identifier: Greeter,
            useClass: Greeter,
        }),
        provide({
            identifier: GREETING,
            useValue: 'world!',
        }),
    ]);

    3. Resolve dependencies

    The injector resolves the dependency graph and returns an instance of the dependency.

    const greeter: Greeter = await injector.get(Greeter);
    
    expect(greeter.greet()).toBe('Hello, world!');

    License

    Distributed under the MIT License. See the LICENSE file for more details.

    Visit original content creator repository
    https://github.com/choi-jack/reflective-dependency-injection

  • ChatRoom

    ChatRoom

    [Course Assignment] SignalR、轮询、长轮询实现的简易聊天室 Simple Chat Room(SingalR、Polling、Long Polling)

    [TOC]

    项目基础:创建服务端项目

    在 VS for Mac 中新建解决方案 “ChatRoom” ,选择.NET Core应用 API

    WebSocket(SignalR)

    1. 创建 SignalR 中心

    在项目中创建 Hubs 文件夹。在 Hubs 文件夹中,使用以下代码创建 ChatHub.cs 文件。

    using System.Threading.Tasks;
    using Microsoft.AspNetCore.SignalR;
    
    namespace ChatRoom.Hubs
    {
        public class ChatHub : Hub
        {
            public async Task SendMessage(string user, string message)
            {
                await Clients.All.SendAsync("ReceiveMessage", user, message);
            }
        }
    }

    2. 配置 SignalR

    配置 SignalR 服务器。在 Startup.cs 文件中添加代码。

    添加命名空间:

    using ChatRoom.Hubs;

    在方法 public void ConfigureServices(IServiceCollection services) 中添加代码:

    services.AddSignalR();

    在方法 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 中添加代码:

    						app.UseSignalR(routes =>
                {
                    routes.MapHub<ChatHub>("/chatHub");
                });

    3. 安装 SignalR 客户端包

    在终端中输入命令

    npm init -y
    npm install @aspnet/signalr

    需要在使用 SignalR 的前端文件中引入 signalr.js ,把该文件复制到当前路径

    cp node_modules/\@aspnet/signalr/dist/browser/signalr.js .

    然后其实其他文件都可以删掉了,只保留 signalr.js 就可以。

    4. 编写客户端

    只写一个最简单的 signalr.html 文件。使用了 Vue.jsElement-UI 组件库。

    <script>
      "use strict";
      var connection = new signalR.HubConnectionBuilder()
        .withUrl("http://localhost:5000/chatHub")
        .build();
    
      //Disable send button until connection is established
      document.getElementById("sendButton").disabled = true;
    
      connection.on("ReceiveMessage", function(user, message) {
        var msg = message
          .replace(/&/g, "&amp;")
          .replace(/</g, "&lt;")
          .replace(/>/g, "&gt;");
        var msgEle = document.createElement("span");
        msgEle.textContent = msg;
        msgEle.style =
          "background-color: #e6e6e6; border-radius: 5rem; padding: .25rem 1rem; width: auto";
        var userEle = document.createElement("span");
        userEle.textContent = user + " :  ";
        var li = document.createElement("li");
        li.appendChild(userEle);
        li.appendChild(msgEle);
        li.style = "list-style: none; margin: 1rem 0; ";
        document.getElementById("messagesList").appendChild(li);
      });
    
      connection
        .start()
        .then(function() {
          document.getElementById("sendButton").disabled = false;
        })
        .catch(function(err) {
          return console.error(err.toString());
        });
    
      document
        .getElementById("sendButton")
        .addEventListener("click", function(event) {
          var user = document.getElementById("userInput").value;
          var message = document.getElementById("messageInput").value;
          if (user == "") {
            alert("用户名不能为空");
          } else if (message == "") {
            alert("不能发送空内容");
          } else {
            connection.invoke("SendMessage", user, message).catch(function(err) {
              return console.error(err.toString());
            });
          }
          event.preventDefault();
        });
    </script>

    6. 启动与测试

    启动服务端程序,用浏览器打开 signalr.html 前端文件,打开两个标签页进行测试。

    遇到的问题

    1. 跨域访问

    本地用html文件直接向服务端发送请求,origin为null,导致请求失败。

    解决方案

    在服务端中配置跨域访问:

    // 在方法 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 中
    						app.UseCors(builder =>
                {
                    builder.WithOrigins("*")
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                });

    2. Negotiate 请求失败 (failed)

    在signalr创建websocket连接时,会有一个negotiate请求(“http://localhost:5000/chatHub/negotiate”),遇到问题是显示请求 failed ,连状态码都没有。我找不到代码的错误,也没有在网上找到解决方案。但是发现这一步是可以跳过的,就简单粗暴地解决了。

    解决方案

    在客户端的请求中,更改建立 signalr 连接的代码如下。

      var connection = new signalR.HubConnectionBuilder()
        .withUrl("http://localhost:5000/chatHub", {
          skipNegotiation: true,
          transport: signalR.HttpTransportType.WebSockets
        })
        .build();

    参考:

    MS文档·教程:ASP.NET Core SignalR 入门

    MS文档·ASP.NET Core SignalR JavaScript 客户端

    轮询

    1. 服务端创建控制器controller

    (项目中原有的 ValuesController.cs 可以删除)

    在项目中创建 Controllers 文件夹。在 Controllers 文件夹中,使用以下代码创建 PollingController.cs 文件。

    • 因为没有数据库,所以直接用静态变量来保存数据。Messages保存聊天室的所有消息记录,Subscribers保存聊天室内所有成员的阅读进度,从而在每个请求到来时,可以根据不同用户的情况返回新消息。
    • 包含三个方法:
      • 订阅,每个进入聊天室的用户将被服务端记录,每个用户用唯一的id标识。
      • 发送消息,用户发出发送消息的请求,消息将被服务端保存在静态变量中。
      • 获取消息,用户轮询使用的请求,每次只返回新消息,即用户没阅读/获取过的消息。

    using System.Collections.Generic;
    using Microsoft.AspNetCore.Mvc;
    
    namespace ChatRoom.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class PollingController : ControllerBase
        {
            static private List<Msg> Messages = new List<Msg>();
            static private Dictionary<string, int> Subscribers = new Dictionary<string, int>();
    
            // POST api/polling/subscribe
            [HttpPut("subscribe/{id}")]
            public void Subscribe(string id)
            {
                if (!Subscribers.ContainsKey(id))
                {
                    Subscribers.Add(id, 0);
                }
            }
    
            // POST api/polling
            [HttpPost]
            public void Post([FromBody]Msg msg)
            {
                Messages.Add(msg);
            }
    
            // GET api/polling/[id]
            [HttpGet("{id}")]
            public ActionResult<IEnumerable<Msg>> Get(string id)
            {
                if (Subscribers[id] == Messages.Count)
                {
                    return null;
                }
                var index = Subscribers[id];
                Subscribers[id] = Messages.Count;
                return Messages.GetRange(index, Messages.Count - index);
            }
        }
    
    		// 可以创建一个 Msg.cs 文件来放这个类,并且可以放在一个 Models 文件夹中
    		// 此处为了方便就直接放在 ChatRoom.Controllers 命名空间下了
        public class Msg
        {
            public string user { get; set; }
            public string message { get; set; }
    
            public Msg(string user, string message)
            {
                this.user = user;
                this.message = message;
            }
        }
    }

    2. 编写客户端

    复制 signalr.html 文件,并重命名该副本为 polling.html 。上面 SignalR 部分第5步的所有 JavaScript 代码可以全部删除。

    更改发送按钮

    				<el-button
              type="primary"
              plain
              id="sendButton"
              value="Send Message"
              @click="send_msg"
              >发送</el-button
            >

    更改创建 Vue 实例的代码

    原代码为:

      <script>
        new Vue({
          el: "#app",
          data: function() {
            return { msg: "", user: "" };
          }
        });
      </script>

    更改为:

    <script>
        new Vue({
          el: "#app",
          data: function() {
            return { msg: "", user: "", id: "", timer: 0 };
          },
          mounted() {
            this.id = Math.random()
              .toString(36)
              .substr(2);
            this.subscribe();
            var self = this;
            this.timer = setInterval(() => {
              self.polling();
            }, 500);
          },
          methods: {
            subscribe() {
              this.$http
                .put("http://localhost:5000/api/polling/subscribe/" + this.id)
                .catch(error => {
                  alert("请求失败");
                });
            },
            polling() {
              this.$http
                .get("http://localhost:5000/api/polling/" + this.id)
                .then(res => {
                  var data = res.body;
                  for (var i = 0; i < data.length; i++) {
                    this.add_msg(data[i].user, data[i].message);
                  }
                });
            },
            add_msg(user, message) {
              var msg = message
                .replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;");
              var msgEle = document.createElement("span");
              msgEle.textContent = msg;
              msgEle.style =
                "background-color: #e6e6e6; border-radius: 5rem; padding: .25rem 1rem; width: auto";
              var userEle = document.createElement("span");
              userEle.textContent = user + " :  ";
              var li = document.createElement("li");
              li.appendChild(userEle);
              li.appendChild(msgEle);
              li.style = "list-style: none; margin: 1rem 0; ";
              document.getElementById("messagesList").appendChild(li);
            },
            send_msg() {
              this.$http
                .post("http://localhost:5000/api/polling", {
                  user: this.user,
                  message: this.msg
                })
                .catch(error => {
                  alert("发送失败");
                });
            }
          }
        });
    </script>
    关键代码说明

            this.timer = setInterval(() => {
              self.polling();
            }, 500);

    设置一个定时器,每500ms执行一次 polling() 方法,在该方法中向服务端发送请求获取新消息。

    长轮询

    1. 服务端创建控制器controller

    在 Controllers 文件夹中,使用以下代码创建 LongPollingController.cs 文件。

    • 包含三个方法:
      • 订阅,每个进入聊天室的用户将被服务端记录,每个用户用唯一的id标识。
      • 发送消息,用户发出发送消息的请求,消息将被服务端保存在静态变量中。
      • 获取消息,用户轮询使用的请求,每次只返回新消息,即用户没阅读/获取过的消息。
    • 与 PollingController 的区别:
      • 增加一个 ManualResetEvent 静态对象来控制线程。
      • 当有获取新消息的请求到达,如果该用户没有新消息需要获取,该线程会被 ManualResetEvent 对象阻塞。
      • 当有新消息发送/提交,会释放所有被阻塞的线程。

    using System.Collections.Generic;
    using System.Threading;
    using Microsoft.AspNetCore.Mvc;
    
    namespace ChatRoom.Controllers
    {
        [Route("api/[controller]")]
        [ApiController]
        public class LongPollingController : ControllerBase
        {
            private static List<Msg> Messages = new List<Msg>();
            private static Dictionary<string, int> Subscribers = new Dictionary<string, int>();
            private static ManualResetEvent mre = new ManualResetEvent(false);
    
            // POST api/longPolling/subscribe
            [HttpPut("subscribe/{id}")]
            public void Subscribe(string id)
            {
                if (!Subscribers.ContainsKey(id))
                {
                    Subscribers.Add(id, 0);
                }
            }
    
            // POST api/longPolling
            [HttpPost]
            public void Post([FromBody]Msg msg)
            {
                Messages.Add(msg);
                mre.Set();
            }
    
            // GET api/longPolling/[id]
            [HttpGet("{id}")]
            public ActionResult<IEnumerable<Msg>> Get(string id)
            {
                if (!Subscribers.ContainsKey(id))
                {
                    return null;
                }
                if (Subscribers[id] >= Messages.Count)
                {
                    mre.Reset();
                    mre.WaitOne();
                }
                
                var index = Subscribers[id];
                Subscribers[id] = Messages.Count;
                return Messages.GetRange(index, Messages.Count - index);
            }
        }
    }

    2. 编写客户端

    复制 polling.html 文件,并重命名该副本为 longpolling.html

    更改创建 Vue 实例的代码

    	<script>
        new Vue({
          el: "#app",
          data: function() {
            return { msg: "", user: "", id: "" };
          },
          mounted() {
            this.id = Math.random()
              .toString(36)
              .substr(2);
            this.subscribe();
            setTimeout(this.polling, 3000);
          },
          methods: {
            subscribe() {
              this.$http
                .put("http://localhost:5000/api/longPolling/subscribe/" + this.id)
                .catch(error => {
                  alert("请求失败");
                });
            },
            polling() {
              this.$http
                .get("http://localhost:5000/api/longPolling/" + this.id)
                .then(res => {
                  var data = res.body;
                  for (var i = 0; i < data.length; i++) {
                    this.add_msg(data[i].user, data[i].message);
                  }
                  this.polling();
                });
            },
            add_msg(user, message) {
              var msg = message
                .replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;");
              var msgEle = document.createElement("span");
              msgEle.textContent = msg;
              msgEle.style =
                "background-color: #e6e6e6; border-radius: 5rem; padding: .25rem 1rem; width: auto";
              var userEle = document.createElement("span");
              userEle.textContent = user + " :  ";
              var li = document.createElement("li");
              li.appendChild(userEle);
              li.appendChild(msgEle);
              li.style = "list-style: none; margin: 1rem 0; ";
              document.getElementById("messagesList").appendChild(li);
            },
            send_msg() {
              this.$http
                .post("http://localhost:5000/api/longPolling", {
                  user: this.user,
                  message: this.msg
                })
                .catch(error => {
                  alert("发送失败");
                });
            }
          }
        });
      </script>
    关键代码说明

    设置 3s 的等待时间,待页面加载完后,客户端自动(不需要用户操作触发)发出第一次获取消息的请求。

    setTimeout(this.polling, 3000);

    与轮询的 polling() 方法的区别在于,在 polling() 请求成功后,要回调 polling() 方法。一定要写在 .then() 方法里,表示请求成功才执行。

            polling() {
              this.$http
                .get("http://localhost:5000/api/longPolling/" + this.id)
                .then(res => {
                  var data = res.body;
                  for (var i = 0; i < data.length; i++) {
                    this.add_msg(data[i].user, data[i].message);
                  }
                  this.polling();		// 回调 polling() 方法
                });
              // this.polling(); 不可以写在这里
            },

    其他方法 – 使用异步控制器

    还可以使用异步控制器来实现长轮询,这使得服务端在向客户端回应之前可以进行其他操作。

    教程:基于长轮询的 Comet

    MS文档·Asynchronous Controller

    Visit original content creator repository
    https://github.com/SarahWuTX/ChatRoom