Giter Club home page Giter Club logo

reactivesearch-api's Introduction

ReactiveSearch API

Tests

Note: Development has moved to a private repository. This repository is used for release management.

ReactiveSearch API is a declarative, open-source API for querying Elasticsearch. It also acts as a reverse proxy and API gateway to an Elasticsearch cluster. ReactiveSearch API is best suited for site search, app search and e-commerce search use-cases.

Why use ReactiveSearch API

Lets take a search query for a books e-commerce site where a user is searching for the keyword "chronicles" on either title or author fields, has a rating filter applied to only return books with a rating gte 4.

This query takes ~80 lines of code to write with Elasticsearch's DSL. The same query can be expressed in ~20 lines of code with ReactiveSearch.

Lets understand the key differences between the two formats:

  1. The Elasticsearch query is imperative in nature, makes use of search-engine specific terminologies. This makes it more expressive at the cost of a higher learning curve. In comparison, the ReactiveSearch query is declarative and hides the implementation details.

  2. A ReactiveSearch query isn't prone to the nesting hell that Elasticsearch's query is. It expresses each query individually and then composes them together using the react property.

  3. ReactiveSearch query's declarative nature also makes it composable. It is easy to capture intent, enrich the query and apply access control checks to the individual queries.

  4. ReactiveSearch query's declarative nature also makes it a perfect fit for exposing it to publicly inspectable web and mobile networks. Exposing Elasticsearch's DSL in such a setting isn't recommended as it opens up a script injection attack vector.

Full API reference for ReactiveSearch is available over here.

Installation

Running it

In order to run reactivesearch-api, you'll require an Elasticsearch node. There are multiple ways you can setup an Elasticsearch, either locally or remotely. We, however, are delineating the steps for local setup of a single node Elasticsearch via it's Docker image.

Note: The steps described here assumes a docker installation on the system.

  1. Create a docker network
docker network create reactivesearch
  1. Start a single node Elasticsearch cluster locally
docker run -d --rm --name elasticsearch -p 9200:9200 -p 9300:9300 --net=reactivesearch -e "discovery.type=single-node" -e "xpack.security.enabled=false" docker.elastic.co/elasticsearch/elasticsearch:8.2.0

NOTE: It is advised to use -e "xpack.security.enabled=false" for local runs of ElasticSearch since otherwise ES is not available on :9200.

OR

Alternative to using Elasticsearch, you can also start a single node OpenSearch cluster locally

docker run --name opensearch --rm -d -p 9200:9200 -e http.port=9200 -e discovery.type=single-node -e http.max_content_length=10MB -e http.cors.enabled=true -e http.cors.allow-origin=\* -e http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization -e http.cors.allow-credentials=true --net=reactivesearch opensearchproject/opensearch:1.3.2
  1. Start Zinc locally
mkdir data
docker run -v /full/path/of/data:/data -e ZINC_DATA_PATH="/data" -p 4080:4080 \
    -e ZINC_FIRST_ADMIN_USER=appbase -e ZINC_FIRST_ADMIN_PASSWORD=zincf0rappbase \
    --name zinc public.ecr.aws/zinclabs/zinc:latest
  1. Start ReactiveSearch locally
docker build -t reactivesearch . && docker run --rm --name reactivesearch -p 8000:8000 --net=reactivesearch --env-file=config/docker.env reactivesearch

For convenience, the steps described above are combined into a single docker-compose file. You can execute the file with command:

docker-compose up

Building

To build from source you need Git and Go (version 1.11 or higher).

You can build the binary locally by executing the following command from the project directory:

make

This produces an executable & plugin libraries in the root project directory. To start the Reactivesearch server, run:

./build/reactivesearch --env=config/manual.env --log=info

Alternatively, you could execute the following commands to start the server without producing an executable, (but still produce the plugin libraries):

make plugins
go run main.go --env=config/manual.env

Note: Running the executable assumes an active Elasticsearch upstream whose URL is provided in the .env file.

Logging

Define the run time flag (log) to change the default log mode, the possible options are:

debug

Most verbose, use this to get logs for elasticsearch interactions.

info

Prints the basic information

error (default)

Only log the errors

profiling

Set the profiling flag to true at runtime to enable net profiling endpoints. Profiling endpoints are exposed at /debug/pprof route.

Profiling CPU (time taken):

An example of profiling for CPU usage is: /debug/pprof/profile (will wait for 30s and return a profile), or You can directly hit go tool pprof -http=":8080" http://localhost:8000/debug/pprof/profile to get a profile UI (top, graph, flamegraph) for time taken.

Profiling Heap:

An example for profiling for heap usage is: /debug/pprof/heap (will return a profile for the point in time), or You can directly hit go tool pprof -http=":8080" http://localhost:8000/debug/pprof/heap to get a profile UI (top, graph, flamegraph) of heap usage.

diff-logs

Set the diff-logs flag to false (defaults to true) at runtime to disable the use of log diffing algorithm. For high-throughput use-cases (100 requests/sec or above), the log diffing algorithm consumes signficant CPU usage (about ~2 CPU cores per 100 requests/sec) that can be saved by turning off log diffing.

TLS Support

You can optionally start Reactivesearch to serve https requests instead of http requests using the flag https. You also need to provide the server key & certificate file location through the environment file. config/manual.env is configured to use demo server key & certificates, which works for localhost.

    go run main.go --log=info --env=config/manual.env --https

If you wish to manually test TLS support at localhost, curl needs to be also passed an extra parameter providing the cacert, in this case.

    curl https://foo:bar@localhost:8000/_user --cacert sample/rootCA.pem

JWT Key Loading through HTTP

If you wish to test loading JWT Key through HTTP, you can use the following commands to start a HTTP server serving the key

    cd sample
    python -m SimpleHTTPServer 8500

Then start ReactiveSearch using the command:

    go run main.go --log=info --env=config/manual-http-jwt.env

ReactiveSearch also exposes an API endpoint to set the key at runtime, so this need not be set initially.

Run Tests

Currently, tests are implemented for auth, permissions, users and billing modules. You can run tests using:

make test

or

go test -p 1 ./...

Extending ReactiveSearch API

The functionality in ReactiveSearch can extended via plugins. A ReactiveSearch plugin can be considered as a service in itself; it can have its own set of routes that it handles (keeping in mind it doesn't overlap with existing routes of other plugins), define its own chain of middlewares and more importantly its own database it intends to interact with (in our case it is Elasticsearch). For example, one can easily have multiple plugins providing specific services that interact with more than one database. The plugin is responsible for its own request lifecycle in this case.

However, it is not necessary for a plugin to define a set of routes for a service. A plugin can easily be a middleware that can be used by other plugins with no new defined routes whatsoever. A middleware can either interact with a database or not is an implementation choice, but the important point here is that a plugin can be used by other plugins as long as it doesn't end up being a cyclic dependency.

Each plugin is structured in a particular way for brevity. Refer to the plugin docs which describes a basic plugin implementation.

Models

Since every request made to Elasticsearch hits ReactiveSearch server first, it becomes beneficial to provide a set of models that allow a client to define access control policies over the Elasticsearch RESTful API and ReactiveSearch's functionality. ReactiveSearch provides several essential abstractions as plugins that are required in order to interact with Elasticsearch and ReactiveSearch itself.

Available Plugins

User

In order to interact with ReactiveSearch, the client must define either a User or a permission. A User encapsulates its own set of properties that defines its capabilities.

  • username: uniquely identifies the user
  • password: verifies the identity of the user
  • is_admin: distinguishes an admin user
  • categories: analogous to the Elasticsearch's API categories, like Cat API, Search API, Docs API and so on
  • acls: adds another layer of granularity within each Elasticsearch API category
  • ops: operations a user can perform
  • indices: name/pattern of indices the user has access to
  • email: user's email address
  • created_at: time at which the user was created

Permission

A User can create a Permission resource and associate it with a User, defining its capabilities in order to access Elasticsearch's RESTful API. Permissions serve as an entry point for accessing the Elasticsearch API and has a fixed time-to-live unlike a user, after which it will no longer be operational. A User is always in charge of the Permission they create.

  • username: an auto generated username for Basic Auth access
  • password: an auto generated password for Basic Auth access
  • owner: represents the owner of the permission
  • creator: represents the creator of the permission
  • categories: analogous to the Elasticsearch's API categories, like Cat API, Search API, Docs API and so on
  • acls: adds another layer of granularity within each Elasticsearch API category
  • ops: operations a permission can perform
  • indices: name/pattern of indices the permission has access to
  • sources: source IPs from which a permission is allowed to make requests
  • referers: referers from which a permission is allowed to make requests
  • created_at: time at which the permission was created
  • ttl: time-to-live represents the duration till which a permission remains valid
  • limits: request limits per categories given to the permission
  • description: describes the use-case of the permission

Category

Categories can be used to control access to data and APIs in ReactiveSearch. Along with Elasticsearch APIs, categories cover the APIs provided by ReactiveSearch itself to allow fine-grained control over the API consumption. For Elasticsearch, Categories broadly resembles to the API classification that Elasticsearch provides such as Document APIs, Search APIs, Indices APIs and so on. For ReactiveSearch, Categories resembles to the additional APIs on top of Elasticsearch APIs, such as analytics and book keeping. Refer to category docs for the list of categories that ReactiveSearch supports.

ACL

ACLs allow a fine grained control over the Elasticsearch APIs in addition to the Categories. Each ACL resembles an action performed by an Elasticsearch API. For brevity, setting and organising Categories automatically sets the default ACLs associated with the set Categories. Setting ACLs adds just another level of control to provide access to Elasticsearch APIs within a given Category. Refer to acl docs for the list of acls that ReactiveSearch supports.

Op

Operation delineates the kind of operation a request intends to make. The operation of the request is identified before the request is served. The classification of the request operation depends on the use-case and the implementation of the plugin. Operation is currently classified into three kinds:

  • Read: operation permits read requests exclusively.
  • Write: operation permits write requests exclusively.
  • Delete: operation permits delete requests exclusively.

In order to allow a user or permission to make requests that involve modifying the data, a combination of the above operations would be required. For example: ["read", "write"] operation would allow a user or permission to perform both read and write requests but would forbid making delete requests.

Request Logging

ReactiveSearch server currently maintains audit logs for all the requests made via it to elasticsearch. Both request and responses are stored for the users to view and inspect later. The request logs can be fetched for both specific indices or the whole cluster. The dedicated endpoints to fetch the index/cluster logs can be found here.

ReactiveSearch: UI Libraries

The ReactiveSearch API is used by ReactiveSearch and Searchbox libraries. If you're building a search UI using React, Vue, Flutter, React Native or Vanilla JS, these libraries provide scaffolding and commonly used search components that can compress weeks of development time into days.

ReactiveSearch ๐Ÿž and appbase.io ๐Ÿงˆ

appbase.io extends the opensource ReactiveSearch API with the following functionalities:

  1. Actionable Analytics capture telemetry from the ReactiveSearch API and provide powerful search-driven insights into users, clicks, conversions, geographical distribution, slow searches and more.
  2. Search Relevance provides a REST API and point-and-click interface to deploy a search relevance strategy by being able to configure language, Search/Aggregation/Results settings, Query Rules.
  3. Application Cache provides a blazing fast search performance and improved thorughput for search.
  4. UI Builder allows creating Search and Recommendations UI widgets with no code.

You can deploy appbase.io in cloud. We also provide one-click installs for AWS, Heroku, Docker and Kubernetes. Get started with these over here.

Docs

Refer to the REST API docs for ReactiveSearch.

reactivesearch-api's People

Contributors

bietkul avatar bsrk avatar deepjyoti30 avatar dependabot[bot] avatar github-actions[bot] avatar hackertron avatar jeet-parekh avatar lakhansamani avatar octate avatar palash25 avatar shahanuj2610 avatar siddharthlatest avatar thetechoddbug avatar utsavoza avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactivesearch-api's Issues

Make sure all the existing test cases are working as expected

How to reproduce?

Run go test ./... at the root in feat/es7 branch

You can easily see that a lot of test cases get failed, we need to fix it and make sure that all the test cases work as expected.

Additional Context

We recently upgraded the elastic search version from 6.x to 7.x which has some breaking changes, probably that might be the reason for some failed test cases.

Failed to compile on ubuntu 20 with golang 1.17.7

Failed to compile manually

/opt/apps/reactivesearch-api$ make
go build --buildmode=plugin -o build/plugins/auth.so plugins/auth/main/auth.go

github.com/cespare/xxhash

asm: xxhash_amd64.s:120: when dynamic linking, R15 is clobbered by a global variable access and is used here: 00092 (/home/tomburnell/go/pkg/mod/github.com/cespare/[email protected]/xxhash_amd64.s:120) ADDQ R15, AX
asm: assembly failed

github.com/cespare/xxhash/v2

asm: xxhash_amd64.s:120: when dynamic linking, R15 is clobbered by a global variable access and is used here: 00092 (/home/tomburnell/go/pkg/mod/github.com/cespare/xxhash/[email protected]/xxhash_amd64.s:120) ADDQ R15, AX
asm: assembly failed

github.com/klauspost/compress/zstd/internal/xxhash

asm: xxhash_amd64.s:120: when dynamic linking, R15 is clobbered by a global variable access and is used here: 00092 (/home/tomburnell/go/pkg/mod/github.com/klauspost/[email protected]/zstd/internal/xxhash/xxhash_amd64.s:120) ADDQ R15, AX
asm: assembly failed
make: *** [Makefile:20: plugins/auth/main/auth.so] Error 2

What version of ReactiveSearch API are you using ?

commit hash: 8da3f17

ubuntu 20

go version
go version go1.17.7 linux/amd64

$ uname -a
Linux artcore-vb 5.4.0-100-generic #113-Ubuntu SMP Thu Feb 3 18:43:29 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

i$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 9.3.0-17ubuntu120.04' --with-bugurl=file:///usr/share/doc/gcc-9/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,gm2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-9 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-9-HskZEa/gcc-9-9.3.0/debian/tmp-nvptx/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1
20.04)

Add test cases for the users plugin

Test cases are missing for the following files in the users plugin:

  • handler functions - located in handlers.go
  • middleware functions - located in middleware.go

problem when using arc

  1. github.com/appbaseio-confidential or github.com/appbaseio ?
  2. seems arc only validate the username. password can be anything
  3. password should be encrypted stored. (maybe bcrypt)

Does it work with Kibana ?

I see this doesn't sit on top of the es unlike xpack or opendistro so I can not use

elasticsearch.username: and elasticsearch.password:

as mentioned in kibana docs
I tried to use basic auth by passing username and passwork in the url like http://foo:bar@arc:8000 but that didn't work.

Please let me know if it's possible to run it with Kibana ?

License key and docker installation open source

What version of ReactiveSearch API are you using ?

Latest version

What issue did you run into?

Started project up with docker compose, getting error

msg="[cmd] Unable to read license file open --log: no such file or directory" file=" entry.go:359"

What did you expect to see?

Expected reactive search api to start up

What did you see instead?

error:

msg="[cmd] Unable to read license file open --log: no such file or directory" file=" entry.go:359"

There seems to be something missing steps in the docker setup. Where do I get my license key and where do I put it in the docker compose / env file ?

Error 401 (Unauthorized)

What version of ReactiveSearch API are you using ?

Lastest

What issue did you run into?

When configuring a remote elasticsearch the variables found in config/docker.env (username and password) don't work, I had to put the credentials in the url. Is there a way to not have to pass the credentials through the url?

What did you expect to see?

ES_CLUSTER_URL=https://my-elastic.com
USERNAME=username
PASSWORD=password

What did you see instead?

ES_CLUSTER_URL=https://username:[email protected]

Internal Server Error when posting a permission while being authenticated with another permission

What version of ReactiveSearch API are you using ?

8.9.0

What issue did you run into?

  1. Create a permission (API Credentials) with a read-write access to the "permissions" category.
  2. Use the credentials of the just created permission in HTTP Basic Auth to send a POST /_permission request, trying to create another permission object.

What did you expect to see?

A created permission.

What did you see instead?

Internal server error saying "*user.User" not found in request context.

From what I was able to find in the code as a non-Go developer, looks like the user is being read from the context right here for the only purpose to check if it's admin or not, which decides the permission's default settings. And if the user not found, the error is being thrown.
Looking at the auth middleware I can see it being assigned only when the request is authenticated on behalf of a user. When you use API Credentials to authenticate the request, the permission is being assigned to the context instead.
I haven't found anywhere in the documentation that you can only create permissions when authenticated as a user. So I think it's a bug.
Would it be possible to fix it by assuming the user is not an admin if the request is authenticated with the API Credentials?

Open-source docker image availability

When using the example include docker-compose.yaml file, it builds the API server locally and is able to boot up. When using the dockerhub image available on https://hub.docker.com/r/appbaseio/reactivesearch-api, it fails to boot asking for a valid APPBASE_ID.

Could this be happening because of the build args not being opensource? https://github.com/appbaseio/reactivesearch-api/blob/dev/Dockerfile#L3-L18

My modified docker-compose file:

version: "3"
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.2.0
    container_name: elasticsearch
    networks:
      - reactivesearch
    environment:
      - bootstrap.memory_lock=true
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms1g -Xmx1g"
    ulimits:
      nproc: 65536
      nofile:
        soft: 65536
        hard: 65536
      memlock:
        soft: -1
        hard: -1
    ports:
      - 9200:9200
  reactivesearch:
    #build: ./
    image: appbaseio/reactivesearch-api:8.20.3
    environment:
      - ES_CLUSTER_URL=http://elasticsearch:9200
    container_name: reactivesearch
    env_file:
      - config/docker.env
    networks:
      - reactivesearch
    restart: on-failure
    ports:
      - 8000:8000
    depends_on:
      - elasticsearch

networks:
  reactivesearch:
    driver: bridge
sarah@remote-dev:~/reactivesearch-api$ docker compose up reactivesearch
[+] Running 1/1
 โœ” reactivesearch Pulled                                                                                       0.9s 
[+] Running 2/0
 โœ” Container elasticsearch   Running                                                                           0.0s 
 โœ” Container reactivesearch  Recreated                                                                         0.1s 
Attaching to reactivesearch
reactivesearch  | => port used 8000
reactivesearch  | [cmd] : reading env file .env . This may happen if the environments are declared directly :  open .env: no such file or directory
reactivesearch  | time="2024/01/09 19:08:35" level=fatal msg="APPBASE_ID env required but not present" file=" billing.go:615"
reactivesearch exited with code 0

What version of ReactiveSearch API are you using ?

latest

What issue did you run into?

What did you expect to see?

What did you see instead?

Add test cases for the elasticsearch plugin

Currently, we don't have test cases for the elasticsearch plugin, we should add test cases for the following files.

  • handlers functions - located in handlers.go
  • routes functions - located in routes.go
  • middleware functions - located in middleware.go

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.