diff --git a/.github/workflows/deploy-backend.yml b/.github/workflows/deploy-backend.yml index d181f699..1ee4ae6f 100644 --- a/.github/workflows/deploy-backend.yml +++ b/.github/workflows/deploy-backend.yml @@ -1,34 +1,64 @@ -name: Deploy Backend +name: Test and Deploy Backend -on: [ push, pull_request ] +on: [ push ] jobs: test: runs-on: "ubuntu-latest" steps: - - name: 'Checkout github action' + - name: Checkout repo uses: actions/checkout@v4 - + - name: Setup golang uses: actions/setup-go@v5 with: go-version: '1.22' - + - name: Install dependencies run: | go install gotest.tools/gotestsum@latest go get ./... - + - name: Build - run: go build -o . ./... - - - name: Run Tests + run: go build -o ./main ./app + + - name: Run tests run: gotestsum --hide-summary=skipped ./app/tests/... > tests-result.txt - + - name: Upload tests result uses: actions/upload-artifact@v4 with: name: tests-result path: tests-result.txt - # deploy: - # if: github.event_name == 'pull_request' && github.ref == 'refs/heads/main' + + deploy: + runs-on: ubuntu-latest + needs: test + if: contains(' + refs/heads/dev + refs/heads/main + ', github.ref) + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: edoriggio/api-scout + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index e2f948a7..6f60e494 100644 --- a/.gitignore +++ b/.gitignore @@ -21,8 +21,10 @@ go.work .vscode/ .DS_Store -ca.crt +*.crt *.config.yml !config/template.config.yml models/* -!.keep \ No newline at end of file +!.keep + +mongo-ids.txt \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..baa7e1b0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: check-yaml + - id: check-added-large-files + - id: detect-private-key + - repo: https://github.com/dnephin/pre-commit-golang + rev: v0.5.0 + hooks: + - id: go-unit-tests +# - repo: local +# hooks: +# - id: generate-go-docs +# name: generate-go-docs +# entry: ./scripts/hooks/generate-go-docs.sh +# language: system +# - id: generate-swagger-docs +# name: generate-swagger-docs +# entry: ./scripts/hooks/generate-swagger-docs.sh +# language: system diff --git a/Dockerfile b/Dockerfile index 19e0acec..77a36016 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,17 +6,18 @@ COPY go.* ./ RUN go mod download COPY . . -RUN go build -o /backend/build/app ./app/cmd/main +RUN go install github.com/swaggo/swag/cmd/swag@latest +RUN cd app && swag init +RUN go build -o /backend/build/app /backend/app FROM alpine:latest WORKDIR /backend -ENV GIN_MODE="release" -ENV MODELS_HOST="models" +ARG GIN_MODE +ARG MODELS_HOST COPY --from=builder /backend/build/app /backend/build/app -COPY --from=builder /backend/config /backend/config EXPOSE 8080 ENTRYPOINT [ "/backend/build/app" ] \ No newline at end of file diff --git a/README.md b/README.md index a88c7a4e..9d2ef5de 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ -# API Scout - Backend +# API Scout +## Configure -## Set ENV Variables +### Set ENV Variables The following are the variables that need to be set: - `GIN_MODE`: In which mode Gin should be running (can be `release` or `debug`) - `MODELS_HOST`: The hostname of the DL models container (use `models` if in release mode, `127.0.0.1` if in debug mode) -## Downloading USE Model +### Downloading USE Model To download the Universal Sentence Encoder (USE) model, run the python script in `scripts/download-use.py` by running the following commands (you should run these commands while in the `backend` directory): @@ -18,7 +19,7 @@ conda run -n api-scout python ./scripts/download-use.py You will now have a new directory in `models` called `universal-encoder`. This model will be used by the `docker-compose.yml` file to serve the model in a container. -## Spinning up the Containers +### Spinning up the Containers For replication purposes, in this repo you will find both a `Dockerfile` and a `docker-compose.yml` file. The `Dockerfile` will create a Docker image with a build of the Golang backend in it. To create the image, simply run: @@ -34,6 +35,33 @@ docker-compose up -d This will create a Docker container for the Golang backend and for the USE model. The backend will now be able to make HTTP calls to the USE model to embed queries and documents. N.B.: The USE container will not expose any ports, it will be called locally by the backend by means of the `be-network` shared network. -## Dependencies +### Dependencies For the backend to work, both an ElasticSearch instance and a MongoDB instance should be up and running. + +## Documentation + +To generate and consult the documentation, you can use the following commands. + +### Generate Documentation + +To generate the documentation, first you need to make sure that you have all the necessary dependencies installed. Run the following commands: + +```shell +go install go.abhg.dev/doc2go@latest +npm install -g pagefind@latest +``` + +Once all dependencies have been installed, run: + +```shell +doc2go -config ./app/internal/doc2go.rc ./app/internal/... +``` + +### Consult Documentation + +To consult the documentation of the `app/internal` API, run the following command: + +```shell +cd docs && python -m http.server 8000 +``` diff --git a/app/docs/docs.go b/app/docs/docs.go new file mode 100644 index 00000000..7623a103 --- /dev/null +++ b/app/docs/docs.go @@ -0,0 +1,381 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/search": { + "post": { + "description": "Retrieve OpenAPI specifications matching the given query", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Search OpenAPI specifications", + "parameters": [ + { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "page number", + "name": "page", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "size of the page", + "name": "pageSize", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "default": 100, + "description": "knn's k", + "name": "k", + "in": "query" + }, + { + "description": "search query", + "name": "fragment", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.EmbeddingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.SpecificationWithApi" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + } + } + } + }, + "/specification": { + "post": { + "description": "Insert new OpenAPI specifications in the database.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "specification" + ], + "summary": "Insert OpenAPI specifications", + "parameters": [ + { + "description": "New Specifications", + "name": "specifications", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.SpecificationsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + } + } + } + }, + "/specification/{id}": { + "get": { + "description": "Retrieve a specific OpenAPI specification's content given a valid ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "specification" + ], + "summary": "Get OpenAPI specification", + "parameters": [ + { + "type": "string", + "description": "Specification ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.SpecificationWithApi" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + } + } + } + } + }, + "definitions": { + "models.Api": { + "type": "object", + "properties": { + "commits": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "latest": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "$ref": "#/definitions/models.Version" + } + } + }, + "models.EmbeddingRequest": { + "type": "object", + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "filters": { + "type": "string" + }, + "fragment": { + "type": "string" + } + } + }, + "models.HTTPError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 400 + }, + "message": { + "type": "string", + "example": "Bad Request" + } + } + }, + "models.Metrics": { + "type": "object", + "properties": { + "schema": { + "type": "object", + "properties": { + "models": { + "type": "integer" + }, + "properties": { + "type": "integer" + } + } + }, + "security": { + "type": "object", + "properties": { + "endpoints": { + "type": "integer" + } + } + }, + "structure": { + "type": "object", + "properties": { + "methods": { + "type": "integer" + }, + "operations": { + "type": "integer" + }, + "paths": { + "type": "integer" + } + } + } + } + }, + "models.MongoDocument": { + "type": "object", + "properties": { + "api": { + "$ref": "#/definitions/models.Api" + }, + "date": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "metrics": { + "$ref": "#/definitions/models.Metrics" + }, + "mongo-id": { + "type": "string" + }, + "score": { + "type": "number" + }, + "specification": { + "$ref": "#/definitions/models.Specification" + } + } + }, + "models.Specification": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "version": { + "$ref": "#/definitions/models.Version" + } + } + }, + "models.SpecificationBackend": { + "type": "object", + "additionalProperties": true + }, + "models.SpecificationWithApi": { + "type": "object", + "properties": { + "metadata": { + "$ref": "#/definitions/models.MongoDocument" + }, + "specification": { + "type": "string" + } + } + }, + "models.SpecificationsRequest": { + "type": "object", + "properties": { + "specifications": { + "type": "array", + "items": { + "$ref": "#/definitions/models.SpecificationBackend" + } + } + } + }, + "models.Version": { + "type": "object", + "properties": { + "build": { + "type": "string" + }, + "major": { + "type": "integer" + }, + "minor": { + "type": "integer" + }, + "patch": { + "type": "integer" + }, + "prerelease": { + "type": "string" + }, + "raw": { + "type": "string" + }, + "valid": { + "type": "boolean" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "/api/v1", + Schemes: []string{}, + Title: "API Scout", + Description: "This is the backend for the API Scout platform.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/app/docs/swagger.json b/app/docs/swagger.json new file mode 100644 index 00000000..9b74ebf2 --- /dev/null +++ b/app/docs/swagger.json @@ -0,0 +1,356 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is the backend for the API Scout platform.", + "title": "API Scout", + "contact": {}, + "version": "1.0" + }, + "basePath": "/api/v1", + "paths": { + "/search": { + "post": { + "description": "Retrieve OpenAPI specifications matching the given query", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "search" + ], + "summary": "Search OpenAPI specifications", + "parameters": [ + { + "minimum": 1, + "type": "integer", + "default": 1, + "description": "page number", + "name": "page", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "default": 10, + "description": "size of the page", + "name": "pageSize", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "default": 100, + "description": "knn's k", + "name": "k", + "in": "query" + }, + { + "description": "search query", + "name": "fragment", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.EmbeddingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.SpecificationWithApi" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + } + } + } + }, + "/specification": { + "post": { + "description": "Insert new OpenAPI specifications in the database.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "specification" + ], + "summary": "Insert OpenAPI specifications", + "parameters": [ + { + "description": "New Specifications", + "name": "specifications", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/models.SpecificationsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + } + } + } + }, + "/specification/{id}": { + "get": { + "description": "Retrieve a specific OpenAPI specification's content given a valid ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "specification" + ], + "summary": "Get OpenAPI specification", + "parameters": [ + { + "type": "string", + "description": "Specification ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.SpecificationWithApi" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/models.HTTPError" + } + } + } + } + } + }, + "definitions": { + "models.Api": { + "type": "object", + "properties": { + "commits": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "latest": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "$ref": "#/definitions/models.Version" + } + } + }, + "models.EmbeddingRequest": { + "type": "object", + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "filters": { + "type": "string" + }, + "fragment": { + "type": "string" + } + } + }, + "models.HTTPError": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "example": 400 + }, + "message": { + "type": "string", + "example": "Bad Request" + } + } + }, + "models.Metrics": { + "type": "object", + "properties": { + "schema": { + "type": "object", + "properties": { + "models": { + "type": "integer" + }, + "properties": { + "type": "integer" + } + } + }, + "security": { + "type": "object", + "properties": { + "endpoints": { + "type": "integer" + } + } + }, + "structure": { + "type": "object", + "properties": { + "methods": { + "type": "integer" + }, + "operations": { + "type": "integer" + }, + "paths": { + "type": "integer" + } + } + } + } + }, + "models.MongoDocument": { + "type": "object", + "properties": { + "api": { + "$ref": "#/definitions/models.Api" + }, + "date": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "metrics": { + "$ref": "#/definitions/models.Metrics" + }, + "mongo-id": { + "type": "string" + }, + "score": { + "type": "number" + }, + "specification": { + "$ref": "#/definitions/models.Specification" + } + } + }, + "models.Specification": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "version": { + "$ref": "#/definitions/models.Version" + } + } + }, + "models.SpecificationBackend": { + "type": "object", + "additionalProperties": true + }, + "models.SpecificationWithApi": { + "type": "object", + "properties": { + "metadata": { + "$ref": "#/definitions/models.MongoDocument" + }, + "specification": { + "type": "string" + } + } + }, + "models.SpecificationsRequest": { + "type": "object", + "properties": { + "specifications": { + "type": "array", + "items": { + "$ref": "#/definitions/models.SpecificationBackend" + } + } + } + }, + "models.Version": { + "type": "object", + "properties": { + "build": { + "type": "string" + }, + "major": { + "type": "integer" + }, + "minor": { + "type": "integer" + }, + "patch": { + "type": "integer" + }, + "prerelease": { + "type": "string" + }, + "raw": { + "type": "string" + }, + "valid": { + "type": "boolean" + } + } + } + } +} \ No newline at end of file diff --git a/app/docs/swagger.yaml b/app/docs/swagger.yaml new file mode 100644 index 00000000..8de5423d --- /dev/null +++ b/app/docs/swagger.yaml @@ -0,0 +1,236 @@ +basePath: /api/v1 +definitions: + models.Api: + properties: + commits: + type: integer + id: + type: integer + latest: + type: boolean + name: + type: string + source: + type: string + version: + $ref: '#/definitions/models.Version' + type: object + models.EmbeddingRequest: + properties: + fields: + items: + type: string + type: array + filters: + type: string + fragment: + type: string + type: object + models.HTTPError: + properties: + code: + example: 400 + type: integer + message: + example: Bad Request + type: string + type: object + models.Metrics: + properties: + schema: + properties: + models: + type: integer + properties: + type: integer + type: object + security: + properties: + endpoints: + type: integer + type: object + structure: + properties: + methods: + type: integer + operations: + type: integer + paths: + type: integer + type: object + type: object + models.MongoDocument: + properties: + api: + $ref: '#/definitions/models.Api' + date: + type: string + length: + type: integer + metrics: + $ref: '#/definitions/models.Metrics' + mongo-id: + type: string + score: + type: number + specification: + $ref: '#/definitions/models.Specification' + type: object + models.Specification: + properties: + type: + type: string + version: + $ref: '#/definitions/models.Version' + type: object + models.SpecificationBackend: + additionalProperties: true + type: object + models.SpecificationWithApi: + properties: + metadata: + $ref: '#/definitions/models.MongoDocument' + specification: + type: string + type: object + models.SpecificationsRequest: + properties: + specifications: + items: + $ref: '#/definitions/models.SpecificationBackend' + type: array + type: object + models.Version: + properties: + build: + type: string + major: + type: integer + minor: + type: integer + patch: + type: integer + prerelease: + type: string + raw: + type: string + valid: + type: boolean + type: object +info: + contact: {} + description: This is the backend for the API Scout platform. + title: API Scout + version: "1.0" +paths: + /search: + post: + consumes: + - application/json + description: Retrieve OpenAPI specifications matching the given query + parameters: + - default: 1 + description: page number + in: query + minimum: 1 + name: page + type: integer + - default: 10 + description: size of the page + in: query + maximum: 100 + minimum: 1 + name: pageSize + type: integer + - default: 100 + description: knn's k + in: query + maximum: 100 + minimum: 1 + name: k + type: integer + - description: search query + in: body + name: fragment + required: true + schema: + $ref: '#/definitions/models.EmbeddingRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.SpecificationWithApi' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.HTTPError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.HTTPError' + summary: Search OpenAPI specifications + tags: + - search + /specification: + post: + consumes: + - application/json + description: Insert new OpenAPI specifications in the database. + parameters: + - description: New Specifications + in: body + name: specifications + required: true + schema: + $ref: '#/definitions/models.SpecificationsRequest' + produces: + - application/json + responses: + "200": + description: OK + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.HTTPError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.HTTPError' + summary: Insert OpenAPI specifications + tags: + - specification + /specification/{id}: + get: + consumes: + - application/json + description: Retrieve a specific OpenAPI specification's content given a valid + ID + parameters: + - description: Specification ID + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.SpecificationWithApi' + "400": + description: Bad Request + schema: + $ref: '#/definitions/models.HTTPError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/models.HTTPError' + summary: Get OpenAPI specification + tags: + - specification +swagger: "2.0" diff --git a/app/internal/controller/controller.go b/app/internal/controller/controller.go new file mode 100644 index 00000000..de4d5f71 --- /dev/null +++ b/app/internal/controller/controller.go @@ -0,0 +1,40 @@ +package controller + +import ( + "backend/app/internal/elastic" + "backend/app/internal/models" + "backend/app/internal/mongodb" + + "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" +) + +func SetupRoutes(router *gin.Engine, config *models.Config) { + mongoClient := mongodb.Connect(config.Mongo) + elasticClient := elastic.Connect(config.Elastic) + + // Create routes + v1 := router.Group("/api/v1") + { + v1.POST("/search", SearchHandler(mongoClient, elasticClient)) + v1.POST("/preprocess", GetEmbedding) + spec := v1.Group("/specification") + { + spec.POST("/", PostSpecificationHandler(mongoClient, elasticClient)) + spec.GET("/:id", GetSpecificationHandler(mongoClient, elasticClient)) + spec.PUT("/sync", SyncSpecificationsHandler(mongoClient, elasticClient)) + } + } + + router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) +} + +func NewHTTPError(ctx *gin.Context, status int, message string) { + body := models.HTTPError{ + Code: status, + Message: message, + } + + ctx.AbortWithStatusJSON(status, body) +} diff --git a/app/internal/controller/search.go b/app/internal/controller/search.go new file mode 100644 index 00000000..e562738f --- /dev/null +++ b/app/internal/controller/search.go @@ -0,0 +1,168 @@ +package controller + +import ( + "backend/app/internal/elastic" + "backend/app/internal/embedding" + "backend/app/internal/models" + "backend/app/internal/mongodb" + "backend/app/internal/retrieval" + "errors" + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/elastic/go-elasticsearch/v8" + "github.com/gin-gonic/gin" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +// SearchHandler godoc +// +// @Summary Search OpenAPI specifications +// @Description Retrieve OpenAPI specifications matching the given query +// @Tags search +// @Accept json +// @Produce json +// @Param page query int false "page number" minimum(1) default(1) +// @Param size query int false "size of the page" minimum(1) maximum(100) default(10) +// @Param k query int false "knn's k" minimum(1) maximum(100) default(100) +// @Param fragment body models.EmbeddingRequest true "search query" +// @Success 200 {object} []models.SpecificationWithApi +// @Failure 400 {object} models.HTTPError +// @Failure 500 {object} models.HTTPError +// @Router /search [post] +func SearchHandler(mongoClient *mongo.Client, elasticClient *elasticsearch.Client) gin.HandlerFunc { + fn := func(ctx *gin.Context) { + var body models.EmbeddingRequest + pageSize, err := GetQueryValueAndValidate(ctx, "size") + page, err := GetQueryValueAndValidate(ctx, "page") + k, err := GetQueryValueAndValidate(ctx, "k") + + if err != nil { + NewHTTPError(ctx, http.StatusBadRequest, err.Error()) + return + } + + err = ctx.BindJSON(&body) + + if err != nil { + NewHTTPError(ctx, http.StatusBadRequest, "The query has not been correctly formatted") + return + } + + embeddings, _, err := embedding.PerformPipeline([]string{body.Fragment}, true) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, "The embedding backend is not reachable") + return + } + + filters, err := retrieval.ParseDSLRequest(body.DSL) + + if err != nil { + NewHTTPError(ctx, http.StatusBadRequest, err.Error()) + return + } + + if strings.Compare(body.Fragment, "") != 0 && page * pageSize > k { + NewHTTPError(ctx, http.StatusBadRequest, "page * size must not be greater than k") + return + } + + var query string + + if strings.Compare(body.Fragment, "") == 0 { + query = retrieval.CreateKnnQuery(nil, *filters, pageSize, page, k) + } else { + query = retrieval.CreateKnnQuery(embeddings.Predictions[0], *filters, pageSize, page, k) + } + + response, err := elastic.SearchDocument(elasticClient, query, "apis") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + var jsonMaps []models.SpecificationWithApi + db := mongoClient.Database("apis") + + for _, item := range response.Hits.Hits { + objId, err := primitive.ObjectIDFromHex(item.Document.Metadata.MongoId) + document, err := mongodb.RetrieveDocument(db, bson.M{"_id": objId}, "specifications") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + var specObj models.SpecificationWithApi + var jsonMap models.MongoResponse + err = bson.Unmarshal(document, &jsonMap) + specObj.MongoDocument = jsonMap.InitObject() + specObj.MongoDocument.Length = item.Document.Metadata.Length + specObj.MongoDocument.Score = item.Score + specObj.MongoDocument.Metrics = item.Document.Metadata.Metrics + specObj.Specification = document.Lookup("api").String() + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + if len(body.Fields) > 0 { + specObj, err = retrieval.FilterFields(specObj, body.Fields) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + } + + jsonMaps = append(jsonMaps, specObj) + } + + ctx.JSON(http.StatusOK, jsonMaps) + } + + return fn +} + +func GetEmbedding(ctx *gin.Context) { + res := struct { + Query string `json:"query"` + }{} + + err := ctx.BindJSON(&res) + + if err != nil { + NewHTTPError(ctx, http.StatusBadRequest, "The query has not been correctly formatted") + return + } + + res.Query = embedding.PreprocessFragment([]string{res.Query}, false)[0] + + ctx.JSON(http.StatusOK, res) +} + +func GetQueryValueAndValidate(ctx *gin.Context, key string) (int, error) { + if defaultValues, in := models.ParametersMap[key]; in { + if value, present := ctx.GetQuery(key); present { + valueInt, err := strconv.Atoi(value) + + if (err != nil || valueInt < defaultValues[1] || valueInt > defaultValues[2]) && strings.Compare(key, "page") != 0 { + return 0, errors.New(fmt.Sprintf("%s must be a number, >= %d, and <= %d", + key, defaultValues[1], defaultValues[2])) + } else { + return valueInt, nil + } + } else { + return defaultValues[0], nil + } + } + + return 0, errors.New("the passed key is invalid") +} diff --git a/app/internal/controller/specification.go b/app/internal/controller/specification.go new file mode 100644 index 00000000..a08657d5 --- /dev/null +++ b/app/internal/controller/specification.go @@ -0,0 +1,203 @@ +package controller + +import ( + "fmt" + "net/http" + + "backend/app/internal/elastic" + "backend/app/internal/embedding" + "backend/app/internal/models" + "backend/app/internal/mongodb" + "github.com/elastic/go-elasticsearch/v8" + "github.com/gin-gonic/gin" + "github.com/goccy/go-json" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +// GetSpecificationHandler godoc +// +// @Summary Get OpenAPI specification +// @Description Retrieve a specific OpenAPI specification's content given a valid ID +// @Tags specification +// @Accept json +// @Produce json +// @Param id path string true "Specification ID" +// @Success 200 {object} models.SpecificationWithApi +// @Failure 400 {object} models.HTTPError +// @Failure 500 {object} models.HTTPError +// @Router /specification/{id} [get] +func GetSpecificationHandler(mongoClient *mongo.Client, elasticClient *elasticsearch.Client) gin.HandlerFunc { + fn := func(ctx *gin.Context) { + //var body models.EmbeddingRequest + id := ctx.Param("id") + db := mongoClient.Database("apis") + objId, err := primitive.ObjectIDFromHex(id) + + if err != nil { + NewHTTPError(ctx, http.StatusBadRequest, "The id has not been correctly formatted") + return + } + + // Retrieve document based on its id + query := bson.M{"_id": objId} + specDoc, err := mongodb.RetrieveDocument(db, query, "specifications") + + if err != nil { + NewHTTPError(ctx, http.StatusNotFound, "The document with the given ID has not been found") + return + } + + var doc models.MongoResponse + err = bson.Unmarshal(specDoc, &doc) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + queryElastic := fmt.Sprintf( + `{"query": {"nested": {"path": "metadata", "query": {"match": {"metadata.mongo-id": "%s"}}}}}`, + doc.MongoId) + response, err := elastic.SearchDocument(elasticClient, queryElastic, "apis") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + // Unmarshal raw bson into MongoResponse object + var specObj models.SpecificationWithApi + var jsonMap models.MongoResponse + err = bson.Unmarshal(specDoc, &jsonMap) + specObj.MongoDocument = jsonMap.InitObject() + specObj.MongoDocument.Length = response.Hits.Hits[0].Document.Metadata.Length + specObj.MongoDocument.Metrics = response.Hits.Hits[0].Document.Metadata.Metrics + specObj.Specification = specDoc.Lookup("api").String() + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + // Return the JSON representation of the document + ctx.JSON(http.StatusOK, specObj) + } + + return fn +} + +// PostSpecificationHandler godoc +// +// @Summary Insert OpenAPI specifications +// @Description Insert new OpenAPI specifications in the database. +// @Tags specification +// @Accept json +// @Produce json +// @Param specifications body models.SpecificationsRequest true "New Specifications" +// @Success 200 +// @Failure 400 {object} models.HTTPError +// @Failure 500 {object} models.HTTPError +// @Router /specification [post] +func PostSpecificationHandler(mongoClient *mongo.Client, elasticClient *elasticsearch.Client) gin.HandlerFunc { + fn := func(ctx *gin.Context) { + var body models.SpecificationsRequest + var mongoIds []string + var specifications []string + var specificationJSONs []interface{} + + err := ctx.BindJSON(&body) + + if err != nil { + NewHTTPError(ctx, http.StatusBadRequest, "The body has not been correctly formatted") + return + } + + for index := range body.Specifications { + var specification interface{} + + if body.Specifications[index]["api"] == nil { + NewHTTPError(ctx, http.StatusBadRequest, "The body has not been correctly formatted") + return + } + + jsonBody, err := json.Marshal(body.Specifications[index]) + err = json.Unmarshal(jsonBody, &specification) + + if err != nil { + NewHTTPError(ctx, http.StatusBadRequest, "The body has not been correctly formatted") + return + } + + specificationJSONs = append(specificationJSONs, specification) + specifications = append(specifications, string(jsonBody)) + } + + db := mongoClient.Database("apis") + documentIDs, err := mongodb.InsertDocuments(db, specificationJSONs, "specifications") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + var embeddings *models.EmbeddingResponse + embeddings, length, err := embedding.PerformPipeline(specifications, false) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + for index, embeddingVal := range embeddings.Predictions { + jsonSpecification, err := json.Marshal(specificationJSONs[index]) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + var request models.EsRequest + err = json.Unmarshal(jsonSpecification, &request.MongoDocument) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + var mongoRes models.MongoResponse + mongoId := documentIDs.InsertedIDs[index].(primitive.ObjectID) + mongoIds = append(mongoIds, mongoId.Hex()) + + document, err := mongodb.RetrieveDocument(mongoClient.Database("apis"), bson.M{"_id": mongoId}, "specifications") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + err = bson.Unmarshal(document, &mongoRes) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + request.MongoDocument = *mongoRes.InitObject() + request.MongoDocument.Length = length[index] + request.Embedding = embeddingVal + + err = elastic.InsertDocument(elasticClient, request, "apis") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + } + + ctx.Status(200) + } + + return fn +} diff --git a/app/internal/controller/sync.go b/app/internal/controller/sync.go new file mode 100644 index 00000000..92a7a88d --- /dev/null +++ b/app/internal/controller/sync.go @@ -0,0 +1,170 @@ +package controller + +import ( + "context" + "fmt" + "go.mongodb.org/mongo-driver/mongo/options" + "log" + "net/http" + "strconv" + "strings" + "time" + + "backend/app/internal/elastic" + "backend/app/internal/embedding" + "backend/app/internal/models" + "backend/app/internal/mongodb" + + "github.com/elastic/go-elasticsearch/v8" + "github.com/gin-gonic/gin" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" +) + +func SyncSpecificationsHandler(mongoClient *mongo.Client, elasticClient *elasticsearch.Client) gin.HandlerFunc { + fn := func(ctx *gin.Context) { + var documents *mongo.Cursor + var err error + + skip := 0 + skipQuery := ctx.Query("skip") + + if strings.Compare(skipQuery, "") != 0 && strings.Compare(skipQuery, "auto") != 0 { + skip, err = strconv.Atoi(skipQuery) + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, "the skip parameter must be a number") + return + } + } else if strings.Compare(skipQuery, "auto") == 0 { + count, err := elastic.GetDocumentCount(elasticClient, "apis") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + skip = int(count) + } + + documents, err = mongodb.RetrieveDocuments(mongoClient.Database("apis"), bson.D{{}}, "specifications") + + if err != nil { + NewHTTPError(ctx, http.StatusInternalServerError, err.Error()) + return + } + + current := 1 + total, err := mongoClient.Database("apis").Collection("specifications").CountDocuments( + context.TODO(), + bson.D{}, + options.Count().SetHint("_id_"), + ) + + if err != nil { + panic(err) + } + + var prec int64 + + for documents.Next(context.TODO()) { + start := time.Now().UnixNano() + + percentage := (float64(current) / float64(total)) * 100.0 + totTime := (float64(prec) * float64(total - int64(current))) / 3600000000000.0 + log.Printf("Saving document %d/%d - [%.1f%%] - Done in: %.1fh", current, total, percentage, totTime) + + if skip > current { + log.Printf("Skip query set to %d, skipping...", skip) + + prec = time.Now().UnixNano()-start + current++ + + continue + } + + var document models.MongoResponse + specification := documents.Current.Lookup("api") + err := documents.Decode(&document) + + if err != nil { + log.Print("Error decoding document, skipping...",) + continue + } + + log.Printf("Mongo ID: %s", document.MongoId) + + mongoDocument := document.InitObject() + query := fmt.Sprintf( + `{"query": {"nested": {"path": "metadata", "query": {"match": {"metadata.mongo-id": "%s"}}}}}`, + mongoDocument.MongoId) + res, err := elastic.SearchDocument(elasticClient, query, "apis") + + if err != nil { + log.Print("Error retrieving document, skipping...",) + continue + } + + if len(res.Hits.Hits) == 0 { + var embeddings *models.EmbeddingResponse + embeddings, length, err := embedding.PerformPipeline([]string{specification.String()}, false) + + if err != nil { + log.Print("Error embedding document, skipping...",) + continue + } + + if len(embeddings.Predictions) != 0 { + var esDocument models.EsRequest + esDocument.MongoDocument = *mongoDocument + esDocument.MongoDocument.Length = length[0] + esDocument.Embedding = embeddings.Predictions[0] + + id, err := primitive.ObjectIDFromHex(mongoDocument.MongoId) + + if err != nil { + log.Print("Error decoding document, skipping...",) + continue + } + + var metricsDocument models.Metrics + + database := mongoClient.Database("apis") + metrics, err := mongodb.RetrieveDocument(database, bson.M{"_id": id}, "metrics") + + if err != nil { + log.Print("No metrics found for this document") + continue + } + + err = bson.Unmarshal(metrics, &metricsDocument) + + if err != nil { + log.Print("Error decoding metrics, skipping...",) + continue + } + + esDocument.MongoDocument.Metrics = metricsDocument + err = elastic.InsertDocument(elasticClient, esDocument, "apis") + + if err != nil { + log.Print("Error inserting document, skipping...",) + continue + } + } else { + log.Print("No embedding was produced, skipping...") + } + } else { + log.Printf("Already exists, skipping...") + } + + stop := time.Now().UnixNano() + prec = stop-start + + current++ + } + } + + return fn +} diff --git a/app/internal/doc2go.rc b/app/internal/doc2go.rc new file mode 100644 index 00000000..0aa77bd8 --- /dev/null +++ b/app/internal/doc2go.rc @@ -0,0 +1,3 @@ +pagefind +out ./docs +home backend/app/internal/ diff --git a/app/internal/elastic/elastic.go b/app/internal/elastic/elastic.go index 0a1006e5..08735825 100644 --- a/app/internal/elastic/elastic.go +++ b/app/internal/elastic/elastic.go @@ -1,29 +1,31 @@ package elastic import ( + "backend/app/internal/models" "fmt" - "os" + "log" "github.com/elastic/go-elasticsearch/v8" ) - -func Connect(host string, port int, user string, password string) (*elasticsearch.Client, error) { +// Connect - used to connect to the elasticsearch database. It will return an elasticsearch client that can be used to +// perform queries on the database. +func Connect(config models.ElasticConfig) *elasticsearch.Client { esConfig := elasticsearch.Config{ Addresses: []string{ - fmt.Sprintf("https://%s:%d", host, port), + fmt.Sprintf("%s://%s:%d", config.Protocol, config.Host, config.Port), }, - Username: user, - Password: password, - CACert: GetCertificate(), + Username: config.User, + Password: config.Password, } - return elasticsearch.NewClient(esConfig) -} + client, err := elasticsearch.NewClient(esConfig) + + if err != nil { + panic(err) + } -func GetCertificate() []byte { - pwd, _ := os.Getwd() - cert, _ := os.ReadFile(pwd + "/ca.crt") + log.Print("Connected to ElasticSearch") - return cert + return client } diff --git a/app/internal/elastic/operations.go b/app/internal/elastic/operations.go new file mode 100644 index 00000000..ca7d5c3b --- /dev/null +++ b/app/internal/elastic/operations.go @@ -0,0 +1,114 @@ +package elastic + +import ( + "bytes" + "context" + "errors" + "io" + "log" + "net/http" + "os" + "strings" + + "backend/app/internal/models" + + "github.com/elastic/go-elasticsearch/v8" + "github.com/goccy/go-json" +) + +// InsertDocument - send and save a document in an elasticsearch index. An elasticsearch client, document and index need +// to be passed to the function. +func InsertDocument(client *elasticsearch.Client, document models.EsRequest, index string) error { + jsonDocument, err := json.Marshal(document) + + if err != nil { + return err + } + + res, err := client.Index(index, bytes.NewReader(jsonDocument)) + + if res != nil && res.StatusCode == http.StatusCreated { + if os.Getenv("GIN_MODE") == "debug" { + log.Printf("[ELASTIC-debug] Indexing: %s", res.Status()) + } + } else { + return errors.New("could not insert document") + } + + return err +} + +// SearchDocument - search a document in an index based on a query. An elasticsearch client, a query and an index need +// to be passed to the function. +func SearchDocument(client *elasticsearch.Client, query string, index string) (*models.EsSearchResponse, error) { + var response models.EsSearchResponse + + res, err := client.Search( + client.Search.WithIndex(index), + client.Search.WithBody(strings.NewReader(query)), + client.Search.WithContext(context.TODO()), + ) + + if err != nil { + return nil, err + } + + if os.Getenv("GIN_MODE") == "debug" { + log.Printf("[ELASTIC-debug] Search: %s", res.Status()) + } + + // Read the body of the elasticsearch response + out := new(bytes.Buffer) + b1 := bytes.NewBuffer([]byte{}) + tr := io.TeeReader(res.Body, b1) + _, err = out.ReadFrom(tr) + + if err != nil { + return nil, err + } + + // Unmarshal the byte array into the response + err = json.Unmarshal([]byte(out.String()), &response) + + return &response, err +} + +func GetDocumentCount(client *elasticsearch.Client, index string) (int64, error) { + var docsCount struct{Count int64 `json:"count"`} + + res, err := client.Count( + client.Count.WithIndex(index), + client.Count.WithContext(context.TODO()), + ) + + if err != nil { + return 0, err + } + + out := new(bytes.Buffer) + b1 := bytes.NewBuffer([]byte{}) + tr := io.TeeReader(res.Body, b1) + _, err = out.ReadFrom(tr) + + if err != nil { + return 0, err + } + + err = json.Unmarshal([]byte(out.String()), &docsCount) + + return docsCount.Count, err +} + +// DeleteDocument - delete a document in an index based on its id. An elasticsearch client, an id and an index need to +// be passed to the function. +func DeleteDocument(client *elasticsearch.Client, id string, index string) error { + res, err := client.Delete(index, id) + + if err == nil { + if os.Getenv("GIN_MODE") == "debug" { + log.Printf("[ELASTIC-debug] Delete: %s", res.Status()) + } + } + + return err +} diff --git a/app/internal/embedding/embedding.go b/app/internal/embedding/embedding.go index f76da130..f6533e7c 100644 --- a/app/internal/embedding/embedding.go +++ b/app/internal/embedding/embedding.go @@ -1,18 +1,18 @@ package embedding import ( - "backend/app/internal/structs" "bytes" "encoding/json" "log" "net/http" "os" -) - -type Embeddings = structs.Embeddings + "backend/app/internal/models" +) -func Embed(fragments []string) *Embeddings { +// Embed use the Universal Sentence Encoder model to transform the array of fragments (string) into an array of +// embeddings (512-dimension float32 embedding). A list of embeddings needs to be passed to the function. +func Embed(fragments []string) (*models.EmbeddingResponse, error) { body, _ := json.Marshal(map[string][]string{ "instances": fragments, }) @@ -20,18 +20,18 @@ func Embed(fragments []string) *Embeddings { // Call embedding model reqBody := bytes.NewBuffer(body) res, err := http.Post( - "http://" + os.Getenv("MODELS_HOST") + ":8501/v1/models/universal-encoder:predict", + "http://"+os.Getenv("MODELS_HOST")+":8501/v1/models/universal-encoder:predict", "application/json", reqBody, ) // Error handling if err != nil { - log.Fatal(err) + return nil, err } // Decode the JSON body containing the embeddings - embeddings := new(Embeddings) + embeddings := new(models.EmbeddingResponse) err = json.NewDecoder(res.Body).Decode(embeddings) // Error handling @@ -39,5 +39,5 @@ func Embed(fragments []string) *Embeddings { log.Fatal(err) } - return embeddings + return embeddings, err } diff --git a/app/internal/embedding/pipeline.go b/app/internal/embedding/pipeline.go index 31329889..ee99c22b 100644 --- a/app/internal/embedding/pipeline.go +++ b/app/internal/embedding/pipeline.go @@ -1,9 +1,29 @@ package embedding +import ( + "backend/app/internal/models" + "errors" +) -func PerformPipeline(fragments []string, isQuery bool) *Embeddings { +// PerformPipeline - fragments are preprocessed and embeddings are generated and returned. An array of fragments +// (string) and a boolean indicating if the fragments are queries or not need to be passed to the function. +func PerformPipeline(fragments []string, isQuery bool) (*models.EmbeddingResponse, []int, error) { + var preprocessedLengths []int preprocessed := PreprocessFragment(fragments, isQuery) - embeddings := Embed(preprocessed) - return embeddings + if preprocessed == nil { + return nil, []int{}, errors.New("no fragments were generated") + } + + embeddings, err := Embed(preprocessed) + + if err != nil { + return nil, []int{}, err + } + + for _, preprocessedItem := range preprocessed { + preprocessedLengths = append(preprocessedLengths, len(preprocessedItem)) + } + + return embeddings, preprocessedLengths, nil } diff --git a/app/internal/embedding/preprocessing.go b/app/internal/embedding/preprocessing.go index 5fef0fb0..57e6077c 100644 --- a/app/internal/embedding/preprocessing.go +++ b/app/internal/embedding/preprocessing.go @@ -10,7 +10,9 @@ import ( stripmd "github.com/writeas/go-strip-markdown" ) - +// PreprocessFragment - fragments are preprocessed by running a standard NLP pipeline, composed of string cleaning, +// stop-word removal, and stemming. An array of fragments (string), and a boolean indicating is the fragments are +// queries or not need to be passed to the function. func PreprocessFragment(fragments []string, isQuery bool) []string { cleanFragment := fragments @@ -18,12 +20,15 @@ func PreprocessFragment(fragments []string, isQuery bool) []string { cleanFragment = ExtractTags(cleanFragment) } - return Stemming(StopWordRemoval(cleanFragment)) + return cleanFragment } +// ExtractTags - extract the NL tags from a fragment (JSON document documenting a REST API), and return an array of +// strings, one for each fragment. An array of fragments needs to be passed to the function. func ExtractTags(fragments []string) []string { var nlFragments []string - nlTagsRegex := regexp.MustCompile(`['"](?:description|name|title|summary)['"]:\s"([^"]+)"|'([^']+)'`) + nlTagsRegex := regexp.MustCompile(`['"](?:description|name|title|summary)['"]:\s?(?:"([^"]+)"|'([^']+)')`) + urlsRegex := regexp.MustCompile(`https?://(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)`) for _, fragment := range fragments { var nlFragmentTags []string @@ -39,13 +44,20 @@ func ExtractTags(fragments []string) []string { } } + // Remove Markdown syntax + cleanFragment := stripmd.Strip(strings.Join(nlFragmentTags, " ")) + // Remove URLs + cleanFragment = urlsRegex.ReplaceAllString(cleanFragment, "") + // Join all extracted strings, remove all Markdown formatting, and append to fragments array - nlFragments = append(nlFragments, stripmd.Strip(strings.Join(nlFragmentTags, " "))) + nlFragments = append(nlFragments, cleanFragment) } return nlFragments } +// StopWordRemoval - remove all stopwords from the given strings. An array of strings needs to be passed to the +// function. func StopWordRemoval(fragments []string) []string { var newFragments []string @@ -59,9 +71,11 @@ func StopWordRemoval(fragments []string) []string { return newFragments } +// Stemming - stem all the words contained in the given strings. An array of strings needs to be passed to the +// function. func Stemming(fragments []string) []string { var stemmed []string - f := func (r rune) bool { return unicode.IsSpace(r) } + f := func(r rune) bool { return unicode.IsSpace(r) } for _, fragment := range fragments { var stemmedWords []string @@ -71,7 +85,7 @@ func Stemming(fragments []string) []string { for _, word := range words { // Perform stemming and append to stemmed words array engStemmer := porter2.Stemmer - //Trim non-alphanumeric characters from strin + //Trim non-alphanumeric characters from string trimmedWord := strings.TrimFunc(word, func(r rune) bool { return !unicode.IsLetter(r) && !unicode.IsNumber(r) }) diff --git a/app/internal/helpers/certificate.go b/app/internal/helpers/certificate.go new file mode 100644 index 00000000..9fb209d1 --- /dev/null +++ b/app/internal/helpers/certificate.go @@ -0,0 +1,13 @@ +package helpers + +import ( + "os" +) + +// GetCertificate - retrieve the elasticsearch cluster certificate. +func GetCertificate() []byte { + pwd, _ := os.Getwd() + cert, _ := os.ReadFile(pwd + "/ca.crt") + + return cert +} diff --git a/app/internal/helpers/config.go b/app/internal/helpers/config.go index 58af9123..b80dfaa4 100644 --- a/app/internal/helpers/config.go +++ b/app/internal/helpers/config.go @@ -1,24 +1,25 @@ package helpers import ( + "fmt" "log" "os" - "backend/app/internal/structs" + "backend/app/internal/models" "github.com/kelseyhightower/envconfig" "gopkg.in/yaml.v2" ) +type Config = models.Config -type Config = structs.Config - +// LoadConfigs - parse and store in a struct all the config values needed by the backend. func LoadConfigs() Config { var cfg Config // Read config file pwd, err := os.Getwd() - file, err := os.Open(pwd + "/config/local.config.yml") + file, err := os.Open(pwd + fmt.Sprintf("/config/%s.config.yml", os.Getenv("GIN_MODE"))) // Handle errors defer func(file *os.File) { diff --git a/app/internal/models/config.go b/app/internal/models/config.go new file mode 100644 index 00000000..43d3a1fb --- /dev/null +++ b/app/internal/models/config.go @@ -0,0 +1,31 @@ +package models + +// Config - used to store all configurations needed by the backend. +type Config struct { + Backend BackendConfig `yaml:"backend"` + Mongo MongoConfig `yaml:"mongodb"` + Elastic ElasticConfig `yaml:"elastic"` +} + +// BackendConfig - all backend-related configurations. +type BackendConfig struct { + Port int `yaml:"port"` +} + +// MongoConfig - all mongodb-related configurations. +type MongoConfig struct { + Protocol string `yaml:"protocol"` + Host string `yaml:"host"` + Port int `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` +} + +// ElasticConfig - all elasticsearch-related configurations. +type ElasticConfig struct { + Protocol string `yaml:"protocol"` + Host string `yaml:"host"` + Port int `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` +} diff --git a/app/internal/models/dsl.go b/app/internal/models/dsl.go new file mode 100644 index 00000000..6b63fa4f --- /dev/null +++ b/app/internal/models/dsl.go @@ -0,0 +1,150 @@ +package models + +import ( + "errors" + "slices" + "strconv" + "strings" + "time" +) + +var Operators = []string{"==", "!=", "~=", "<>", ">=", "<=", ">", "<"} +var NegOperators = []string{"!="} + +var OperatorsMap = map[string][]string{ + "==": {"bool", "str", "int", "version", "date"}, + "!=": {"bool", "str", "int", "version", "date"}, + "~=": {"str", "version"}, + ">=": {"int", "version", "date"}, + "<=": {"int", "version", "date"}, + ">": {"int", "version", "date"}, + "<": {"int", "version", "date"}, +} + +var TypesMap = map[string]string{ + "date": "date", + "length": "int", + + "api.version.raw": "version", + "api.version.valid": "bool", + "api.version.major": "int", + "api.version.minor": "int", + "api.version.patch": "int", + "api.version.prerelease": "str", + "api.version.build": "str", + "api.name": "str", + "api.commits": "int", + "api.latest": "bool", + "api.source": "str", + "api.id": "int", + + "specification.version.raw": "version", + "specification.version.valid": "bool", + "specification.version.major": "int", + "specification.version.minor": "int", + "specification.version.patch": "int", + "specification.version.prerelease": "str", + "specification.version.build": "str", + "specification.type": "str", + + "metrics.security.endpoints": "int", + "metrics.schema.models": "int", + "metrics.schema.properties": "int", + "metrics.structure.paths": "int", + "metrics.structure.operations": "int", + "metrics.structure.methods": "int", +} + +var OperatorToEsMap = map[string]string{ + "==": "term", + "!=": "term", + "~=": "regexp", + ">=": "gte", + "<=": "lte", + ">": "gt", + "<": "lt", +} + +var BracketsMap = map[string]string{ + "[": ">=", + "(": ">", + "]": "<=", + ")": "<", +} + +type Filter struct { + Lhs string + Operator string + Rhs string +} + +func (filter *Filter) Validate() error { + acceptedType := TypesMap[filter.Lhs] + operatorTypes := OperatorsMap[filter.Operator] + + if slices.Contains(operatorTypes, acceptedType) { + switch acceptedType { + case "str": + if strings.Compare(string(filter.Rhs[0]), `"`) == 0 && + strings.Compare(string(filter.Rhs[len(filter.Rhs)-1]), `"`) == 0 && + strings.Compare(filter.Rhs, "") != 0 { + + filter.Rhs = strings.Trim(filter.Rhs, `"`) + return nil + } + case "version": + if filter.Operator == "~=" || GetSemanticVersion(filter.Rhs).Valid { + return nil + } + case "bool": + if strings.Compare(filter.Rhs, "true") == 0 || strings.Compare(filter.Rhs, "false") == 0 { + return nil + } + case "int": + if _, err := strconv.ParseInt(filter.Rhs, 10, 64); err == nil { + return nil + } + case "date": + if res, err := time.Parse("02/01/2006", filter.Rhs); err == nil { + filter.Rhs = res.Format(time.RFC3339) + return nil + } + } + } + + return errors.New("the type of the right hand side does not match the ones of the operator") +} + +func (filter *Filter) ToEsFilter() (string, bool) { + var esFilter strings.Builder + rhs := filter.Rhs + esOperator := OperatorToEsMap[filter.Operator] + positive := !slices.Contains(NegOperators, filter.Operator) + + switch esOperator { + case "term": + if TypesMap[filter.Lhs] != "boolean" && TypesMap[filter.Lhs] != "int" { + rhs = `"` + rhs + `"` + } + + if strings.Compare(filter.Rhs, "true") == 0 { + esFilter.WriteString(`"term": {"metadata.` + filter.Lhs + `": true}`) + break + } else if strings.Compare(filter.Rhs, "false") == 0 { + esFilter.WriteString(`"term": {"metadata.` + filter.Lhs + `": false}`) + break + } + + esFilter.WriteString(`"term": {"metadata.` + filter.Lhs + `": ` + rhs + `}`) + case "regexp": + esFilter.WriteString(`"regexp": {"metadata.` + filter.Lhs + `": ".*` + filter.Rhs + `.*"}`) + case "gte", "lte", "gt", "lt": + if TypesMap[filter.Lhs] == "version" || TypesMap[filter.Lhs] == "date" { + rhs = `"` + rhs + `"` + } + + esFilter.WriteString(`"range": {"metadata.` + filter.Lhs + `": {"` + esOperator + `": ` + rhs + `}}`) + } + + return esFilter.String(), positive +} diff --git a/app/internal/models/elastic.go b/app/internal/models/elastic.go new file mode 100644 index 00000000..b2eaab8a --- /dev/null +++ b/app/internal/models/elastic.go @@ -0,0 +1,29 @@ +package models + +// EsRequest - structure of an elasticsearch document to be sent to the elasticsearch client. +type EsRequest struct { + MongoDocument MongoDocument `json:"metadata"` + Embedding []float32 `json:"embedding"` +} + +// EsSearchResponse - structure of the response sent by the elasticsearch client +type EsSearchResponse struct { + Hits struct { + Hits []Hit `json:"hits"` + } `json:"hits"` +} + +// Hit - structure of an elasticsearch document +type Hit struct { + Id string `json:"_id"` + Index string `json:"_index"` + Score float64 `json:"_score"` + Document struct { + Metadata struct { + MongoId string `json:"mongo-id"` + Length int `json:"length"` + Metrics Metrics `json:"metrics"` + } `json:"metadata"` + Embedding []float32 `json:"embedding"` + } `json:"_source"` +} diff --git a/app/internal/models/embeddings.go b/app/internal/models/embeddings.go new file mode 100644 index 00000000..75b21162 --- /dev/null +++ b/app/internal/models/embeddings.go @@ -0,0 +1,15 @@ +package models + +var PossibleFilters = []string{"metadata", "specification"} + +// EmbeddingRequest - structure of the request to be sent to the embedding server. +type EmbeddingRequest struct { + Fragment string `json:"fragment,omitempty"` + DSL string `json:"filters,omitempty"` + Fields []string `json:"fields,omitempty"` +} + +// EmbeddingResponse - structure of the response sent back by the embedding server. +type EmbeddingResponse struct { + Predictions [][]float32 +} diff --git a/app/internal/models/error.go b/app/internal/models/error.go new file mode 100644 index 00000000..683ead49 --- /dev/null +++ b/app/internal/models/error.go @@ -0,0 +1,7 @@ +package models + +// HTTPError - structure of the error response sent by the backend +type HTTPError struct { + Code int `json:"code" example:"400"` + Message string `json:"message" example:"Bad Request"` +} diff --git a/app/internal/models/knn.go b/app/internal/models/knn.go new file mode 100644 index 00000000..787cfcc5 --- /dev/null +++ b/app/internal/models/knn.go @@ -0,0 +1,8 @@ +package models + +// ParametersMap - The elements of the map are structured as follows: "queryKey": {default, min, max} +var ParametersMap = map[string][]int{ + "size": {10, 1, 120}, + "page": {1, 1, -1}, + "k": {100, 1, 120}, +} diff --git a/app/internal/models/mapping.go b/app/internal/models/mapping.go new file mode 100644 index 00000000..a69045fd --- /dev/null +++ b/app/internal/models/mapping.go @@ -0,0 +1,86 @@ +package models + +var mapping = `{ + "mappings": { + "properties": { + "metadata": { + "type": "nested", + "properties": { + "mongo-id": { "type": "text" }, + "length": { "type": "long" }, + "date": { "type": "date" }, + "api": { + "type": "nested", + "properties": { + "name": { "type": "text" }, + "id": { "type": "text" }, + "commits": { "type": "long" }, + "latest": { "type": "boolean" }, + "source": { "type": "text" }, + "version": { + "type": "nested", + "properties": { + "raw": { "type": "version" }, + "valid": { "type": "boolean" }, + "major": { "type": "long" }, + "minor": { "type": "long" }, + "patch": { "type": "long" }, + "prerelease": { "type": "text" }, + "build": { "type": "text" } + } + } + } + }, + "metrics": { + "type": "nested", + "properties": { + "security": { + "type": "nested", + "properties": { + "endpoints": { "type": "long" } + } + }, + "schema": { + "type": "nested", + "properties": { + "models": { "type": "long" }, + "properties": { "type": "long" } + } + }, + "structure": { + "type": "nested", + "properties": { + "paths": { "type": "long" }, + "operations": { "type": "long" }, + "methods": { "type": "long" } + } + } + } + }, + "specification": { + "type": "nested", + "properties": { + "type": { "type": "text" }, + "version": { + "type": "nested", + "properties": { + "raw": { "type": "version" }, + "valid": { "type": "boolean" }, + "major": { "type": "long" }, + "minor": { "type": "long" }, + "patch": { "type": "long" }, + "prerelease": { "type": "text" }, + "build": { "type": "text" } + } + } + } + } + } + }, + "embedding": { + "type": "dense_vector", + "dims": 512 + } + } + } +}` diff --git a/app/internal/models/mongo.go b/app/internal/models/mongo.go new file mode 100644 index 00000000..426246b4 --- /dev/null +++ b/app/internal/models/mongo.go @@ -0,0 +1,174 @@ +package models + +import ( + "go.mongodb.org/mongo-driver/bson" + "regexp" + "strconv" + "strings" + "time" +) + +// MongoResponse - structure of the Mongo document sent by the db +type MongoResponse struct { + MongoId string `bson:"_id"` + Id int `bson:"api_spec_id"` + Name string `bson:"_name"` + Commits int `bson:"commits"` + Latest bool `bson:"latest"` + Score bool + OASType string + Source string + Date time.Time + ApiVersion Version + OASVersion Version + + SpecificationJson bson.Raw `bson:"api"` + NameAlt string `bson:"api_title"` + ApiVersionAlt1 string `bson:"_version"` + ApiVersionAlt2 string `bson:"api_version"` + SourceAlt1 string `bson:"_api_url"` + SourceAlt2 string `bson:"url"` + DateAlt1 time.Time `bson:"_created_at"` + DateAlt2 time.Time `bson:"commit_date"` +} + +type MongoDocument struct { + MongoId string `json:"mongo-id"` + Length int `json:"length"` + Date time.Time `json:"date"` + Score float64 `json:"score,omitempty"` + Api Api `json:"api"` + Metrics Metrics `json:"metrics"` + Specification Specification `json:"specification"` +} + +type Api struct { + Id int `json:"id"` + Name string `json:"name"` + Version Version `json:"version"` + Commits int `json:"commits"` + Latest bool `json:"latest"` + Source string `json:"source"` +} + +type Metrics struct { + Security struct { + Endpoints int `json:"endpoints" bson:"endpointsCount"` + } `json:"security" bson:"securityData"` + Schema struct { + Models int `json:"models" bson:"schemas"` + Properties int `json:"properties" bson:"properties"` + } `json:"schema" bson:"schemaSize"` + Structure struct { + Paths int `json:"paths" bson:"paths"` + Operations int `json:"operations" bson:"operations"` + Methods int `json:"methods" bson:"used_methods"` + } `json:"structure" bson:"structureSize"` +} + +type Specification struct { + Version Version `json:"version"` + Type string `json:"type"` +} + +type Version struct { + Raw string `json:"raw"` + Valid bool `json:"valid"` + Major int `json:"major"` + Minor int `json:"minor"` + Patch int `json:"patch"` + Prerelease string `json:"prerelease"` + Build string `json:"build"` +} + +// SpecificationWithApi - structure containing both the mongo document and the embedding created by the backend +type SpecificationWithApi struct { + MongoDocument *MongoDocument `json:"metadata,omitempty"` + Specification string `json:"specification,omitempty"` +} + +// InitObject - function to fix the initiated object +func (b *MongoResponse) InitObject() *MongoDocument { + GetOasVersion(b) + + if strings.Compare(b.NameAlt, "") != 0 { + b.Name = b.NameAlt + b.ApiVersion = GetSemanticVersion(b.ApiVersionAlt2) + b.Source = GetSource(b.SourceAlt2) + b.Date = b.DateAlt2 + } else { + b.ApiVersion = GetSemanticVersion(b.ApiVersionAlt1) + b.Source = GetSource(b.SourceAlt1) + b.Date = b.DateAlt1 + } + + return &MongoDocument{ + MongoId: b.MongoId, + Date: b.Date, + Api: Api{ + Id: b.Id, + Name: b.Name, + Version: b.ApiVersion, + Commits: b.Commits, + Latest: b.Latest, + Source: b.Source, + }, + Specification: Specification{ + Version: b.OASVersion, + Type: b.OASType, + }, + } +} + +func GetOasVersion(specification *MongoResponse) { + oasOpenapi := specification.SpecificationJson.Lookup("openapi").String() + oasSwagger := specification.SpecificationJson.Lookup("swagger").String() + oasOpenapi = strings.Trim(oasOpenapi, `\\"`) + oasSwagger = strings.Trim(oasSwagger, `\\"`) + + if strings.Compare(oasOpenapi, "") != 0 { + specification.OASVersion = GetSemanticVersion(oasOpenapi) + specification.OASType = "openapi" + } else { + specification.OASVersion = GetSemanticVersion(oasSwagger) + specification.OASType = "swagger" + } +} + +func GetSemanticVersion(version string) Version { + regex := regexp.MustCompile("^(?P0|[1-9]\\d*)\\.(?P0|[1-9]\\d*)\\.(?P0|[1-9]\\d*)(?:-(?P(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+(?P[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$") + major := regex.SubexpIndex("major") + minor := regex.SubexpIndex("minor") + patch := regex.SubexpIndex("patch") + prerelease := regex.SubexpIndex("prerelease") + build := regex.SubexpIndex("build") + + var semanticVersion Version + semanticVersion.Raw = strings.TrimLeft(version, "v") + matches := regex.FindStringSubmatch(version) + + if matches != nil { + major, _ := strconv.ParseInt(matches[major], 10, 32) + minor, _ := strconv.ParseInt(matches[minor], 10, 32) + patch, _ := strconv.ParseInt(matches[patch], 10, 32) + + semanticVersion.Valid = true + semanticVersion.Major = int(major) + semanticVersion.Minor = int(minor) + semanticVersion.Patch = int(patch) + semanticVersion.Prerelease = matches[prerelease] + semanticVersion.Build = matches[build] + } + + return semanticVersion +} + +func GetSource(url string) string { + if strings.Contains(url, "github") { + return "github" + } else if strings.Contains(url, "swagger") { + return "swaggerhub" + } + + return "" +} diff --git a/app/internal/models/specifications.go b/app/internal/models/specifications.go new file mode 100644 index 00000000..2b827b26 --- /dev/null +++ b/app/internal/models/specifications.go @@ -0,0 +1,9 @@ +package models + +// SpecificationsRequest - structure of the request to be sent to the backend whenever new specifications are added +type SpecificationsRequest struct { + Specifications []SpecificationBackend `json:"specifications"` +} + +// SpecificationBackend - type of the single specification +type SpecificationBackend map[string]interface{} diff --git a/app/internal/mongo/pipeline.go b/app/internal/mongo/pipeline.go deleted file mode 100644 index 509499c1..00000000 --- a/app/internal/mongo/pipeline.go +++ /dev/null @@ -1,3 +0,0 @@ -package mongo - - diff --git a/app/internal/mongodb/mongodb.go b/app/internal/mongodb/mongodb.go new file mode 100644 index 00000000..7dff2f57 --- /dev/null +++ b/app/internal/mongodb/mongodb.go @@ -0,0 +1,33 @@ +package mongodb + +import ( + "context" + "fmt" + "log" + + "backend/app/internal/models" + + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +// Connect - used to connect to the mongodb database. It will return a mongodb client that can be used to perform +// queries on the database. +func Connect(config models.MongoConfig) *mongo.Client { + ctx := context.TODO() + uri := fmt.Sprintf( + "%s://%s:%s@%s:%d/?authSource=apis", + config.Protocol, config.User, config.Password, config.Host, config.Port, + ) + opts := options.Client().ApplyURI(uri) + + // Create a new client and connect to the server + client, err := mongo.Connect(ctx, opts) + + if err != nil { + panic(err) + } + log.Print("Connected to MongoDB") + + return client +} diff --git a/app/internal/mongodb/operations.go b/app/internal/mongodb/operations.go new file mode 100644 index 00000000..3b071d85 --- /dev/null +++ b/app/internal/mongodb/operations.go @@ -0,0 +1,34 @@ +package mongodb + +import ( + "context" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +// RetrieveDocument - search a document in a collection based on a query. A mongodb database, a query and a collection +// need to be passed to the function. +func RetrieveDocument(database *mongo.Database, query bson.M, collection string) (bson.Raw, error) { + coll := database.Collection(collection) + res, err := coll.FindOne(context.Background(), query).Raw() + + return res, err +} + +// RetrieveDocuments - search documents in a collection based on a query. A mongodb database, a query and a collection +// need to be passed to the function. +func RetrieveDocuments(database *mongo.Database, query bson.D, collection string) (*mongo.Cursor, error) { + coll := database.Collection(collection) + res, err := coll.Find(context.Background(), query) + + return res, err +} + +// InsertDocuments - insert documents in a collection based on a query. A mongodb database, a document and a collection +// need to be passed to the function. +func InsertDocuments(database *mongo.Database, documents []interface{}, collection string) (*mongo.InsertManyResult, error) { + coll := database.Collection(collection) + ids, err := coll.InsertMany(context.Background(), documents) + + return ids, err +} diff --git a/app/internal/retrieval/dsl.go b/app/internal/retrieval/dsl.go new file mode 100644 index 00000000..7edb65d4 --- /dev/null +++ b/app/internal/retrieval/dsl.go @@ -0,0 +1,162 @@ +package retrieval + +import ( + "errors" + "slices" + "strings" + + "backend/app/internal/models" +) + +func ParseDSLRequest(dslString string) (*string, error) { + filters, err := CreateFilters(strings.Split(dslString, " ")) + + if err != nil { + return nil, err + } + + return CreateEsFilter(filters), nil +} + +func CreateEsFilter(filters []models.Filter) *string { + var must strings.Builder + var mustNot strings.Builder + var esFilter strings.Builder + esFilter.WriteString(`"bool": {`) + + filterSource := false + filterCommits := false + filterLength := false + + for index, filter := range filters { + pathArray := strings.Split(filter.Lhs, ".") + path := strings.Join(slices.Delete(pathArray, len(pathArray)-1, len(pathArray)), ".") + + if len(path) != 0 { + path = "." + path + } + + parsed, positive := filter.ToEsFilter() + query := `{"nested": {"path": "metadata` + path + `", "query": {` + parsed + "}}}" + + if index != len(filters)-1 { + query += "," + } + + if positive { + must.WriteString(query) + } else { + mustNot.WriteString(query) + } + + filterSource = filter.Lhs == "api.source" + filterCommits = filter.Lhs == "api.id" || filter.Lhs == "api.latest" + filterLength = filter.Lhs == "length" + } + + esFilter.WriteString(`"must": [` + must.String()) + + if len(must.String()) != 0 { + esFilter.WriteString(", ") + } + + if !filterCommits { + esFilter.WriteString(`{"nested": {"path": "metadata.api", "query": {"term": {"metadata.api.latest": true}}}}, `) + } + + if !filterLength { + esFilter.WriteString(`{"nested": {"path": "metadata", "query": {"range": {"metadata.length": {"gte": 500}}}}}, `) + } + + if !filterSource { + esFilter.WriteString(`{"nested": {"path": "metadata.api", "query": {"regexp": {"metadata.api.source": ".+"}}}}, `) + } + + esFilter.WriteString(`{"nested": {"path": "metadata.api", "query": {"regexp": {"metadata.api.name": ".+"}}}}], `) + esFilter.WriteString(`"must_not": [` + mustNot.String() + `]`) + esFilter.WriteString("}") + res := esFilter.String() + + return &res +} + +func CreateFilters(filtersRaw []string) ([]models.Filter, error) { + var filters []models.Filter + + for _, filterRaw := range filtersRaw { + for _, operator := range models.Operators { + if strings.Contains(filterRaw, operator) { + sides := strings.Split(filterRaw, operator) + + if _, in := models.TypesMap[sides[0]]; in { + // Range operation is split into two operations. + // e.g. api.commits<>[1,5] => api.commits>=1 api.commits<=5 + if strings.Compare(operator, "<>") == 0 { + if _, in := models.BracketsMap[string(sides[1][0])]; !in { + return nil, errors.New("in the range, you can only use [, ], ), or )") + } + + if _, in := models.BracketsMap[string(sides[1][len(sides[1])-1])]; !in { + return nil, errors.New("in the range, you can only use [, ], ), or )") + } + + limits := strings.Split(strings.Trim(sides[1], "[()]"), ",") + bracketL := models.BracketsMap[string(sides[1][0])] + bracketR := models.BracketsMap[string(sides[1][len(sides[1])-1])] + + if len(limits) != 2 { + return nil, errors.New("there are less than two elements in the range") + } + + limitL := limits[0] + limitR := limits[1] + + filterL := models.Filter{ + Lhs: sides[0], + Operator: bracketL, + Rhs: limitL, + } + + filterR := models.Filter{ + Lhs: sides[0], + Operator: bracketR, + Rhs: limitR, + } + + err := filterL.Validate() + err = filterR.Validate() + + if err != nil { + return nil, err + } + + filters = append(filters, filterL) + filters = append(filters, filterR) + } else { + if strings.Compare(sides[1], "") != 0 { + filter := models.Filter{ + Lhs: sides[0], + Operator: operator, + Rhs: sides[1], + } + + err := filter.Validate() + + if err != nil { + return nil, err + } + + filters = append(filters, filter) + } + } + } else { + return nil, errors.New("the given left hand side filter name does not exist") + } + + break + } + } + } + + return filters, nil +} diff --git a/app/internal/retrieval/fields.go b/app/internal/retrieval/fields.go new file mode 100644 index 00000000..7c0a1c67 --- /dev/null +++ b/app/internal/retrieval/fields.go @@ -0,0 +1,27 @@ +package retrieval + +import ( + "errors" + "slices" + + "backend/app/internal/models" +) + +func FilterFields(response models.SpecificationWithApi, fields []string) (models.SpecificationWithApi, error) { + var filteredResponse = new(models.SpecificationWithApi) + + for _, field := range fields { + if slices.Contains(models.PossibleFilters, field) { + switch field { + case "metadata": + filteredResponse.MongoDocument = response.MongoDocument + case "specification": + filteredResponse.Specification = response.Specification + } + } else { + return models.SpecificationWithApi{}, errors.New("the given field does not exist") + } + } + + return *filteredResponse, nil +} diff --git a/app/internal/retrieval/knn.go b/app/internal/retrieval/knn.go new file mode 100644 index 00000000..519072e1 --- /dev/null +++ b/app/internal/retrieval/knn.go @@ -0,0 +1,37 @@ +package retrieval + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +func CreateKnnQuery(embedding []float32, filters string, pageSize int, page int, k int) string { + var query strings.Builder + + query.WriteString(`{"from": ` + strconv.Itoa((page - 1) * pageSize) + `, "size": ` + strconv.Itoa(pageSize)) + + if embedding != nil { + query.WriteString(`, "knn": {"field": "embedding", "query_vector": [`) + + for ind, el := range embedding { + value := reflect.ValueOf(el).Float() + query.WriteString(fmt.Sprintf(`%f`, value)) + + if ind != len(embedding)-1 { + query.WriteString(", ") + } + } + + query.WriteString(`], "k": ` + strconv.Itoa(k) + `, "num_candidates": 10000, "filter": {`) + query.WriteString(filters) + query.WriteString(`}}}`) + } else { + query.WriteString(`, "query": {`) + query.WriteString(filters) + query.WriteString("}}") + } + + return query.String() +} diff --git a/app/internal/routes/retrieve.go b/app/internal/routes/retrieve.go deleted file mode 100644 index 485569a2..00000000 --- a/app/internal/routes/retrieve.go +++ /dev/null @@ -1,30 +0,0 @@ -package routes - -import ( - "log" - "net/http" - - "backend/app/internal/embedding" - "backend/app/internal/structs" - - "github.com/gin-gonic/gin" -) - - -type EmbeddingRequest = structs.EmbeddingRequest - -func InitRetrieverRoutes(router *gin.Engine) { - router.POST("/search", Search) -} - -func Search(c *gin.Context) { - var body EmbeddingRequest - err := c.BindJSON(&body) - - if err != nil { - c.JSON(http.StatusBadRequest, "") - } - - embeddings := embedding.PerformPipeline([]string{body.Fragment}, true) - log.Print(embeddings) -} diff --git a/app/internal/routes/routes.go b/app/internal/routes/routes.go deleted file mode 100644 index 6597ccd2..00000000 --- a/app/internal/routes/routes.go +++ /dev/null @@ -1,10 +0,0 @@ -package routes - -import ( - "github.com/gin-gonic/gin" -) - - -func InitRoutes(router *gin.Engine) { - InitRetrieverRoutes(router) -} diff --git a/app/internal/structs/config.go b/app/internal/structs/config.go deleted file mode 100644 index 3a705361..00000000 --- a/app/internal/structs/config.go +++ /dev/null @@ -1,14 +0,0 @@ -package structs - - -type Config struct { - Backend struct { - Port int `yaml:"port"` - }`yaml:"backend"` - Elastic struct { - Host string `yaml:"host"` - Port int `yaml:"port"` - User string `yaml:"user"` - Password string `yaml:"password"` - }`yaml:"elastic"` -} diff --git a/app/internal/structs/embedding-request.go b/app/internal/structs/embedding-request.go deleted file mode 100644 index 62436ea8..00000000 --- a/app/internal/structs/embedding-request.go +++ /dev/null @@ -1,6 +0,0 @@ -package structs - - -type EmbeddingRequest struct { - Fragment string`json:"fragment"` -} diff --git a/app/internal/structs/embeddings.go b/app/internal/structs/embeddings.go deleted file mode 100644 index bcb319c9..00000000 --- a/app/internal/structs/embeddings.go +++ /dev/null @@ -1,6 +0,0 @@ -package structs - - -type Embeddings struct { - Predictions [][]float32 -} diff --git a/app/cmd/main/main.go b/app/main.go similarity index 52% rename from app/cmd/main/main.go rename to app/main.go index 48a3e0bf..58c4803a 100644 --- a/app/cmd/main/main.go +++ b/app/main.go @@ -1,21 +1,26 @@ package main import ( + "backend/app/internal/controller" "fmt" "log" + _ "backend/app/docs" "backend/app/internal/helpers" - "backend/app/internal/routes" - "github.com/gin-gonic/gin" ) +// @title API Scout +// @version 1.0 +// @description This is the backend for the API Scout platform. +// @BasePath /api/v1 func main() { cfg := helpers.LoadConfigs() + // Start the webserver router := gin.Default() - routes.InitRoutes(router) + controller.SetupRoutes(router, &cfg) err := router.Run(fmt.Sprintf(":%d", cfg.Backend.Port)) if err != nil { diff --git a/app/tests/dsl/filters_test.go b/app/tests/dsl/filters_test.go new file mode 100644 index 00000000..55fd9432 --- /dev/null +++ b/app/tests/dsl/filters_test.go @@ -0,0 +1,95 @@ +package dsl + +import ( + "testing" + + "backend/app/internal/retrieval" +) + +func TestNonExistentFilter(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.version.something>3"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestNonExistentOperator(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.version.raw!!3"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongFormatOnlyFilter(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.version.raw"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongFormatOnlyOperator(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{">="}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongFormatOnlyRightHandSide(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"5.0.0"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongFormatFilterAndOperator(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.version.raw=="}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongFormatOperatorAndRightHandSide(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"==3.0.0"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongRightHandSideVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.version.raw==3"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongRightHandSideString(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.source==20"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongRightHandSideBoolean(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.latest==ciao"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} + +func TestWrongRightHandSideInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{"api.version.minor==true"}) + + if filters != nil && err == nil { + t.Fatal(filters) + } +} diff --git a/app/tests/dsl/operations/contains_test.go b/app/tests/dsl/operations/contains_test.go new file mode 100644 index 00000000..e3c6826e --- /dev/null +++ b/app/tests/dsl/operations/contains_test.go @@ -0,0 +1,32 @@ +package operations + +import ( + "strings" + "testing" + + "backend/app/internal/retrieval" +) + +func TestContainsOperatorString(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.source~="git"`}) + + if filters == nil && + strings.Compare(filters[0].Lhs, "api.source") != 0 && + strings.Compare(filters[0].Operator, "~=") != 0 && + strings.Compare(filters[0].Rhs, `"git"`) != 0 && + err == nil { + t.Fatal(filters) + } +} + +func TestContainsOperatorVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw~=3.0`}) + + if filters == nil && + strings.Compare(filters[0].Lhs, "api.source") != 0 && + strings.Compare(filters[0].Operator, "~=") != 0 && + strings.Compare(filters[0].Rhs, `"3.0"`) != 0 && + err == nil { + t.Fatal(filters) + } +} diff --git a/app/tests/dsl/operations/equal_test.go b/app/tests/dsl/operations/equal_test.go new file mode 100644 index 00000000..a13de2ad --- /dev/null +++ b/app/tests/dsl/operations/equal_test.go @@ -0,0 +1,58 @@ +package operations + +import ( + "backend/app/internal/retrieval" + "log" + "strings" + "testing" +) + +func TestEqualOperatorString(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.source=="github"`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.source") != 0 || + strings.Compare(filters[0].Operator, "==") != 0 || + strings.Compare(filters[0].Rhs, "github") != 0 || + err != nil { + + log.Print(strings.Compare(filters[0].Lhs, "api.source") != 0) + t.Fatal(filters) + } +} + +func TestEqualOperatorBool(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.valid==true`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.valid") != 0 || + strings.Compare(filters[0].Operator, "==") != 0 || + strings.Compare(filters[0].Rhs, "true") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestEqualOperatorInt(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major==5`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, "==") != 0 || + strings.Compare(filters[0].Rhs, "5") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestEqualOperatorVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw==3.0.0`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, "==") != 0 || + strings.Compare(filters[0].Rhs, "3.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} diff --git a/app/tests/dsl/operations/greater_test.go b/app/tests/dsl/operations/greater_test.go new file mode 100644 index 00000000..74d51510 --- /dev/null +++ b/app/tests/dsl/operations/greater_test.go @@ -0,0 +1,56 @@ +package operations + +import ( + "strings" + "testing" + + "backend/app/internal/retrieval" +) + +func TestGreaterOperatorInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major>4`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, ">") != 0 || + strings.Compare(filters[0].Rhs, "4") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestGreaterOperatorVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw>4.0.0`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, ">") != 0 || + strings.Compare(filters[0].Rhs, "4.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestGreaterEqualOperatorInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major>=4`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, ">=") != 0 || + strings.Compare(filters[0].Rhs, "4") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestGreaterEqualOperatorVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw>=4.0.0`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, ">=") != 0 || + strings.Compare(filters[0].Rhs, "4.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} diff --git a/app/tests/dsl/operations/less_test.go b/app/tests/dsl/operations/less_test.go new file mode 100644 index 00000000..ac59d204 --- /dev/null +++ b/app/tests/dsl/operations/less_test.go @@ -0,0 +1,56 @@ +package operations + +import ( + "strings" + "testing" + + "backend/app/internal/retrieval" +) + +func TestLessOperatorInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major<4`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, "<") != 0 || + strings.Compare(filters[0].Rhs, "4") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestLessOperatorVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw<4.0.0`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, "<") != 0 || + strings.Compare(filters[0].Rhs, "4.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestLessEqualOperatorInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major<=4`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, "<=") != 0 || + strings.Compare(filters[0].Rhs, "4") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestLessEqualOperatorVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw<=4.0.0`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, "<=") != 0 || + strings.Compare(filters[0].Rhs, "4.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} diff --git a/app/tests/dsl/operations/not_equal_test.go b/app/tests/dsl/operations/not_equal_test.go new file mode 100644 index 00000000..a0e1575d --- /dev/null +++ b/app/tests/dsl/operations/not_equal_test.go @@ -0,0 +1,56 @@ +package operations + +import ( + "strings" + "testing" + + "backend/app/internal/retrieval" +) + +func TestNotEqualOperatorString(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.source!="github"`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.source") != 0 || + strings.Compare(filters[0].Operator, "!=") != 0 || + strings.Compare(filters[0].Rhs, "github") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestNotEqualOperatorBool(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.valid!=true`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.valid") != 0 || + strings.Compare(filters[0].Operator, "!=") != 0 || + strings.Compare(filters[0].Rhs, "true") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestNotEqualOperatorInt(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major!=5`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, "!=") != 0 || + strings.Compare(filters[0].Rhs, "5") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestNotEqualOperatorVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw!=3.0.0`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, "!=") != 0 || + strings.Compare(filters[0].Rhs, "3.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} diff --git a/app/tests/dsl/operations/range_test.go b/app/tests/dsl/operations/range_test.go new file mode 100644 index 00000000..dffca772 --- /dev/null +++ b/app/tests/dsl/operations/range_test.go @@ -0,0 +1,128 @@ +package operations + +import ( + "strings" + "testing" + + "backend/app/internal/retrieval" +) + +func TestRangeOperatorSquaresInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major<>[1,5]`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, ">=") != 0 || + strings.Compare(filters[0].Rhs, "1") != 0 || + strings.Compare(filters[1].Lhs, "api.version.major") != 0 || + strings.Compare(filters[1].Operator, "<=") != 0 || + strings.Compare(filters[1].Rhs, "5") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestRangeOperatorSquareRoundInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major<>[1,5)`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, ">=") != 0 || + strings.Compare(filters[0].Rhs, "1") != 0 || + strings.Compare(filters[1].Lhs, "api.version.major") != 0 || + strings.Compare(filters[1].Operator, "<") != 0 || + strings.Compare(filters[1].Rhs, "5") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestRangeOperatorRoundSquareInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major<>(1,5]`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, ">") != 0 || + strings.Compare(filters[0].Rhs, "1") != 0 || + strings.Compare(filters[1].Lhs, "api.version.major") != 0 || + strings.Compare(filters[1].Operator, "<=") != 0 || + strings.Compare(filters[1].Rhs, "5") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestRangeOperatorRoundsInteger(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.major<>(1,5)`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.major") != 0 || + strings.Compare(filters[0].Operator, ">") != 0 || + strings.Compare(filters[0].Rhs, "1") != 0 || + strings.Compare(filters[1].Lhs, "api.version.major") != 0 || + strings.Compare(filters[1].Operator, "<") != 0 || + strings.Compare(filters[1].Rhs, "5") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestRangeOperatorSquaresVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw<>[1.0.0,3.0.0]`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, ">=") != 0 || + strings.Compare(filters[0].Rhs, "1.0.0") != 0 || + strings.Compare(filters[1].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[1].Operator, "<=") != 0 || + strings.Compare(filters[1].Rhs, "3.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestRangeOperatorSquareRoundVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw<>[1.0.0,3.0.0)`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, ">=") != 0 || + strings.Compare(filters[0].Rhs, "1.0.0") != 0 || + strings.Compare(filters[1].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[1].Operator, "<") != 0 || + strings.Compare(filters[1].Rhs, "3.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestRangeOperatorRoundSquareVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw<>(1.0.0,3.0.0]`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, ">") != 0 || + strings.Compare(filters[0].Rhs, "1.0.0") != 0 || + strings.Compare(filters[1].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[1].Operator, "<=") != 0 || + strings.Compare(filters[1].Rhs, "3.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} + +func TestRangeOperatorRoundsVersion(t *testing.T) { + filters, err := retrieval.CreateFilters([]string{`api.version.raw<>(1.0.0,3.0.0)`}) + + if filters == nil || + strings.Compare(filters[0].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[0].Operator, ">") != 0 || + strings.Compare(filters[0].Rhs, "1.0.0") != 0 || + strings.Compare(filters[1].Lhs, "api.version.raw") != 0 || + strings.Compare(filters[1].Operator, "<") != 0 || + strings.Compare(filters[1].Rhs, "3.0.0") != 0 || + err != nil { + t.Fatal(filters) + } +} diff --git a/app/tests/embedding/extracting_test.go b/app/tests/embedding/extracting_test.go index 5d6307d3..d6e87a08 100644 --- a/app/tests/embedding/extracting_test.go +++ b/app/tests/embedding/extracting_test.go @@ -7,7 +7,6 @@ import ( "backend/app/internal/embedding" ) - func TestExtractEmptyString(t *testing.T) { fragments := []string{""} res := embedding.ExtractTags(fragments) @@ -17,7 +16,7 @@ func TestExtractEmptyString(t *testing.T) { } } -func TestExtractEmptyArray(t *testing.T) { +func TestExtractEmptyArray(t *testing.T) { var fragments []string res := embedding.ExtractTags(fragments) diff --git a/app/tests/embedding/pipeline_test.go b/app/tests/embedding/pipeline_test.go index 006aa131..c3a02d46 100644 --- a/app/tests/embedding/pipeline_test.go +++ b/app/tests/embedding/pipeline_test.go @@ -13,9 +13,9 @@ func TestPipelineEmptyString(t *testing.T) { } fragments := []string{""} - res := embedding.PerformPipeline(fragments, true) + res, _, _ := embedding.PerformPipeline(fragments, true) - if len(res.Predictions) != 1 || len(res.Predictions[0]) != 512 { + if res == nil { t.Fatal(res) } } @@ -26,9 +26,9 @@ func TestPipelineEmptyArray(t *testing.T) { } var fragments []string - res := embedding.PerformPipeline(fragments, true) + res, _, _ := embedding.PerformPipeline(fragments, true) - if len(res.Predictions) != 0 { + if res != nil { t.Fatal(res) } } @@ -39,7 +39,7 @@ func TestPipelineQuery(t *testing.T) { } fragments := []string{"this is a test query"} - res := embedding.PerformPipeline(fragments, true) + res, _, _ := embedding.PerformPipeline(fragments, true) if len(res.Predictions) != 1 || len(res.Predictions[0]) != 512 { t.Fatal(res) @@ -52,7 +52,20 @@ func TestPipelineQueries(t *testing.T) { } fragments := []string{"this is a test query", "this is another different test query"} - res := embedding.PerformPipeline(fragments, true) + res, _, _ := embedding.PerformPipeline(fragments, true) + + if len(res.Predictions) != 2 || len(res.Predictions[0]) != 512 || len(res.Predictions[1]) != 512 { + t.Fatal(res) + } +} + +func TestPipelineQueries1(t *testing.T) { + if os.Getenv("MODELS_HOST") == "" { + t.Skip("Skipping testing in CI environment") + } + + fragments := []string{"this is a https://twitter.com/_geodatasource/profile_image?size=original query", "this is another http://www.google.com"} + res, _, _ := embedding.PerformPipeline(fragments, true) if len(res.Predictions) != 2 || len(res.Predictions[0]) != 512 || len(res.Predictions[1]) != 512 { t.Fatal(res) @@ -65,7 +78,7 @@ func TestPipelineDocument(t *testing.T) { } fragments := []string{"{\n \"openapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"test title\",\n \"description\": \"test description\",\n \"version\": \"0.0.1\",\n \"summary\": 'test \"summary\"'\n }\n}"} - res := embedding.PerformPipeline(fragments, false) + res, _, _ := embedding.PerformPipeline(fragments, false) if len(res.Predictions) != 1 || len(res.Predictions[0]) != 512 { t.Fatal(res) @@ -78,7 +91,7 @@ func TestPipelineDocuments(t *testing.T) { } fragments := []string{"{\n \"openapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"test title\",\n \"description\": \"test description\",\n \"version\": \"0.0.1\",\n \"summary\": 'test \"summary\"'\n }\n}", "\"version\": \"0.0.1\""} - res := embedding.PerformPipeline(fragments, false) + res, _, _ := embedding.PerformPipeline(fragments, false) if len(res.Predictions) != 2 || len(res.Predictions[0]) != 512 || len(res.Predictions[1]) != 512 { t.Fatal(res) diff --git a/app/tests/embedding/preprocesing_test.go b/app/tests/embedding/preprocesing_test.go index 2543d342..3e07a442 100644 --- a/app/tests/embedding/preprocesing_test.go +++ b/app/tests/embedding/preprocesing_test.go @@ -1,13 +1,11 @@ package embedding import ( - "strings" "testing" "backend/app/internal/embedding" ) - func TestPreprocessEmptyString(t *testing.T) { fragments := []string{""} res := embedding.PreprocessFragment(fragments, true) @@ -17,7 +15,7 @@ func TestPreprocessEmptyString(t *testing.T) { } } -func TestPreprocessEmptyArray(t *testing.T) { +func TestPreprocessEmptyArray(t *testing.T) { var fragments []string res := embedding.PreprocessFragment(fragments, true) @@ -25,45 +23,3 @@ func TestPreprocessEmptyArray(t *testing.T) { t.Fatal(res) } } - -func TestPreprocessQuery(t *testing.T) { - fragments := []string{"this is a test query"} - res := embedding.PreprocessFragment(fragments, true) - - if len(res) != 1 || strings.Compare(res[0], "test queri") != 0 { - t.Fatal(res) - } -} - -func TestPreprocessQueries(t *testing.T) { - fragments := []string{"this is a test query", "this is another different test query"} - res := embedding.PreprocessFragment(fragments, true) - - if len(res) != 2 || - strings.Compare(res[0], "test queri") != 0 || - strings.Compare(res[1], "differ test queri") != 0 { - - t.Fatal(res) - } -} - -func TestPreprocessDocument(t *testing.T) { - fragments := []string{"{\n \"openapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"test title\",\n \"description\": \"test description\",\n \"version\": \"0.0.1\",\n \"summary\": 'test \"summary\"'\n }\n}"} - res := embedding.PreprocessFragment(fragments, false) - - if len(res) != 1 || strings.Compare(res[0], "test titl test descript test summari") != 0 { - t.Fatal(res) - } -} - -func TestPreprocessDocuments(t *testing.T) { - fragments := []string{"{\n \"openapi\": \"3.0.0\",\n \"info\": {\n \"title\": \"test title\",\n \"description\": \"test description\",\n \"version\": \"0.0.1\",\n \"summary\": 'test \"summary\"'\n }\n}", "\"version\": \"0.0.1\""} - res := embedding.PreprocessFragment(fragments, false) - - if len(res) != 2 || - strings.Compare(res[0], "test titl test descript test summari") != 0 || - strings.Compare(res[1], "") != 0 { - - t.Fatal(res) - } -} \ No newline at end of file diff --git a/app/tests/embedding/stemming_test.go b/app/tests/embedding/stemming_test.go index d75ac580..5533a285 100644 --- a/app/tests/embedding/stemming_test.go +++ b/app/tests/embedding/stemming_test.go @@ -7,7 +7,6 @@ import ( "backend/app/internal/embedding" ) - func TestStemmingEmptyString(t *testing.T) { fragments := []string{""} res := embedding.Stemming(fragments) @@ -17,7 +16,7 @@ func TestStemmingEmptyString(t *testing.T) { } } -func TestStemmingEmptyArray(t *testing.T) { +func TestStemmingEmptyArray(t *testing.T) { var fragments []string res := embedding.Stemming(fragments) @@ -55,4 +54,3 @@ func TestStemmingIncorrectlyFormattedQueries(t *testing.T) { t.Fatal(res) } } - diff --git a/app/tests/embedding/stopwords_test.go b/app/tests/embedding/stopwords_test.go index 6d880d70..00d70b6e 100644 --- a/app/tests/embedding/stopwords_test.go +++ b/app/tests/embedding/stopwords_test.go @@ -7,7 +7,6 @@ import ( "backend/app/internal/embedding" ) - func TestStopwordsEmptyString(t *testing.T) { fragments := []string{""} res := embedding.StopWordRemoval(fragments) @@ -17,7 +16,7 @@ func TestStopwordsEmptyString(t *testing.T) { } } -func TestStopwordsEmptyArray(t *testing.T) { +func TestStopwordsEmptyArray(t *testing.T) { var fragments []string res := embedding.StopWordRemoval(fragments) diff --git a/environment.yml b/config/environment.yml similarity index 100% rename from environment.yml rename to config/environment.yml diff --git a/config/template.config.yml b/config/template.config.yml index 3bb2e814..5615299d 100644 --- a/config/template.config.yml +++ b/config/template.config.yml @@ -1,9 +1,18 @@ -# Server Configurations backend: port: 8080 + +# Mongo Configurations +mongodb: + protocol: "" + host: "" + port: 27017 + user: "" + password: "" + # ElasticSearch Credentials elastic: + protocol: "" host: "" port: 9200 user: "" - password: "" + password: "" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9d74a087..c0b6972b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,29 @@ -version: "2.2" services: models: image: tensorflow/serving container_name: models + environment: + MODEL_NAME: "universal-encoder" networks: - - be-network + - backend-network volumes: - ./models/universal-encoder:/models/universal-encoder - environment: - MODEL_NAME: "universal-encoder" backend: - image: api-scout-backend:latest + image: edoriggio/api-scout:dev container_name: backend + environment: + GIN_MODE: "release" + MODELS_HOST: "models" networks: - - be-network + - backend-network ports: - "8080:8080" + volumes: + - ./config:/backend/config + - ./ca.crt:/backend/ca.crt + networks: - be-network: - driver: bridge + backend-network: + name: backend-network + external: true diff --git a/docs/_/css/main.css b/docs/_/css/main.css new file mode 100644 index 00000000..aad59947 --- /dev/null +++ b/docs/_/css/main.css @@ -0,0 +1,161 @@ +body { + margin: 1em 2em; + font-family: Helvetica, sans-serif; + background-color: #f8f8f8; + font-size: 1em; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0.3em; + margin-bottom: 0.3em; +} +h1, h2, h3, h4 { font-weight: 500; } +h2 { font-size: 1.75em } +h3 { font-size: 1.5em } +h4 { font-size: 1.33em } +h5 { font-size: 1em } + +a { + text-decoration: none; + color: #0366a5; +} +a:hover { + text-decoration: underline; +} + +a.permalink { display: none; } +a.permalink:hover { + text-decoration: none; +} +*:hover > a.permalink { display: inline; } + +nav { + padding: 1em; + background-color: #eee; + border-radius: 0.5em; + display: flex; + flex-wrap: wrap; +} + +nav .navbar-right { + margin-left: auto; +} + +/* Remove first level of nesting for a package's index section. */ +#pkg-index + ul, #pkg-examples + ul { + list-style-type: none; + padding: 0; +} + +code, kbd, pre { + font-family: Consolas, monospace; +} + +pre { + color: #222; + overflow-x: auto; + border: 1px solid #ccc; + border-radius: 0.5em; + background-color: #eee; + padding: 0.75em; + font-size: 0.9em; +} + +details.example > summary { + color: #0366a5; + cursor: pointer; +} + +details.deprecated > summary { + list-style: none; +} + +span.deprecated-tag { + color: #eee; + background-color: #999; + padding: 0.125rem 0.3rem; + border-radius: 0.3rem; + font-size: 0.7rem; + vertical-align: middle; + cursor: pointer; +} + +#search { margin: 0.3em 0; } + +#generated-by-footer { font-size: x-small; } + +/* Background */ .bg { background-color: #ffffff; } +/* PreWrapper */ .chroma { background-color: #ffffff; } +/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 } +/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit } +/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; } +/* LineHighlight */ .chroma .hl { background-color: #e5e5e5 } +/* LineNumbersTable */ .chroma .lnt { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* LineNumbers */ .chroma .ln { white-space: pre; -webkit-user-select: none; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f } +/* Line */ .chroma .line { display: flex; } +/* Keyword */ .chroma .k { color: #000000; font-weight: bold } +/* KeywordConstant */ .chroma .kc { color: #000000; font-weight: bold } +/* KeywordDeclaration */ .chroma .kd { color: #000000; font-weight: bold } +/* KeywordNamespace */ .chroma .kn { color: #000000; font-weight: bold } +/* KeywordPseudo */ .chroma .kp { color: #000000; font-weight: bold } +/* KeywordReserved */ .chroma .kr { color: #000000; font-weight: bold } +/* KeywordType */ .chroma .kt { color: #445588; font-weight: bold } +/* NameAttribute */ .chroma .na { color: #008080 } +/* NameBuiltin */ .chroma .nb { color: #0086b3 } +/* NameBuiltinPseudo */ .chroma .bp { color: #999999 } +/* NameClass */ .chroma .nc { color: #445588; font-weight: bold } +/* NameConstant */ .chroma .no { color: #008080 } +/* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold } +/* NameEntity */ .chroma .ni { color: #800080 } +/* NameException */ .chroma .ne { color: #990000; font-weight: bold } +/* NameFunction */ .chroma .nf { color: #990000; font-weight: bold } +/* NameLabel */ .chroma .nl { color: #990000; font-weight: bold } +/* NameNamespace */ .chroma .nn { color: #555555 } +/* NameTag */ .chroma .nt { color: #000080 } +/* NameVariable */ .chroma .nv { color: #008080 } +/* NameVariableClass */ .chroma .vc { color: #008080 } +/* NameVariableGlobal */ .chroma .vg { color: #008080 } +/* NameVariableInstance */ .chroma .vi { color: #008080 } +/* LiteralString */ .chroma .s { color: #dd1144 } +/* LiteralStringAffix */ .chroma .sa { color: #dd1144 } +/* LiteralStringBacktick */ .chroma .sb { color: #dd1144 } +/* LiteralStringChar */ .chroma .sc { color: #dd1144 } +/* LiteralStringDelimiter */ .chroma .dl { color: #dd1144 } +/* LiteralStringDoc */ .chroma .sd { color: #dd1144 } +/* LiteralStringDouble */ .chroma .s2 { color: #dd1144 } +/* LiteralStringEscape */ .chroma .se { color: #dd1144 } +/* LiteralStringHeredoc */ .chroma .sh { color: #dd1144 } +/* LiteralStringInterpol */ .chroma .si { color: #dd1144 } +/* LiteralStringOther */ .chroma .sx { color: #dd1144 } +/* LiteralStringRegex */ .chroma .sr { color: #009926 } +/* LiteralStringSingle */ .chroma .s1 { color: #dd1144 } +/* LiteralStringSymbol */ .chroma .ss { color: #990073 } +/* LiteralNumber */ .chroma .m { color: #009999 } +/* LiteralNumberBin */ .chroma .mb { color: #009999 } +/* LiteralNumberFloat */ .chroma .mf { color: #009999 } +/* LiteralNumberHex */ .chroma .mh { color: #009999 } +/* LiteralNumberInteger */ .chroma .mi { color: #009999 } +/* LiteralNumberIntegerLong */ .chroma .il { color: #009999 } +/* LiteralNumberOct */ .chroma .mo { color: #009999 } +/* Operator */ .chroma .o { color: #000000; font-weight: bold } +/* OperatorWord */ .chroma .ow { color: #000000; font-weight: bold } +/* Comment */ .chroma .c { color: #999988; font-style: italic } +/* CommentHashbang */ .chroma .ch { color: #999988; font-style: italic } +/* CommentMultiline */ .chroma .cm { color: #999988; font-style: italic } +/* CommentSingle */ .chroma .c1 { color: #999988; font-style: italic } +/* CommentSpecial */ .chroma .cs { color: #999999; font-weight: bold; font-style: italic } +/* CommentPreproc */ .chroma .cp { color: #999999; font-weight: bold; font-style: italic } +/* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold; font-style: italic } +/* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd } +/* GenericEmph */ .chroma .ge { color: #000000; font-style: italic } +/* GenericError */ .chroma .gr { color: #aa0000 } +/* GenericHeading */ .chroma .gh { color: #999999 } +/* GenericInserted */ .chroma .gi { color: #000000; background-color: #ddffdd } +/* GenericOutput */ .chroma .go { color: #888888 } +/* GenericPrompt */ .chroma .gp { color: #555555 } +/* GenericStrong */ .chroma .gs { font-weight: bold } +/* GenericSubheading */ .chroma .gu { color: #aaaaaa } +/* GenericTraceback */ .chroma .gt { color: #aa0000 } +/* GenericUnderline */ .chroma .gl { text-decoration: underline } +/* TextWhitespace */ .chroma .w { color: #bbbbbb } diff --git a/docs/_/icons/apple-touch-icon.png b/docs/_/icons/apple-touch-icon.png new file mode 100644 index 00000000..8b847112 Binary files /dev/null and b/docs/_/icons/apple-touch-icon.png differ diff --git a/docs/_/icons/favicon-16x16.png b/docs/_/icons/favicon-16x16.png new file mode 100644 index 00000000..445198b0 Binary files /dev/null and b/docs/_/icons/favicon-16x16.png differ diff --git a/docs/_/icons/favicon-32x32.png b/docs/_/icons/favicon-32x32.png new file mode 100644 index 00000000..393810fb Binary files /dev/null and b/docs/_/icons/favicon-32x32.png differ diff --git a/docs/_/icons/favicon.ico b/docs/_/icons/favicon.ico new file mode 100644 index 00000000..e7452a5f Binary files /dev/null and b/docs/_/icons/favicon.ico differ diff --git a/docs/_/js/permalink.js b/docs/_/js/permalink.js new file mode 100644 index 00000000..062ccf3f --- /dev/null +++ b/docs/_/js/permalink.js @@ -0,0 +1,44 @@ +// If the page was opened with an anchor (e.g. #foo), +// and the destination is a
element, open it. +function openDetailsAnchor() { + let hash = window.location.hash + if (!hash) { + return + } + let el = document.getElementById(hash.slice(1)) // remove leading '#' + if (!el) { + return + } + + let details = el.closest("details") + while (details) { + details.open = true + details = details.parentElement.closest("details") + } + + // New elements may have appeared. + // Set hash again to scroll to the right place. + window.location.hash = hash; + return false; +} + +window.addEventListener('hashchange', openDetailsAnchor) + +window.addEventListener('load', () => { + document.querySelectorAll("h2, h3, h4, h5, h6").forEach((el) => { + if (!el.id) { + return + } + el.innerHTML += ' ' + }) + + document.querySelectorAll("details.example > summary").forEach((el) => { + let id = el.parentElement.id; + if (!id) { + return + } + el.innerHTML += ' ' + }) + + openDetailsAnchor() +}) diff --git a/docs/_/pagefind/filter/unknown_9115dcc.pf_filter b/docs/_/pagefind/filter/unknown_9115dcc.pf_filter new file mode 100644 index 00000000..4282e114 Binary files /dev/null and b/docs/_/pagefind/filter/unknown_9115dcc.pf_filter differ diff --git a/docs/_/pagefind/fragment/unknown_0f340d8.pf_fragment b/docs/_/pagefind/fragment/unknown_0f340d8.pf_fragment new file mode 100644 index 00000000..d543505c Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_0f340d8.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_33b6ab7.pf_fragment b/docs/_/pagefind/fragment/unknown_33b6ab7.pf_fragment new file mode 100644 index 00000000..77700107 Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_33b6ab7.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_455b5ce.pf_fragment b/docs/_/pagefind/fragment/unknown_455b5ce.pf_fragment new file mode 100644 index 00000000..c6f21d44 Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_455b5ce.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_5627a9c.pf_fragment b/docs/_/pagefind/fragment/unknown_5627a9c.pf_fragment new file mode 100644 index 00000000..8e0b7302 Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_5627a9c.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_6acfcfa.pf_fragment b/docs/_/pagefind/fragment/unknown_6acfcfa.pf_fragment new file mode 100644 index 00000000..ee4b3b42 Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_6acfcfa.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_9764785.pf_fragment b/docs/_/pagefind/fragment/unknown_9764785.pf_fragment new file mode 100644 index 00000000..cb3767be Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_9764785.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_ba11e75.pf_fragment b/docs/_/pagefind/fragment/unknown_ba11e75.pf_fragment new file mode 100644 index 00000000..fdd2911b Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_ba11e75.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_c7b999b.pf_fragment b/docs/_/pagefind/fragment/unknown_c7b999b.pf_fragment new file mode 100644 index 00000000..34c996fa Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_c7b999b.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_d42fa13.pf_fragment b/docs/_/pagefind/fragment/unknown_d42fa13.pf_fragment new file mode 100644 index 00000000..fcf0d5d6 Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_d42fa13.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_fa99fd0.pf_fragment b/docs/_/pagefind/fragment/unknown_fa99fd0.pf_fragment new file mode 100644 index 00000000..304507c9 Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_fa99fd0.pf_fragment differ diff --git a/docs/_/pagefind/fragment/unknown_fe47cfb.pf_fragment b/docs/_/pagefind/fragment/unknown_fe47cfb.pf_fragment new file mode 100644 index 00000000..8ba85464 Binary files /dev/null and b/docs/_/pagefind/fragment/unknown_fe47cfb.pf_fragment differ diff --git a/docs/_/pagefind/index/unknown_7a71f54.pf_index b/docs/_/pagefind/index/unknown_7a71f54.pf_index new file mode 100644 index 00000000..19f4333b Binary files /dev/null and b/docs/_/pagefind/index/unknown_7a71f54.pf_index differ diff --git a/docs/_/pagefind/index/unknown_9923a1f.pf_index b/docs/_/pagefind/index/unknown_9923a1f.pf_index new file mode 100644 index 00000000..2bb54814 Binary files /dev/null and b/docs/_/pagefind/index/unknown_9923a1f.pf_index differ diff --git a/docs/_/pagefind/pagefind-entry.json b/docs/_/pagefind/pagefind-entry.json new file mode 100644 index 00000000..944e678c --- /dev/null +++ b/docs/_/pagefind/pagefind-entry.json @@ -0,0 +1 @@ +{"version":"1.0.4","languages":{"unknown":{"hash":"unknown_d32456ea962894a","wasm":null,"page_count":6}}} \ No newline at end of file diff --git a/docs/_/pagefind/pagefind-highlight.js b/docs/_/pagefind/pagefind-highlight.js new file mode 100644 index 00000000..c823fbfe --- /dev/null +++ b/docs/_/pagefind/pagefind-highlight.js @@ -0,0 +1,1069 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/mark.js/dist/mark.js +var require_mark = __commonJS({ + "node_modules/mark.js/dist/mark.js"(exports, module) { + (function(global, factory) { + typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : global.Mark = factory(); + })(exports, function() { + "use strict"; + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { + return typeof obj; + } : function(obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + var classCallCheck = function(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + var createClass = function() { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) + descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + return function(Constructor, protoProps, staticProps) { + if (protoProps) + defineProperties(Constructor.prototype, protoProps); + if (staticProps) + defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + var _extends = Object.assign || function(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }; + var DOMIterator = function() { + function DOMIterator2(ctx) { + var iframes = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true; + var exclude = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : []; + var iframesTimeout = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : 5e3; + classCallCheck(this, DOMIterator2); + this.ctx = ctx; + this.iframes = iframes; + this.exclude = exclude; + this.iframesTimeout = iframesTimeout; + } + createClass(DOMIterator2, [{ + key: "getContexts", + value: function getContexts() { + var ctx = void 0, filteredCtx = []; + if (typeof this.ctx === "undefined" || !this.ctx) { + ctx = []; + } else if (NodeList.prototype.isPrototypeOf(this.ctx)) { + ctx = Array.prototype.slice.call(this.ctx); + } else if (Array.isArray(this.ctx)) { + ctx = this.ctx; + } else if (typeof this.ctx === "string") { + ctx = Array.prototype.slice.call(document.querySelectorAll(this.ctx)); + } else { + ctx = [this.ctx]; + } + ctx.forEach(function(ctx2) { + var isDescendant = filteredCtx.filter(function(contexts) { + return contexts.contains(ctx2); + }).length > 0; + if (filteredCtx.indexOf(ctx2) === -1 && !isDescendant) { + filteredCtx.push(ctx2); + } + }); + return filteredCtx; + } + }, { + key: "getIframeContents", + value: function getIframeContents(ifr, successFn) { + var errorFn = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : function() { + }; + var doc = void 0; + try { + var ifrWin = ifr.contentWindow; + doc = ifrWin.document; + if (!ifrWin || !doc) { + throw new Error("iframe inaccessible"); + } + } catch (e) { + errorFn(); + } + if (doc) { + successFn(doc); + } + } + }, { + key: "isIframeBlank", + value: function isIframeBlank(ifr) { + var bl = "about:blank", src = ifr.getAttribute("src").trim(), href = ifr.contentWindow.location.href; + return href === bl && src !== bl && src; + } + }, { + key: "observeIframeLoad", + value: function observeIframeLoad(ifr, successFn, errorFn) { + var _this = this; + var called = false, tout = null; + var listener = function listener2() { + if (called) { + return; + } + called = true; + clearTimeout(tout); + try { + if (!_this.isIframeBlank(ifr)) { + ifr.removeEventListener("load", listener2); + _this.getIframeContents(ifr, successFn, errorFn); + } + } catch (e) { + errorFn(); + } + }; + ifr.addEventListener("load", listener); + tout = setTimeout(listener, this.iframesTimeout); + } + }, { + key: "onIframeReady", + value: function onIframeReady(ifr, successFn, errorFn) { + try { + if (ifr.contentWindow.document.readyState === "complete") { + if (this.isIframeBlank(ifr)) { + this.observeIframeLoad(ifr, successFn, errorFn); + } else { + this.getIframeContents(ifr, successFn, errorFn); + } + } else { + this.observeIframeLoad(ifr, successFn, errorFn); + } + } catch (e) { + errorFn(); + } + } + }, { + key: "waitForIframes", + value: function waitForIframes(ctx, done) { + var _this2 = this; + var eachCalled = 0; + this.forEachIframe(ctx, function() { + return true; + }, function(ifr) { + eachCalled++; + _this2.waitForIframes(ifr.querySelector("html"), function() { + if (!--eachCalled) { + done(); + } + }); + }, function(handled) { + if (!handled) { + done(); + } + }); + } + }, { + key: "forEachIframe", + value: function forEachIframe(ctx, filter, each) { + var _this3 = this; + var end = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : function() { + }; + var ifr = ctx.querySelectorAll("iframe"), open = ifr.length, handled = 0; + ifr = Array.prototype.slice.call(ifr); + var checkEnd = function checkEnd2() { + if (--open <= 0) { + end(handled); + } + }; + if (!open) { + checkEnd(); + } + ifr.forEach(function(ifr2) { + if (DOMIterator2.matches(ifr2, _this3.exclude)) { + checkEnd(); + } else { + _this3.onIframeReady(ifr2, function(con) { + if (filter(ifr2)) { + handled++; + each(con); + } + checkEnd(); + }, checkEnd); + } + }); + } + }, { + key: "createIterator", + value: function createIterator(ctx, whatToShow, filter) { + return document.createNodeIterator(ctx, whatToShow, filter, false); + } + }, { + key: "createInstanceOnIframe", + value: function createInstanceOnIframe(contents) { + return new DOMIterator2(contents.querySelector("html"), this.iframes); + } + }, { + key: "compareNodeIframe", + value: function compareNodeIframe(node, prevNode, ifr) { + var compCurr = node.compareDocumentPosition(ifr), prev = Node.DOCUMENT_POSITION_PRECEDING; + if (compCurr & prev) { + if (prevNode !== null) { + var compPrev = prevNode.compareDocumentPosition(ifr), after = Node.DOCUMENT_POSITION_FOLLOWING; + if (compPrev & after) { + return true; + } + } else { + return true; + } + } + return false; + } + }, { + key: "getIteratorNode", + value: function getIteratorNode(itr) { + var prevNode = itr.previousNode(); + var node = void 0; + if (prevNode === null) { + node = itr.nextNode(); + } else { + node = itr.nextNode() && itr.nextNode(); + } + return { + prevNode, + node + }; + } + }, { + key: "checkIframeFilter", + value: function checkIframeFilter(node, prevNode, currIfr, ifr) { + var key = false, handled = false; + ifr.forEach(function(ifrDict, i) { + if (ifrDict.val === currIfr) { + key = i; + handled = ifrDict.handled; + } + }); + if (this.compareNodeIframe(node, prevNode, currIfr)) { + if (key === false && !handled) { + ifr.push({ + val: currIfr, + handled: true + }); + } else if (key !== false && !handled) { + ifr[key].handled = true; + } + return true; + } + if (key === false) { + ifr.push({ + val: currIfr, + handled: false + }); + } + return false; + } + }, { + key: "handleOpenIframes", + value: function handleOpenIframes(ifr, whatToShow, eCb, fCb) { + var _this4 = this; + ifr.forEach(function(ifrDict) { + if (!ifrDict.handled) { + _this4.getIframeContents(ifrDict.val, function(con) { + _this4.createInstanceOnIframe(con).forEachNode(whatToShow, eCb, fCb); + }); + } + }); + } + }, { + key: "iterateThroughNodes", + value: function iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) { + var _this5 = this; + var itr = this.createIterator(ctx, whatToShow, filterCb); + var ifr = [], elements = [], node = void 0, prevNode = void 0, retrieveNodes = function retrieveNodes2() { + var _getIteratorNode = _this5.getIteratorNode(itr); + prevNode = _getIteratorNode.prevNode; + node = _getIteratorNode.node; + return node; + }; + while (retrieveNodes()) { + if (this.iframes) { + this.forEachIframe(ctx, function(currIfr) { + return _this5.checkIframeFilter(node, prevNode, currIfr, ifr); + }, function(con) { + _this5.createInstanceOnIframe(con).forEachNode(whatToShow, function(ifrNode) { + return elements.push(ifrNode); + }, filterCb); + }); + } + elements.push(node); + } + elements.forEach(function(node2) { + eachCb(node2); + }); + if (this.iframes) { + this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb); + } + doneCb(); + } + }, { + key: "forEachNode", + value: function forEachNode(whatToShow, each, filter) { + var _this6 = this; + var done = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : function() { + }; + var contexts = this.getContexts(); + var open = contexts.length; + if (!open) { + done(); + } + contexts.forEach(function(ctx) { + var ready = function ready2() { + _this6.iterateThroughNodes(whatToShow, ctx, each, filter, function() { + if (--open <= 0) { + done(); + } + }); + }; + if (_this6.iframes) { + _this6.waitForIframes(ctx, ready); + } else { + ready(); + } + }); + } + }], [{ + key: "matches", + value: function matches(element, selector) { + var selectors = typeof selector === "string" ? [selector] : selector, fn = element.matches || element.matchesSelector || element.msMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.webkitMatchesSelector; + if (fn) { + var match = false; + selectors.every(function(sel) { + if (fn.call(element, sel)) { + match = true; + return false; + } + return true; + }); + return match; + } else { + return false; + } + } + }]); + return DOMIterator2; + }(); + var Mark$1 = function() { + function Mark3(ctx) { + classCallCheck(this, Mark3); + this.ctx = ctx; + this.ie = false; + var ua = window.navigator.userAgent; + if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) { + this.ie = true; + } + } + createClass(Mark3, [{ + key: "log", + value: function log(msg) { + var level = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "debug"; + var log2 = this.opt.log; + if (!this.opt.debug) { + return; + } + if ((typeof log2 === "undefined" ? "undefined" : _typeof(log2)) === "object" && typeof log2[level] === "function") { + log2[level]("mark.js: " + msg); + } + } + }, { + key: "escapeStr", + value: function escapeStr(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + } + }, { + key: "createRegExp", + value: function createRegExp(str) { + if (this.opt.wildcards !== "disabled") { + str = this.setupWildcardsRegExp(str); + } + str = this.escapeStr(str); + if (Object.keys(this.opt.synonyms).length) { + str = this.createSynonymsRegExp(str); + } + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.setupIgnoreJoinersRegExp(str); + } + if (this.opt.diacritics) { + str = this.createDiacriticsRegExp(str); + } + str = this.createMergedBlanksRegExp(str); + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.createJoinersRegExp(str); + } + if (this.opt.wildcards !== "disabled") { + str = this.createWildcardsRegExp(str); + } + str = this.createAccuracyRegExp(str); + return str; + } + }, { + key: "createSynonymsRegExp", + value: function createSynonymsRegExp(str) { + var syn = this.opt.synonyms, sens = this.opt.caseSensitive ? "" : "i", joinerPlaceholder = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : ""; + for (var index in syn) { + if (syn.hasOwnProperty(index)) { + var value = syn[index], k1 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(index) : this.escapeStr(index), k2 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(value) : this.escapeStr(value); + if (k1 !== "" && k2 !== "") { + str = str.replace(new RegExp("(" + this.escapeStr(k1) + "|" + this.escapeStr(k2) + ")", "gm" + sens), joinerPlaceholder + ("(" + this.processSynomyms(k1) + "|") + (this.processSynomyms(k2) + ")") + joinerPlaceholder); + } + } + } + return str; + } + }, { + key: "processSynomyms", + value: function processSynomyms(str) { + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.setupIgnoreJoinersRegExp(str); + } + return str; + } + }, { + key: "setupWildcardsRegExp", + value: function setupWildcardsRegExp(str) { + str = str.replace(/(?:\\)*\?/g, function(val) { + return val.charAt(0) === "\\" ? "?" : ""; + }); + return str.replace(/(?:\\)*\*/g, function(val) { + return val.charAt(0) === "\\" ? "*" : ""; + }); + } + }, { + key: "createWildcardsRegExp", + value: function createWildcardsRegExp(str) { + var spaces = this.opt.wildcards === "withSpaces"; + return str.replace(/\u0001/g, spaces ? "[\\S\\s]?" : "\\S?").replace(/\u0002/g, spaces ? "[\\S\\s]*?" : "\\S*"); + } + }, { + key: "setupIgnoreJoinersRegExp", + value: function setupIgnoreJoinersRegExp(str) { + return str.replace(/[^(|)\\]/g, function(val, indx, original) { + var nextChar = original.charAt(indx + 1); + if (/[(|)\\]/.test(nextChar) || nextChar === "") { + return val; + } else { + return val + "\0"; + } + }); + } + }, { + key: "createJoinersRegExp", + value: function createJoinersRegExp(str) { + var joiner = []; + var ignorePunctuation = this.opt.ignorePunctuation; + if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) { + joiner.push(this.escapeStr(ignorePunctuation.join(""))); + } + if (this.opt.ignoreJoiners) { + joiner.push("\\u00ad\\u200b\\u200c\\u200d"); + } + return joiner.length ? str.split(/\u0000+/).join("[" + joiner.join("") + "]*") : str; + } + }, { + key: "createDiacriticsRegExp", + value: function createDiacriticsRegExp(str) { + var sens = this.opt.caseSensitive ? "" : "i", dct = this.opt.caseSensitive ? ["a\xE0\xE1\u1EA3\xE3\u1EA1\u0103\u1EB1\u1EAF\u1EB3\u1EB5\u1EB7\xE2\u1EA7\u1EA5\u1EA9\u1EAB\u1EAD\xE4\xE5\u0101\u0105", "A\xC0\xC1\u1EA2\xC3\u1EA0\u0102\u1EB0\u1EAE\u1EB2\u1EB4\u1EB6\xC2\u1EA6\u1EA4\u1EA8\u1EAA\u1EAC\xC4\xC5\u0100\u0104", "c\xE7\u0107\u010D", "C\xC7\u0106\u010C", "d\u0111\u010F", "D\u0110\u010E", "e\xE8\xE9\u1EBB\u1EBD\u1EB9\xEA\u1EC1\u1EBF\u1EC3\u1EC5\u1EC7\xEB\u011B\u0113\u0119", "E\xC8\xC9\u1EBA\u1EBC\u1EB8\xCA\u1EC0\u1EBE\u1EC2\u1EC4\u1EC6\xCB\u011A\u0112\u0118", "i\xEC\xED\u1EC9\u0129\u1ECB\xEE\xEF\u012B", "I\xCC\xCD\u1EC8\u0128\u1ECA\xCE\xCF\u012A", "l\u0142", "L\u0141", "n\xF1\u0148\u0144", "N\xD1\u0147\u0143", "o\xF2\xF3\u1ECF\xF5\u1ECD\xF4\u1ED3\u1ED1\u1ED5\u1ED7\u1ED9\u01A1\u1EDF\u1EE1\u1EDB\u1EDD\u1EE3\xF6\xF8\u014D", "O\xD2\xD3\u1ECE\xD5\u1ECC\xD4\u1ED2\u1ED0\u1ED4\u1ED6\u1ED8\u01A0\u1EDE\u1EE0\u1EDA\u1EDC\u1EE2\xD6\xD8\u014C", "r\u0159", "R\u0158", "s\u0161\u015B\u0219\u015F", "S\u0160\u015A\u0218\u015E", "t\u0165\u021B\u0163", "T\u0164\u021A\u0162", "u\xF9\xFA\u1EE7\u0169\u1EE5\u01B0\u1EEB\u1EE9\u1EED\u1EEF\u1EF1\xFB\xFC\u016F\u016B", "U\xD9\xDA\u1EE6\u0168\u1EE4\u01AF\u1EEA\u1EE8\u1EEC\u1EEE\u1EF0\xDB\xDC\u016E\u016A", "y\xFD\u1EF3\u1EF7\u1EF9\u1EF5\xFF", "Y\xDD\u1EF2\u1EF6\u1EF8\u1EF4\u0178", "z\u017E\u017C\u017A", "Z\u017D\u017B\u0179"] : ["a\xE0\xE1\u1EA3\xE3\u1EA1\u0103\u1EB1\u1EAF\u1EB3\u1EB5\u1EB7\xE2\u1EA7\u1EA5\u1EA9\u1EAB\u1EAD\xE4\xE5\u0101\u0105A\xC0\xC1\u1EA2\xC3\u1EA0\u0102\u1EB0\u1EAE\u1EB2\u1EB4\u1EB6\xC2\u1EA6\u1EA4\u1EA8\u1EAA\u1EAC\xC4\xC5\u0100\u0104", "c\xE7\u0107\u010DC\xC7\u0106\u010C", "d\u0111\u010FD\u0110\u010E", "e\xE8\xE9\u1EBB\u1EBD\u1EB9\xEA\u1EC1\u1EBF\u1EC3\u1EC5\u1EC7\xEB\u011B\u0113\u0119E\xC8\xC9\u1EBA\u1EBC\u1EB8\xCA\u1EC0\u1EBE\u1EC2\u1EC4\u1EC6\xCB\u011A\u0112\u0118", "i\xEC\xED\u1EC9\u0129\u1ECB\xEE\xEF\u012BI\xCC\xCD\u1EC8\u0128\u1ECA\xCE\xCF\u012A", "l\u0142L\u0141", "n\xF1\u0148\u0144N\xD1\u0147\u0143", "o\xF2\xF3\u1ECF\xF5\u1ECD\xF4\u1ED3\u1ED1\u1ED5\u1ED7\u1ED9\u01A1\u1EDF\u1EE1\u1EDB\u1EDD\u1EE3\xF6\xF8\u014DO\xD2\xD3\u1ECE\xD5\u1ECC\xD4\u1ED2\u1ED0\u1ED4\u1ED6\u1ED8\u01A0\u1EDE\u1EE0\u1EDA\u1EDC\u1EE2\xD6\xD8\u014C", "r\u0159R\u0158", "s\u0161\u015B\u0219\u015FS\u0160\u015A\u0218\u015E", "t\u0165\u021B\u0163T\u0164\u021A\u0162", "u\xF9\xFA\u1EE7\u0169\u1EE5\u01B0\u1EEB\u1EE9\u1EED\u1EEF\u1EF1\xFB\xFC\u016F\u016BU\xD9\xDA\u1EE6\u0168\u1EE4\u01AF\u1EEA\u1EE8\u1EEC\u1EEE\u1EF0\xDB\xDC\u016E\u016A", "y\xFD\u1EF3\u1EF7\u1EF9\u1EF5\xFFY\xDD\u1EF2\u1EF6\u1EF8\u1EF4\u0178", "z\u017E\u017C\u017AZ\u017D\u017B\u0179"]; + var handled = []; + str.split("").forEach(function(ch) { + dct.every(function(dct2) { + if (dct2.indexOf(ch) !== -1) { + if (handled.indexOf(dct2) > -1) { + return false; + } + str = str.replace(new RegExp("[" + dct2 + "]", "gm" + sens), "[" + dct2 + "]"); + handled.push(dct2); + } + return true; + }); + }); + return str; + } + }, { + key: "createMergedBlanksRegExp", + value: function createMergedBlanksRegExp(str) { + return str.replace(/[\s]+/gmi, "[\\s]+"); + } + }, { + key: "createAccuracyRegExp", + value: function createAccuracyRegExp(str) { + var _this = this; + var chars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\xA1\xBF"; + var acc = this.opt.accuracy, val = typeof acc === "string" ? acc : acc.value, ls = typeof acc === "string" ? [] : acc.limiters, lsJoin = ""; + ls.forEach(function(limiter) { + lsJoin += "|" + _this.escapeStr(limiter); + }); + switch (val) { + case "partially": + default: + return "()(" + str + ")"; + case "complementary": + lsJoin = "\\s" + (lsJoin ? lsJoin : this.escapeStr(chars)); + return "()([^" + lsJoin + "]*" + str + "[^" + lsJoin + "]*)"; + case "exactly": + return "(^|\\s" + lsJoin + ")(" + str + ")(?=$|\\s" + lsJoin + ")"; + } + } + }, { + key: "getSeparatedKeywords", + value: function getSeparatedKeywords(sv) { + var _this2 = this; + var stack = []; + sv.forEach(function(kw) { + if (!_this2.opt.separateWordSearch) { + if (kw.trim() && stack.indexOf(kw) === -1) { + stack.push(kw); + } + } else { + kw.split(" ").forEach(function(kwSplitted) { + if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) { + stack.push(kwSplitted); + } + }); + } + }); + return { + "keywords": stack.sort(function(a, b) { + return b.length - a.length; + }), + "length": stack.length + }; + } + }, { + key: "isNumeric", + value: function isNumeric(value) { + return Number(parseFloat(value)) == value; + } + }, { + key: "checkRanges", + value: function checkRanges(array) { + var _this3 = this; + if (!Array.isArray(array) || Object.prototype.toString.call(array[0]) !== "[object Object]") { + this.log("markRanges() will only accept an array of objects"); + this.opt.noMatch(array); + return []; + } + var stack = []; + var last = 0; + array.sort(function(a, b) { + return a.start - b.start; + }).forEach(function(item) { + var _callNoMatchOnInvalid = _this3.callNoMatchOnInvalidRanges(item, last), start = _callNoMatchOnInvalid.start, end = _callNoMatchOnInvalid.end, valid = _callNoMatchOnInvalid.valid; + if (valid) { + item.start = start; + item.length = end - start; + stack.push(item); + last = end; + } + }); + return stack; + } + }, { + key: "callNoMatchOnInvalidRanges", + value: function callNoMatchOnInvalidRanges(range, last) { + var start = void 0, end = void 0, valid = false; + if (range && typeof range.start !== "undefined") { + start = parseInt(range.start, 10); + end = start + parseInt(range.length, 10); + if (this.isNumeric(range.start) && this.isNumeric(range.length) && end - last > 0 && end - start > 0) { + valid = true; + } else { + this.log("Ignoring invalid or overlapping range: " + ("" + JSON.stringify(range))); + this.opt.noMatch(range); + } + } else { + this.log("Ignoring invalid range: " + JSON.stringify(range)); + this.opt.noMatch(range); + } + return { + start, + end, + valid + }; + } + }, { + key: "checkWhitespaceRanges", + value: function checkWhitespaceRanges(range, originalLength, string) { + var end = void 0, valid = true, max = string.length, offset = originalLength - max, start = parseInt(range.start, 10) - offset; + start = start > max ? max : start; + end = start + parseInt(range.length, 10); + if (end > max) { + end = max; + this.log("End range automatically set to the max value of " + max); + } + if (start < 0 || end - start < 0 || start > max || end > max) { + valid = false; + this.log("Invalid range: " + JSON.stringify(range)); + this.opt.noMatch(range); + } else if (string.substring(start, end).replace(/\s+/g, "") === "") { + valid = false; + this.log("Skipping whitespace only range: " + JSON.stringify(range)); + this.opt.noMatch(range); + } + return { + start, + end, + valid + }; + } + }, { + key: "getTextNodes", + value: function getTextNodes(cb) { + var _this4 = this; + var val = "", nodes = []; + this.iterator.forEachNode(NodeFilter.SHOW_TEXT, function(node) { + nodes.push({ + start: val.length, + end: (val += node.textContent).length, + node + }); + }, function(node) { + if (_this4.matchesExclude(node.parentNode)) { + return NodeFilter.FILTER_REJECT; + } else { + return NodeFilter.FILTER_ACCEPT; + } + }, function() { + cb({ + value: val, + nodes + }); + }); + } + }, { + key: "matchesExclude", + value: function matchesExclude(el) { + return DOMIterator.matches(el, this.opt.exclude.concat(["script", "style", "title", "head", "html"])); + } + }, { + key: "wrapRangeInTextNode", + value: function wrapRangeInTextNode(node, start, end) { + var hEl = !this.opt.element ? "mark" : this.opt.element, startNode = node.splitText(start), ret = startNode.splitText(end - start); + var repl = document.createElement(hEl); + repl.setAttribute("data-markjs", "true"); + if (this.opt.className) { + repl.setAttribute("class", this.opt.className); + } + repl.textContent = startNode.textContent; + startNode.parentNode.replaceChild(repl, startNode); + return ret; + } + }, { + key: "wrapRangeInMappedTextNode", + value: function wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) { + var _this5 = this; + dict.nodes.every(function(n, i) { + var sibl = dict.nodes[i + 1]; + if (typeof sibl === "undefined" || sibl.start > start) { + if (!filterCb(n.node)) { + return false; + } + var s = start - n.start, e = (end > n.end ? n.end : end) - n.start, startStr = dict.value.substr(0, n.start), endStr = dict.value.substr(e + n.start); + n.node = _this5.wrapRangeInTextNode(n.node, s, e); + dict.value = startStr + endStr; + dict.nodes.forEach(function(k, j) { + if (j >= i) { + if (dict.nodes[j].start > 0 && j !== i) { + dict.nodes[j].start -= e; + } + dict.nodes[j].end -= e; + } + }); + end -= e; + eachCb(n.node.previousSibling, n.start); + if (end > n.end) { + start = n.end; + } else { + return false; + } + } + return true; + }); + } + }, { + key: "wrapMatches", + value: function wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) { + var _this6 = this; + var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1; + this.getTextNodes(function(dict) { + dict.nodes.forEach(function(node) { + node = node.node; + var match = void 0; + while ((match = regex.exec(node.textContent)) !== null && match[matchIdx] !== "") { + if (!filterCb(match[matchIdx], node)) { + continue; + } + var pos = match.index; + if (matchIdx !== 0) { + for (var i = 1; i < matchIdx; i++) { + pos += match[i].length; + } + } + node = _this6.wrapRangeInTextNode(node, pos, pos + match[matchIdx].length); + eachCb(node.previousSibling); + regex.lastIndex = 0; + } + }); + endCb(); + }); + } + }, { + key: "wrapMatchesAcrossElements", + value: function wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) { + var _this7 = this; + var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1; + this.getTextNodes(function(dict) { + var match = void 0; + while ((match = regex.exec(dict.value)) !== null && match[matchIdx] !== "") { + var start = match.index; + if (matchIdx !== 0) { + for (var i = 1; i < matchIdx; i++) { + start += match[i].length; + } + } + var end = start + match[matchIdx].length; + _this7.wrapRangeInMappedTextNode(dict, start, end, function(node) { + return filterCb(match[matchIdx], node); + }, function(node, lastIndex) { + regex.lastIndex = lastIndex; + eachCb(node); + }); + } + endCb(); + }); + } + }, { + key: "wrapRangeFromIndex", + value: function wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) { + var _this8 = this; + this.getTextNodes(function(dict) { + var originalLength = dict.value.length; + ranges.forEach(function(range, counter) { + var _checkWhitespaceRange = _this8.checkWhitespaceRanges(range, originalLength, dict.value), start = _checkWhitespaceRange.start, end = _checkWhitespaceRange.end, valid = _checkWhitespaceRange.valid; + if (valid) { + _this8.wrapRangeInMappedTextNode(dict, start, end, function(node) { + return filterCb(node, range, dict.value.substring(start, end), counter); + }, function(node) { + eachCb(node, range); + }); + } + }); + endCb(); + }); + } + }, { + key: "unwrapMatches", + value: function unwrapMatches(node) { + var parent = node.parentNode; + var docFrag = document.createDocumentFragment(); + while (node.firstChild) { + docFrag.appendChild(node.removeChild(node.firstChild)); + } + parent.replaceChild(docFrag, node); + if (!this.ie) { + parent.normalize(); + } else { + this.normalizeTextNode(parent); + } + } + }, { + key: "normalizeTextNode", + value: function normalizeTextNode(node) { + if (!node) { + return; + } + if (node.nodeType === 3) { + while (node.nextSibling && node.nextSibling.nodeType === 3) { + node.nodeValue += node.nextSibling.nodeValue; + node.parentNode.removeChild(node.nextSibling); + } + } else { + this.normalizeTextNode(node.firstChild); + } + this.normalizeTextNode(node.nextSibling); + } + }, { + key: "markRegExp", + value: function markRegExp(regexp, opt) { + var _this9 = this; + this.opt = opt; + this.log('Searching with expression "' + regexp + '"'); + var totalMatches = 0, fn = "wrapMatches"; + var eachCb = function eachCb2(element) { + totalMatches++; + _this9.opt.each(element); + }; + if (this.opt.acrossElements) { + fn = "wrapMatchesAcrossElements"; + } + this[fn](regexp, this.opt.ignoreGroups, function(match, node) { + return _this9.opt.filter(node, match, totalMatches); + }, eachCb, function() { + if (totalMatches === 0) { + _this9.opt.noMatch(regexp); + } + _this9.opt.done(totalMatches); + }); + } + }, { + key: "mark", + value: function mark(sv, opt) { + var _this10 = this; + this.opt = opt; + var totalMatches = 0, fn = "wrapMatches"; + var _getSeparatedKeywords = this.getSeparatedKeywords(typeof sv === "string" ? [sv] : sv), kwArr = _getSeparatedKeywords.keywords, kwArrLen = _getSeparatedKeywords.length, sens = this.opt.caseSensitive ? "" : "i", handler = function handler2(kw) { + var regex = new RegExp(_this10.createRegExp(kw), "gm" + sens), matches = 0; + _this10.log('Searching with expression "' + regex + '"'); + _this10[fn](regex, 1, function(term, node) { + return _this10.opt.filter(node, kw, totalMatches, matches); + }, function(element) { + matches++; + totalMatches++; + _this10.opt.each(element); + }, function() { + if (matches === 0) { + _this10.opt.noMatch(kw); + } + if (kwArr[kwArrLen - 1] === kw) { + _this10.opt.done(totalMatches); + } else { + handler2(kwArr[kwArr.indexOf(kw) + 1]); + } + }); + }; + if (this.opt.acrossElements) { + fn = "wrapMatchesAcrossElements"; + } + if (kwArrLen === 0) { + this.opt.done(totalMatches); + } else { + handler(kwArr[0]); + } + } + }, { + key: "markRanges", + value: function markRanges(rawRanges, opt) { + var _this11 = this; + this.opt = opt; + var totalMatches = 0, ranges = this.checkRanges(rawRanges); + if (ranges && ranges.length) { + this.log("Starting to mark with the following ranges: " + JSON.stringify(ranges)); + this.wrapRangeFromIndex(ranges, function(node, range, match, counter) { + return _this11.opt.filter(node, range, match, counter); + }, function(element, range) { + totalMatches++; + _this11.opt.each(element, range); + }, function() { + _this11.opt.done(totalMatches); + }); + } else { + this.opt.done(totalMatches); + } + } + }, { + key: "unmark", + value: function unmark(opt) { + var _this12 = this; + this.opt = opt; + var sel = this.opt.element ? this.opt.element : "*"; + sel += "[data-markjs]"; + if (this.opt.className) { + sel += "." + this.opt.className; + } + this.log('Removal selector "' + sel + '"'); + this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, function(node) { + _this12.unwrapMatches(node); + }, function(node) { + var matchesSel = DOMIterator.matches(node, sel), matchesExclude = _this12.matchesExclude(node); + if (!matchesSel || matchesExclude) { + return NodeFilter.FILTER_REJECT; + } else { + return NodeFilter.FILTER_ACCEPT; + } + }, this.opt.done); + } + }, { + key: "opt", + set: function set$$1(val) { + this._opt = _extends({}, { + "element": "", + "className": "", + "exclude": [], + "iframes": false, + "iframesTimeout": 5e3, + "separateWordSearch": true, + "diacritics": true, + "synonyms": {}, + "accuracy": "partially", + "acrossElements": false, + "caseSensitive": false, + "ignoreJoiners": false, + "ignoreGroups": 0, + "ignorePunctuation": [], + "wildcards": "disabled", + "each": function each() { + }, + "noMatch": function noMatch() { + }, + "filter": function filter() { + return true; + }, + "done": function done() { + }, + "debug": false, + "log": window.console + }, val); + }, + get: function get$$1() { + return this._opt; + } + }, { + key: "iterator", + get: function get$$1() { + return new DOMIterator(this.ctx, this.opt.iframes, this.opt.exclude, this.opt.iframesTimeout); + } + }]); + return Mark3; + }(); + function Mark2(ctx) { + var _this = this; + var instance = new Mark$1(ctx); + this.mark = function(sv, opt) { + instance.mark(sv, opt); + return _this; + }; + this.markRegExp = function(sv, opt) { + instance.markRegExp(sv, opt); + return _this; + }; + this.markRanges = function(sv, opt) { + instance.markRanges(sv, opt); + return _this; + }; + this.unmark = function(opt) { + instance.unmark(opt); + return _this; + }; + return this; + } + return Mark2; + }); + } +}); + +// lib/highlight.ts +var import_mark = __toESM(require_mark(), 1); +var PagefindHighlight = class { + constructor(options = { + markContext: null, + highlightParam: "pagefind-highlight", + markOptions: { + className: "pagefind-highlight", + exclude: ["[data-pagefind-ignore]", "[data-pagefind-ignore] *"] + }, + addStyles: true + }) { + var _a, _b; + const { highlightParam, markContext, markOptions, addStyles } = options; + this.highlightParam = highlightParam ?? "pagefind-highlight"; + this.addStyles = addStyles ?? true; + this.markContext = markContext !== void 0 ? markContext : null; + this.markOptions = markOptions !== void 0 ? markOptions : { + className: "pagefind-highlight", + exclude: ["[data-pagefind-ignore]", "[data-pagefind-ignore] *"] + }; + (_a = this.markOptions).className ?? (_a.className = "pagefind__highlight"); + (_b = this.markOptions).exclude ?? (_b.exclude = [ + "[data-pagefind-ignore]", + "[data-pagefind-ignore] *" + ]); + this.markOptions.separateWordSearch = false; + this.highlight(); + } + getHighlightParams(paramName) { + const urlParams = new URLSearchParams(window.location.search); + return urlParams.getAll(paramName); + } + // Inline styles might be too hard to override + addHighlightStyles(className) { + if (!className) + return; + const styleElement = document.createElement("style"); + styleElement.innerText = `:where(.${className}) { background-color: yellow; color: black; }`; + document.head.appendChild(styleElement); + } + createMarkInstance() { + if (this.markContext) { + return new import_mark.default(this.markContext); + } + const pagefindBody = document.querySelectorAll("[data-pagefind-body]"); + if (pagefindBody.length !== 0) { + return new import_mark.default(pagefindBody); + } else { + return new import_mark.default(document.body); + } + } + markText(instance, text) { + instance.mark(text, this.markOptions); + } + highlight() { + const params = this.getHighlightParams(this.highlightParam); + if (!params || params.length === 0) + return; + this.addStyles && this.addHighlightStyles(this.markOptions.className); + const markInstance = this.createMarkInstance(); + this.markText(markInstance, params); + } +}; +window.PagefindHighlight = PagefindHighlight; +export { + PagefindHighlight as default +}; +/*! Bundled license information: + +mark.js/dist/mark.js: + (*!*************************************************** + * mark.js v8.11.1 + * https://markjs.io/ + * Copyright (c) 2014–2018, Julian Kühnel + * Released under the MIT license https://git.io/vwTVl + *****************************************************) +*/ diff --git a/docs/_/pagefind/pagefind-modular-ui.css b/docs/_/pagefind/pagefind-modular-ui.css new file mode 100644 index 00000000..9c6793ed --- /dev/null +++ b/docs/_/pagefind/pagefind-modular-ui.css @@ -0,0 +1,214 @@ +:root { + --pagefind-ui-scale: 0.8; + --pagefind-ui-primary: #034AD8; + --pagefind-ui-fade: #707070; + --pagefind-ui-text: #393939; + --pagefind-ui-background: #ffffff; + --pagefind-ui-border: #eeeeee; + --pagefind-ui-tag: #eeeeee; + --pagefind-ui-border-width: 2px; + --pagefind-ui-border-radius: 8px; + --pagefind-ui-image-border-radius: 8px; + --pagefind-ui-image-box-ratio: 3 / 2; + --pagefind-ui-font: system, -apple-system, ".SFNSText-Regular", + "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", + "Lucida Grande", sans-serif; +} + +[data-pfmod-hidden] { + display: none !important; +} + +[data-pfmod-suppressed] { + opacity: 0 !important; + pointer-events: none !important; +} + +[data-pfmod-sr-hidden] { + -webkit-clip: rect(0 0 0 0) !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + height: 1px !important; + overflow: hidden !important; + overflow: clip !important; + position: absolute !important; + white-space: nowrap !important; + width: 1px !important; +} + +[data-pfmod-loading] { + color: var(--pagefind-ui-text); + background-color: var(--pagefind-ui-text); + border-radius: var(--pagefind-ui-border-radius); + opacity: 0.1; + pointer-events: none; +} + +/* Input */ + +.pagefind-modular-input-wrapper { + position: relative; +} + +.pagefind-modular-input-wrapper::before { + background-color: var(--pagefind-ui-text); + width: calc(18px * var(--pagefind-ui-scale)); + height: calc(18px * var(--pagefind-ui-scale)); + top: calc(23px * var(--pagefind-ui-scale)); + left: calc(20px * var(--pagefind-ui-scale)); + content: ""; + position: absolute; + display: block; + opacity: 0.7; + -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A"); + mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A"); + -webkit-mask-size: 100%; + mask-size: 100%; + z-index: 9; + pointer-events: none; +} + +.pagefind-modular-input { + height: calc(64px * var(--pagefind-ui-scale)); + padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale)); + background-color: var(--pagefind-ui-background); + border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border); + border-radius: var(--pagefind-ui-border-radius); + font-size: calc(21px * var(--pagefind-ui-scale)); + position: relative; + appearance: none; + -webkit-appearance: none; + display: flex; + width: 100%; + box-sizing: border-box; + font-weight: 700; +} + +.pagefind-modular-input::placeholder { + opacity: 0.2; +} + +.pagefind-modular-input-clear { + position: absolute; + top: calc(2px * var(--pagefind-ui-scale)); + right: calc(2px * var(--pagefind-ui-scale)); + height: calc(60px * var(--pagefind-ui-scale)); + border-radius: var(--pagefind-ui-border-radius); + padding: 0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale)); + color: var(--pagefind-ui-text); + font-size: calc(14px * var(--pagefind-ui-scale)); + cursor: pointer; + background-color: var(--pagefind-ui-background); + border: none; + appearance: none; +} + +/* ResultList */ + +.pagefind-modular-list-result { + list-style-type: none; + display: flex; + align-items: flex-start; + gap: min(calc(40px * var(--pagefind-ui-scale)), 3%); + padding: calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale)); + border-top: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border); +} + +.pagefind-modular-list-result:last-of-type { + border-bottom: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border); +} + +.pagefind-modular-list-thumb { + width: min(30%, + calc((30% - (100px * var(--pagefind-ui-scale))) * 100000)); + max-width: calc(120px * var(--pagefind-ui-scale)); + margin-top: calc(10px * var(--pagefind-ui-scale)); + aspect-ratio: var(--pagefind-ui-image-box-ratio); + position: relative; +} + +.pagefind-modular-list-image { + display: block; + position: absolute; + left: 50%; + transform: translateX(-50%); + font-size: 0; + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; + border-radius: var(--pagefind-ui-image-border-radius); +} + +.pagefind-modular-list-inner { + flex: 1; + display: flex; + flex-direction: column; + align-items: flex-start; + margin-top: calc(10px * var(--pagefind-ui-scale)); +} + +.pagefind-modular-list-title { + display: inline-block; + font-weight: 700; + font-size: calc(21px * var(--pagefind-ui-scale)); + margin-top: 0; + margin-bottom: 0; +} + +.pagefind-modular-list-link { + color: var(--pagefind-ui-text); + text-decoration: none; +} + +.pagefind-modular-list-link:hover { + text-decoration: underline; +} + +.pagefind-modular-list-excerpt { + display: inline-block; + font-weight: 400; + font-size: calc(16px * var(--pagefind-ui-scale)); + margin-top: calc(4px * var(--pagefind-ui-scale)); + margin-bottom: 0; + min-width: calc(250px * var(--pagefind-ui-scale)); +} + +/* FilterPills */ + +.pagefind-modular-filter-pills-wrapper { + overflow-x: scroll; + padding: 15px 0; +} + +.pagefind-modular-filter-pills { + display: flex; + gap: 6px; +} + +.pagefind-modular-filter-pill { + display: flex; + justify-content: center; + align-items: center; + border: none; + appearance: none; + padding: 0 calc(24px * var(--pagefind-ui-scale)); + background-color: var(--pagefind-ui-background); + color: var(--pagefind-ui-fade); + border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border); + border-radius: calc(25px * var(--pagefind-ui-scale)); + font-size: calc(18px * var(--pagefind-ui-scale)); + height: calc(50px * var(--pagefind-ui-scale)); + cursor: pointer; + white-space: nowrap; +} + +.pagefind-modular-filter-pill:hover { + border-color: var(--pagefind-ui-primary); +} + +.pagefind-modular-filter-pill[aria-pressed="true"] { + border-color: var(--pagefind-ui-primary); + color: var(--pagefind-ui-primary); +} \ No newline at end of file diff --git a/docs/_/pagefind/pagefind-modular-ui.js b/docs/_/pagefind/pagefind-modular-ui.js new file mode 100644 index 00000000..93019091 --- /dev/null +++ b/docs/_/pagefind/pagefind-modular-ui.js @@ -0,0 +1,8 @@ +(()=>{var b=Object.defineProperty;var w=(i,e)=>{for(var t in e)b(i,t,{get:e[t],enumerable:!0})};var f={};w(f,{FilterPills:()=>h,Input:()=>l,Instance:()=>p,ResultList:()=>a,Summary:()=>o});var r=class i{constructor(e){this.element=document.createElement(e)}id(e){return this.element.id=e,this}class(e){return this.element.classList.add(e),this}attrs(e){for(let[t,s]of Object.entries(e))this.element.setAttribute(t,s);return this}text(e){return this.element.innerText=e,this}html(e){return this.element.innerHTML=e,this}handle(e,t){return this.element.addEventListener(e,t),this}addTo(e){return e instanceof i?e.element.appendChild(this.element):e.appendChild(this.element),this.element}};var T=async(i=100)=>new Promise(e=>setTimeout(e,i)),l=class{constructor(e={}){if(this.inputEl=null,this.clearEl=null,this.instance=null,this.searchID=0,this.debounceTimeoutMs=e.debounceTimeoutMs??300,e.inputElement){if(e.containerElement){console.warn("[Pagefind Input component]: inputElement and containerElement both supplied. Ignoring the container option.");return}this.initExisting(e.inputElement)}else if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Input component]: No selector supplied for containerElement or inputElement");return}this.inputEl.addEventListener("input",async t=>{if(this.instance&&typeof t?.target?.value=="string"){this.updateState(t.target.value);let s=++this.searchID;if(await T(this.debounceTimeoutMs),s!==this.searchID)return null;this.instance?.triggerSearch(t.target.value)}}),this.inputEl.addEventListener("keydown",t=>{t.key==="Escape"&&(++this.searchID,this.inputEl.value="",this.instance?.triggerSearch(""),this.updateState("")),t.key==="Enter"&&t.preventDefault()}),this.inputEl.addEventListener("focus",()=>{this.instance?.triggerLoad()})}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No container found for ${e} selector`);return}if(t.tagName==="INPUT")console.warn(`[Pagefind Input component]: Encountered input element for ${e} when a container was expected`),console.warn("[Pagefind Input component]: Treating containerElement option as inputElement and proceeding"),this.initExisting(e);else{t.innerHTML="";let s=0;for(;document.querySelector(`#pfmod-input-${s}`);)s+=1;let n=new r("form").class("pagefind-modular-input-wrapper").attrs({role:"search","aria-label":"Search this site",action:"javascript:void(0);"});new r("label").attrs({for:`pfmod-input-${s}`,"data-pfmod-sr-hidden":"true"}).text("Search this site").addTo(n),this.inputEl=new r("input").id(`pfmod-input-${s}`).class("pagefind-modular-input").attrs({autocapitalize:"none",enterkeyhint:"search"}).addTo(n),this.clearEl=new r("button").class("pagefind-modular-input-clear").attrs({"data-pfmod-suppressed":"true"}).text("Clear").handle("click",()=>{this.inputEl.value="",this.instance.triggerSearch(""),this.updateState("")}).addTo(n),n.addTo(t)}}initExisting(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No input element found for ${e} selector`);return}if(t.tagName!=="INPUT"){console.error(`[Pagefind Input component]: Expected ${e} to be an element`);return}this.inputEl=t}updateState(e){this.clearEl&&(e&&e?.length?this.clearEl.removeAttribute("data-pfmod-suppressed"):this.clearEl.setAttribute("data-pfmod-suppressed","true"))}register(e){this.instance=e,this.instance.on("search",(t,s)=>{this.inputEl&&document.activeElement!==this.inputEl&&(this.inputEl.value=t,this.updateState(t))})}focus(){this.inputEl&&this.inputEl.focus()}};var g=i=>{if(i instanceof Element)return[i];if(Array.isArray(i)&&i.every(e=>e instanceof Element))return i;if(typeof i=="string"||i instanceof String){let e=document.createElement("div");return e.innerHTML=i,[...e.childNodes]}else return console.error(`[Pagefind ResultList component]: Expected template function to return an HTML element or string, got ${typeof i}`),[]},v=()=>{let i=(e=30)=>". ".repeat(Math.floor(10+Math.random()*e));return`
  • +
    +
    +

    ${i(30)}

    +

    ${i(40)}

    +
    +
  • `},y=i=>{let e=new r("li").class("pagefind-modular-list-result"),t=new r("div").class("pagefind-modular-list-thumb").addTo(e);i?.meta?.image&&new r("img").class("pagefind-modular-list-image").attrs({src:i.meta.image,alt:i.meta.image_alt||i.meta.title}).addTo(t);let s=new r("div").class("pagefind-modular-list-inner").addTo(e),n=new r("p").class("pagefind-modular-list-title").addTo(s);return new r("a").class("pagefind-modular-list-link").text(i.meta?.title).attrs({href:i.meta?.url||i.url}).addTo(n),new r("p").class("pagefind-modular-list-excerpt").html(i.excerpt).addTo(s),e.element},E=i=>{if(!(i instanceof HTMLElement))return null;let e=window.getComputedStyle(i).overflowY;return e!=="visible"&&e!=="hidden"?i:E(i.parentNode)},d=class{constructor(e={}){this.rawResult=e.result,this.placeholderNodes=e.placeholderNodes,this.resultFn=e.resultFn,this.intersectionEl=e.intersectionEl,this.result=null,this.waitForIntersection()}waitForIntersection(){if(!this.placeholderNodes?.length)return;let e={root:this.intersectionEl,rootMargin:"0px",threshold:.01};new IntersectionObserver((s,n)=>{this.result===null&&s?.[0]?.isIntersecting&&(this.load(),n.disconnect())},e).observe(this.placeholderNodes[0])}async load(){if(!this.placeholderNodes?.length)return;this.result=await this.rawResult.data();let e=this.resultFn(this.result),t=g(e);for(;this.placeholderNodes.length>1;)this.placeholderNodes.pop().remove();this.placeholderNodes[0].replaceWith(...t)}},a=class{constructor(e){if(this.intersectionEl=document.body,this.containerEl=null,this.results=[],this.placeholderTemplate=e.placeholderTemplate??v,this.resultTemplate=e.resultTemplate??y,e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind ResultList component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind ResultList component]: No container found for ${e} selector`);return}this.containerEl=t}append(e){for(let t of e)this.containerEl.appendChild(t)}register(e){e.on("results",t=>{this.containerEl&&(this.containerEl.innerHTML="",this.intersectionEl=E(this.containerEl),this.results=t.results.map(s=>{let n=g(this.placeholderTemplate());return this.append(n),new d({result:s,placeholderNodes:n,resultFn:this.resultTemplate,intersectionEl:this.intersectionEl})}))}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerHTML="")})}};var o=class{constructor(e={}){if(this.containerEl=null,this.defaultMessage=e.defaultMessage??"",this.term="",e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Summary component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Summary component]: No container found for ${e} selector`);return}this.containerEl=t,this.containerEl.innerText=this.defaultMessage}register(e){e.on("search",(t,s)=>{this.term=t}),e.on("results",t=>{if(!this.containerEl||!t)return;if(!this.term){this.containerEl.innerText=this.defaultMessage;return}let s=t?.results?.length??0;this.containerEl.innerText=`${s} result${s===1?"":"s"} for ${this.term}`}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerText=`Searching for ${this.term}...`)})}};var h=class{constructor(e={}){if(this.instance=null,this.wrapper=null,this.pillContainer=null,this.available={},this.selected=["All"],this.total=0,this.filterMemo="",this.filter=e.filter,this.ordering=e.ordering??null,this.alwaysShow=e.alwaysShow??!1,this.selectMultiple=e.selectMultiple??!1,!this.filter?.length){console.error("[Pagefind FilterPills component]: No filter option supplied, nothing to display");return}if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind FilterPills component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind FilterPills component]: No container found for ${e} selector`);return}t.innerHTML="";let s=`pagefind_modular_filter_pills_${this.filter}`,n=new r("div").class("pagefind-modular-filter-pills-wrapper").attrs({role:"group","aria-labelledby":s});this.alwaysShow||n.attrs({"data-pfmod-hidden":!0}),new r("div").id(s).class("pagefind-modular-filter-pills-label").attrs({"data-pfmod-sr-hidden":!0}).text(`Filter results by ${this.filter}`).addTo(n),this.pillContainer=new r("div").class("pagefind-modular-filter-pills").addTo(n),this.wrapper=n.addTo(t)}update(){let e=this.available.map(t=>t[0]).join("~");e==this.filterMemo?this.updateExisting():(this.renderNew(),this.filterMemo=e)}pushFilters(){let e=this.selected.filter(t=>t!=="All");this.instance.triggerFilter(this.filter,e)}pillInner(e,t){return this.total?`${e} (${t})`:`${e}`}renderNew(){this.available.forEach(([e,t])=>{new r("button").class("pagefind-modular-filter-pill").html(this.pillInner(e,t)).attrs({"aria-pressed":this.selected.includes(e),type:"button"}).handle("click",()=>{e==="All"?this.selected=["All"]:this.selected.includes(e)?this.selected=this.selected.filter(s=>s!==e):this.selectMultiple?this.selected.push(e):this.selected=[e],this.selected?.length?this.selected?.length>1&&(this.selected=this.selected.filter(s=>s!=="All")):this.selected=["All"],this.update(),this.pushFilters()}).addTo(this.pillContainer)})}updateExisting(){let e=[...this.pillContainer.childNodes];this.available.forEach(([t,s],n)=>{e[n].innerHTML=this.pillInner(t,s),e[n].setAttribute("aria-pressed",this.selected.includes(t))})}register(e){this.instance=e,this.instance.on("filters",t=>{if(!this.pillContainer)return;this.selectMultiple?t=t.available:t=t.total;let s=t[this.filter];if(!s){console.warn(`[Pagefind FilterPills component]: No possible values found for the ${this.filter} filter`);return}this.available=Object.entries(s),Array.isArray(this.ordering)?this.available.sort((n,c)=>{let m=this.ordering.indexOf(n[0]),_=this.ordering.indexOf(c[0]);return(m===-1?1/0:m)-(_===-1?1/0:_)}):this.available.sort((n,c)=>n[0].localeCompare(c[0])),this.available.unshift(["All",this.total]),this.update()}),e.on("results",t=>{this.pillContainer&&(this.total=t?.unfilteredResultCount||0,this.available?.[0]?.[0]==="All"&&(this.available[0][1]=this.total),this.total||this.alwaysShow?this.wrapper.removeAttribute("data-pfmod-hidden"):this.wrapper.setAttribute("data-pfmod-hidden","true"),this.update())})}};var F=async(i=50)=>await new Promise(e=>setTimeout(e,i)),u;try{u=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind-)?modular-ui.js.*$/)[1]}catch{u="/pagefind/"}var p=class{constructor(e={}){this.__pagefind__=null,this.__initializing__=null,this.__searchID__=0,this.__hooks__={search:[],filters:[],loading:[],results:[]},this.components=[],this.searchTerm="",this.searchFilters={},this.searchResult={},this.availableFilters=null,this.totalFilters=null,this.options={bundlePath:e.bundlePath??u,mergeIndex:e.mergeIndex??[]},delete e.bundlePath,delete e.resetStyles,delete e.processResult,delete e.processTerm,delete e.debounceTimeoutMs,delete e.mergeIndex,delete e.translations,this.pagefindOptions=e}add(e){e?.register?.(this),this.components.push(e)}on(e,t){if(!this.__hooks__[e]){let s=Object.keys(this.__hooks__).join(", ");console.error(`[Pagefind Composable]: Unknown event type ${e}. Supported events: [${s}]`);return}if(typeof t!="function"){console.error(`[Pagefind Composable]: Expected callback to be a function, received ${typeof t}`);return}this.__hooks__[e].push(t)}triggerLoad(){this.__load__()}triggerSearch(e){this.searchTerm=e,this.__dispatch__("search",e,this.searchFilters),this.__search__(e,this.searchFilters)}triggerSearchWithFilters(e,t){this.searchTerm=e,this.searchFilters=t,this.__dispatch__("search",e,t),this.__search__(e,t)}triggerFilters(e){this.searchFilters=e,this.__dispatch__("search",this.searchTerm,e),this.__search__(this.searchTerm,e)}triggerFilter(e,t){this.searchFilters=this.searchFilters||{},this.searchFilters[e]=t,this.__dispatch__("search",this.searchTerm,this.searchFilters),this.__search__(this.searchTerm,this.searchFilters)}__dispatch__(e,...t){this.__hooks__[e]?.forEach(s=>s?.(...t))}async __clear__(){this.__dispatch__("results",{results:[],unfilteredTotalCount:0}),this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}async __search__(e,t){this.__dispatch__("loading"),await this.__load__();let s=++this.__searchID__;if(!e||!e.length)return this.__clear__();let n=await this.__pagefind__.search(e,{filters:t});n&&this.__searchID__===s&&(n.filters&&Object.keys(n.filters)?.length&&(this.availableFilters=n.filters,this.totalFilters=n.totalFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})),this.searchResult=n,this.__dispatch__("results",this.searchResult))}async __load__(){if(this.__initializing__){for(;!this.__pagefind__;)await F(50);return}if(this.__initializing__=!0,!this.__pagefind__){let e;try{e=await import(`${this.options.bundlePath}pagefind.js`)}catch(t){console.error(t),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to PagefindComposable Instance",`[DEBUG: Loaded from ${document?.currentScript?.src??"no known script location"}]`].join(` +`))}await e.options(this.pagefindOptions||{});for(let t of this.options.mergeIndex){if(!t.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let s=t.bundlePath;delete t.bundlePath,await e.mergeIndex(s,t)}this.__pagefind__=e}this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}};window.PagefindModularUI=f;})(); diff --git a/docs/_/pagefind/pagefind-ui.css b/docs/_/pagefind/pagefind-ui.css new file mode 100644 index 00000000..d7984a98 --- /dev/null +++ b/docs/_/pagefind/pagefind-ui.css @@ -0,0 +1 @@ +.pagefind-ui__result.svelte-j9e30.svelte-j9e30{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-j9e30.svelte-j9e30:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-thumb.svelte-j9e30.svelte-j9e30{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-j9e30.svelte-j9e30{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-j9e30.svelte-j9e30{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-j9e30.svelte-j9e30{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-j9e30.svelte-j9e30{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-j9e30.svelte-j9e30{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf{display:flex;flex-direction:column;padding-left:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf:first-of-type{padding-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{font-size:.9em;position:relative}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:before{content:"\2937 ";position:absolute;top:0;right:calc(100% + .1em)}.pagefind-ui__result-thumb.svelte-4xnkmf.svelte-4xnkmf{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-4xnkmf.svelte-4xnkmf{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-4xnkmf.svelte-4xnkmf{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-4xnkmf.svelte-4xnkmf{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-4xnkmf.svelte-4xnkmf{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}legend.svelte-1v2r7ls.svelte-1v2r7ls{position:absolute;clip:rect(0 0 0 0)}.pagefind-ui__filter-panel.svelte-1v2r7ls.svelte-1v2r7ls{min-width:min(calc(260px * var(--pagefind-ui-scale)),100%);flex:1;display:flex;flex-direction:column;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{border:0;padding:0}.pagefind-ui__filter-block.svelte-1v2r7ls.svelte-1v2r7ls{padding:0;display:block;border-bottom:solid calc(2px * var(--pagefind-ui-scale)) var(--pagefind-ui-border);padding:calc(20px * var(--pagefind-ui-scale)) 0}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls{font-size:calc(16px * var(--pagefind-ui-scale));position:relative;display:flex;align-items:center;list-style:none;font-weight:700;cursor:pointer;height:calc(24px * var(--pagefind-ui-scale))}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls::-webkit-details-marker{display:none}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls:after{position:absolute;content:"";right:calc(6px * var(--pagefind-ui-scale));top:50%;width:calc(8px * var(--pagefind-ui-scale));height:calc(8px * var(--pagefind-ui-scale));border:solid calc(2px * var(--pagefind-ui-scale)) currentColor;border-right:0;border-top:0;transform:translateY(-70%) rotate(-45deg)}.pagefind-ui__filter-block[open].svelte-1v2r7ls .pagefind-ui__filter-name.svelte-1v2r7ls:after{transform:translateY(-70%) rotate(-225deg)}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{display:flex;flex-direction:column;gap:calc(20px * var(--pagefind-ui-scale));padding-top:calc(30px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls{position:relative;display:flex;align-items:center;gap:calc(8px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls:before{position:absolute;content:"";top:50%;left:calc(8px * var(--pagefind-ui-scale));width:0px;height:0px;border:solid 1px #fff;opacity:0;transform:translate(calc(4.5px * var(--pagefind-ui-scale) * -1),calc(.8px * var(--pagefind-ui-scale))) skew(-5deg) rotate(-45deg);transform-origin:top left;border-top:0;border-right:0;pointer-events:none}.pagefind-ui__filter-value.pagefind-ui__filter-value--checked.svelte-1v2r7ls.svelte-1v2r7ls:before{opacity:1;width:calc(9px * var(--pagefind-ui-scale));height:calc(4px * var(--pagefind-ui-scale));transition:width .1s ease-out .1s,height .1s ease-in}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls{margin:0;width:calc(16px * var(--pagefind-ui-scale));height:calc(16px * var(--pagefind-ui-scale));border:solid 1px var(--pagefind-ui-border);appearance:none;-webkit-appearance:none;border-radius:calc(var(--pagefind-ui-border-radius) / 2);background-color:var(--pagefind-ui-background);cursor:pointer}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls:checked{background-color:var(--pagefind-ui-primary);border:solid 1px var(--pagefind-ui-primary)}.pagefind-ui__filter-label.svelte-1v2r7ls.svelte-1v2r7ls{cursor:pointer;font-size:calc(16px * var(--pagefind-ui-scale));font-weight:400}.pagefind-ui--reset *:where(:not(html,iframe,canvas,img,svg,video):not(svg *,symbol *)){all:unset;display:revert;outline:revert}.pagefind-ui--reset *,.pagefind-ui--reset *:before,.pagefind-ui--reset *:after{box-sizing:border-box}.pagefind-ui--reset a,.pagefind-ui--reset button{cursor:revert}.pagefind-ui--reset ol,.pagefind-ui--reset ul,.pagefind-ui--reset menu{list-style:none}.pagefind-ui--reset img{max-width:100%}.pagefind-ui--reset table{border-collapse:collapse}.pagefind-ui--reset input,.pagefind-ui--reset textarea{-webkit-user-select:auto}.pagefind-ui--reset textarea{white-space:revert}.pagefind-ui--reset meter{-webkit-appearance:revert;appearance:revert}.pagefind-ui--reset ::placeholder{color:unset}.pagefind-ui--reset :where([hidden]){display:none}.pagefind-ui--reset :where([contenteditable]:not([contenteditable="false"])){-moz-user-modify:read-write;-webkit-user-modify:read-write;overflow-wrap:break-word;-webkit-line-break:after-white-space;-webkit-user-select:auto}.pagefind-ui--reset :where([draggable="true"]){-webkit-user-drag:element}.pagefind-ui--reset mark{all:revert}:root{--pagefind-ui-scale:.8;--pagefind-ui-primary:#393939;--pagefind-ui-text:#393939;--pagefind-ui-background:#ffffff;--pagefind-ui-border:#eeeeee;--pagefind-ui-tag:#eeeeee;--pagefind-ui-border-width:2px;--pagefind-ui-border-radius:8px;--pagefind-ui-image-border-radius:8px;--pagefind-ui-image-box-ratio:3 / 2;--pagefind-ui-font:system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif}.pagefind-ui.svelte-e9gkc3{width:100%;color:var(--pagefind-ui-text);font-family:var(--pagefind-ui-font)}.pagefind-ui__hidden.svelte-e9gkc3{display:none!important}.pagefind-ui__suppressed.svelte-e9gkc3{opacity:0;pointer-events:none}.pagefind-ui__form.svelte-e9gkc3{position:relative}.pagefind-ui__form.svelte-e9gkc3:before{background-color:var(--pagefind-ui-text);width:calc(18px * var(--pagefind-ui-scale));height:calc(18px * var(--pagefind-ui-scale));top:calc(23px * var(--pagefind-ui-scale));left:calc(20px * var(--pagefind-ui-scale));content:"";position:absolute;display:block;opacity:.7;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");-webkit-mask-size:100%;mask-size:100%;z-index:9;pointer-events:none}.pagefind-ui__search-input.svelte-e9gkc3{height:calc(64px * var(--pagefind-ui-scale));padding:0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));background-color:var(--pagefind-ui-background);border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);font-size:calc(21px * var(--pagefind-ui-scale));position:relative;appearance:none;-webkit-appearance:none;display:flex;width:100%;box-sizing:border-box;font-weight:700}.pagefind-ui__search-input.svelte-e9gkc3::placeholder{opacity:.2}.pagefind-ui__search-clear.svelte-e9gkc3{position:absolute;top:calc(3px * var(--pagefind-ui-scale));right:calc(3px * var(--pagefind-ui-scale));height:calc(58px * var(--pagefind-ui-scale));padding:0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale));color:var(--pagefind-ui-text);font-size:calc(14px * var(--pagefind-ui-scale));cursor:pointer;background-color:var(--pagefind-ui-background);border-radius:var(--pagefind-ui-border-radius)}.pagefind-ui__drawer.svelte-e9gkc3{gap:calc(60px * var(--pagefind-ui-scale));display:flex;flex-direction:row;flex-wrap:wrap}.pagefind-ui__results-area.svelte-e9gkc3{min-width:min(calc(400px * var(--pagefind-ui-scale)),100%);flex:1000;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__results.svelte-e9gkc3{padding:0}.pagefind-ui__message.svelte-e9gkc3{box-sizing:content-box;font-size:calc(16px * var(--pagefind-ui-scale));height:calc(24px * var(--pagefind-ui-scale));padding:calc(20px * var(--pagefind-ui-scale)) 0;display:flex;align-items:center;font-weight:700;margin-top:0}.pagefind-ui__button.svelte-e9gkc3{margin-top:calc(40px * var(--pagefind-ui-scale));border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);height:calc(48px * var(--pagefind-ui-scale));padding:0 calc(12px * var(--pagefind-ui-scale));font-size:calc(16px * var(--pagefind-ui-scale));color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background);width:100%;text-align:center;font-weight:700;cursor:pointer}.pagefind-ui__button.svelte-e9gkc3:hover{border-color:var(--pagefind-ui-primary);color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background)} diff --git a/docs/_/pagefind/pagefind-ui.js b/docs/_/pagefind/pagefind-ui.js new file mode 100644 index 00000000..e964ecaf --- /dev/null +++ b/docs/_/pagefind/pagefind-ui.js @@ -0,0 +1,2 @@ +(()=>{var is=Object.defineProperty;var v=(n,e)=>{for(var t in e)is(n,t,{get:e[t],enumerable:!0})};function j(){}function lt(n){return n()}function Qt(){return Object.create(null)}function V(n){n.forEach(lt)}function Ye(n){return typeof n=="function"}function G(n,e){return n!=n?e==e:n!==e||n&&typeof n=="object"||typeof n=="function"}var Ke;function le(n,e){return Ke||(Ke=document.createElement("a")),Ke.href=e,n===Ke.href}function xt(n){return Object.keys(n).length===0}var $t=typeof window<"u"?window:typeof globalThis<"u"?globalThis:global,fe=class{constructor(e){this.options=e,this._listeners="WeakMap"in $t?new WeakMap:void 0}observe(e,t){return this._listeners.set(e,t),this._getObserver().observe(e,this.options),()=>{this._listeners.delete(e),this._observer.unobserve(e)}}_getObserver(){var e;return(e=this._observer)!==null&&e!==void 0?e:this._observer=new ResizeObserver(t=>{var s;for(let r of t)fe.entries.set(r.target,r),(s=this._listeners.get(r.target))===null||s===void 0||s(r)})}};fe.entries="WeakMap"in $t?new WeakMap:void 0;var en=!1;function as(){en=!0}function os(){en=!1}function b(n,e){n.appendChild(e)}function y(n,e,t){n.insertBefore(e,t||null)}function C(n){n.parentNode&&n.parentNode.removeChild(n)}function Q(n,e){for(let t=0;tn.removeEventListener(e,t,s)}function p(n,e,t){t==null?n.removeAttribute(e):n.getAttribute(e)!==t&&n.setAttribute(e,t)}function cs(n){return Array.from(n.childNodes)}function N(n,e){e=""+e,n.data!==e&&(n.data=e)}function it(n,e){n.value=e??""}function W(n,e,t){n.classList[t?"add":"remove"](e)}var Xe=class{constructor(e=!1){this.is_svg=!1,this.is_svg=e,this.e=this.n=null}c(e){this.h(e)}m(e,t,s=null){this.e||(this.is_svg?this.e=us(t.nodeName):this.e=k(t.nodeType===11?"TEMPLATE":t.nodeName),this.t=t.tagName!=="TEMPLATE"?t:t.content,this.c(e)),this.i(s)}h(e){this.e.innerHTML=e,this.n=Array.from(this.e.nodeName==="TEMPLATE"?this.e.content.childNodes:this.e.childNodes)}i(e){for(let t=0;tn.indexOf(s)===-1?e.push(s):t.push(s)),t.forEach(s=>s()),se=e}var Je=new Set,ee;function ie(){ee={r:0,c:[],p:ee}}function ae(){ee.r||V(ee.c),ee=ee.p}function z(n,e){n&&n.i&&(Je.delete(n),n.i(e))}function I(n,e,t,s){if(n&&n.o){if(Je.has(n))return;Je.add(n),ee.c.push(()=>{Je.delete(n),s&&(t&&n.d(1),s())}),n.o(e)}else s&&s()}function rn(n,e){I(n,1,1,()=>{e.delete(n.key)})}function ln(n,e,t,s,r,l,i,a,o,h,_,f){let c=n.length,E=l.length,u=c,m={};for(;u--;)m[n[u].key]=u;let d=[],R=new Map,T=new Map,S=[];for(u=E;u--;){let F=f(r,l,u),U=t(F),P=i.get(U);P?s&&S.push(()=>P.p(F,e)):(P=h(U,F),P.c()),R.set(U,d[u]=P),U in m&&T.set(U,Math.abs(u-m[U]))}let w=new Set,B=new Set;function X(F){z(F,1),F.m(a,_),i.set(F.key,F),_=F.first,E--}for(;c&&E;){let F=d[E-1],U=n[c-1],P=F.key,Z=U.key;F===U?(_=F.first,c--,E--):R.has(Z)?!i.has(P)||w.has(P)?X(F):B.has(Z)?c--:T.get(P)>T.get(Z)?(B.add(P),X(F)):(w.add(Z),c--):(o(U,i),c--)}for(;c--;){let F=n[c];R.has(F.key)||o(F,i)}for(;E;)X(d[E-1]);return V(S),d}var ms=["allowfullscreen","allowpaymentrequest","async","autofocus","autoplay","checked","controls","default","defer","disabled","formnovalidate","hidden","inert","ismap","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","selected"],Fi=new Set([...ms]);function an(n,e,t){let s=n.$$.props[e];s!==void 0&&(n.$$.bound[s]=t,t(n.$$.ctx[s]))}function Ze(n){n&&n.c()}function he(n,e,t,s){let{fragment:r,after_update:l}=n.$$;r&&r.m(e,t),s||rt(()=>{let i=n.$$.on_mount.map(lt).filter(Ye);n.$$.on_destroy?n.$$.on_destroy.push(...i):V(i),n.$$.on_mount=[]}),l.forEach(rt)}function oe(n,e){let t=n.$$;t.fragment!==null&&(hs(t.after_update),V(t.on_destroy),t.fragment&&t.fragment.d(e),t.on_destroy=t.fragment=null,t.ctx=[])}function ps(n,e){n.$$.dirty[0]===-1&&(ne.push(n),fs(),n.$$.dirty.fill(0)),n.$$.dirty[e/31|0]|=1<{let u=E.length?E[0]:c;return h.ctx&&r(h.ctx[f],h.ctx[f]=u)&&(!h.skip_bound&&h.bound[f]&&h.bound[f](u),_&&ps(n,f)),c}):[],h.update(),_=!0,V(h.before_update),h.fragment=s?s(h.ctx):!1,e.target){if(e.hydrate){as();let f=cs(e.target);h.fragment&&h.fragment.l(f),f.forEach(C)}else h.fragment&&h.fragment.c();e.intro&&z(n.$$.fragment),he(n,e.target,e.anchor,e.customElement),os(),sn()}_e(o)}var gs;typeof HTMLElement=="function"&&(gs=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){let{on_mount:n}=this.$$;this.$$.on_disconnect=n.map(lt).filter(Ye);for(let e in this.$$.slotted)this.appendChild(this.$$.slotted[e])}attributeChangedCallback(n,e,t){this[n]=t}disconnectedCallback(){V(this.$$.on_disconnect)}$destroy(){oe(this,1),this.$destroy=j}$on(n,e){if(!Ye(e))return j;let t=this.$$.callbacks[n]||(this.$$.callbacks[n]=[]);return t.push(e),()=>{let s=t.indexOf(e);s!==-1&&t.splice(s,1)}}$set(n){this.$$set&&!xt(n)&&(this.$$.skip_bound=!0,this.$$set(n),this.$$.skip_bound=!1)}});var q=class{$destroy(){oe(this,1),this.$destroy=j}$on(e,t){if(!Ye(t))return j;let s=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return s.push(t),()=>{let r=s.indexOf(t);r!==-1&&s.splice(r,1)}}$set(e){this.$$set&&!xt(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}};function D(n){let e=typeof n=="string"?n.charCodeAt(0):n;return e>=97&&e<=122||e>=65&&e<=90}function $(n){let e=typeof n=="string"?n.charCodeAt(0):n;return e>=48&&e<=57}function Y(n){return D(n)||$(n)}var on=["art-lojban","cel-gaulish","no-bok","no-nyn","zh-guoyu","zh-hakka","zh-min","zh-min-nan","zh-xiang"];var ut={"en-gb-oed":"en-GB-oxendict","i-ami":"ami","i-bnn":"bnn","i-default":null,"i-enochian":null,"i-hak":"hak","i-klingon":"tlh","i-lux":"lb","i-mingo":null,"i-navajo":"nv","i-pwn":"pwn","i-tao":"tao","i-tay":"tay","i-tsu":"tsu","sgn-be-fr":"sfb","sgn-be-nl":"vgt","sgn-ch-de":"sgg","art-lojban":"jbo","cel-gaulish":null,"no-bok":"nb","no-nyn":"nn","zh-guoyu":"cmn","zh-hakka":"hak","zh-min":null,"zh-min-nan":"nan","zh-xiang":"hsn"};var Es={}.hasOwnProperty;function Qe(n,e={}){let t=un(),s=String(n),r=s.toLowerCase(),l=0;if(n==null)throw new Error("Expected string, got `"+n+"`");if(Es.call(ut,r)){let a=ut[r];return(e.normalize===void 0||e.normalize===null||e.normalize)&&typeof a=="string"?Qe(a):(t[on.includes(r)?"regular":"irregular"]=s,t)}for(;D(r.charCodeAt(l))&&l<9;)l++;if(l>1&&l<9){if(t.language=s.slice(0,l),l<4){let a=0;for(;r.charCodeAt(l)===45&&D(r.charCodeAt(l+1))&&D(r.charCodeAt(l+2))&&D(r.charCodeAt(l+3))&&!D(r.charCodeAt(l+4));){if(a>2)return i(l,3,"Too many extended language subtags, expected at most 3 subtags");t.extendedLanguageSubtags.push(s.slice(l+1,l+4)),l+=4,a++}}for(r.charCodeAt(l)===45&&D(r.charCodeAt(l+1))&&D(r.charCodeAt(l+2))&&D(r.charCodeAt(l+3))&&D(r.charCodeAt(l+4))&&!D(r.charCodeAt(l+5))&&(t.script=s.slice(l+1,l+5),l+=5),r.charCodeAt(l)===45&&(D(r.charCodeAt(l+1))&&D(r.charCodeAt(l+2))&&!D(r.charCodeAt(l+3))?(t.region=s.slice(l+1,l+3),l+=3):$(r.charCodeAt(l+1))&&$(r.charCodeAt(l+2))&&$(r.charCodeAt(l+3))&&!$(r.charCodeAt(l+4))&&(t.region=s.slice(l+1,l+4),l+=4));r.charCodeAt(l)===45;){let a=l+1,o=a;for(;Y(r.charCodeAt(o));){if(o-a>7)return i(o,1,"Too long variant, expected at most 8 characters");o++}if(o-a>4||o-a>3&&$(r.charCodeAt(a)))t.variants.push(s.slice(a,o)),l=o;else break}for(;r.charCodeAt(l)===45&&!(r.charCodeAt(l+1)===120||!Y(r.charCodeAt(l+1))||r.charCodeAt(l+2)!==45||!Y(r.charCodeAt(l+3)));){let a=l+2,o=0;for(;r.charCodeAt(a)===45&&Y(r.charCodeAt(a+1))&&Y(r.charCodeAt(a+2));){let h=a+1;for(a=h+2,o++;Y(r.charCodeAt(a));){if(a-h>7)return i(a,2,"Too long extension, expected at most 8 characters");a++}}if(!o)return i(a,4,"Empty extension, extensions must have at least 2 characters of content");t.extensions.push({singleton:s.charAt(l+1),extensions:s.slice(l+3,a).split("-")}),l=a}}else l=0;if(l===0&&r.charCodeAt(l)===120||r.charCodeAt(l)===45&&r.charCodeAt(l+1)===120){l=l?l+2:1;let a=l;for(;r.charCodeAt(a)===45&&Y(r.charCodeAt(a+1));){let o=l+1;for(a=o;Y(r.charCodeAt(a));){if(a-o>7)return i(a,5,"Too long private-use area, expected at most 8 characters");a++}t.privateuse.push(s.slice(l+1,a)),l=a}}if(l!==s.length)return i(l,6,"Found superfluous content after tag");return t;function i(a,o,h){return e.warning&&e.warning(h,o,a),e.forgiving?t:un()}}function un(){return{language:null,extendedLanguageSubtags:[],script:null,region:null,variants:[],extensions:[],privateuse:[],irregular:null,regular:null}}function cn(n,e,t){let s=n.slice();return s[8]=e[t][0],s[9]=e[t][1],s}function bs(n){let e,t,s,r,l,i=n[0]&&_n(n);return{c(){i&&i.c(),e=M(),t=k("div"),s=k("p"),s.textContent=`${n[3](30)}`,r=M(),l=k("p"),l.textContent=`${n[3](40)}`,p(s,"class","pagefind-ui__result-title pagefind-ui__loading svelte-j9e30"),p(l,"class","pagefind-ui__result-excerpt pagefind-ui__loading svelte-j9e30"),p(t,"class","pagefind-ui__result-inner svelte-j9e30")},m(a,o){i&&i.m(a,o),y(a,e,o),y(a,t,o),b(t,s),b(t,r),b(t,l)},p(a,o){a[0]?i||(i=_n(a),i.c(),i.m(e.parentNode,e)):i&&(i.d(1),i=null)},d(a){i&&i.d(a),a&&C(e),a&&C(t)}}}function Rs(n){let e,t,s,r,l=n[1].meta?.title+"",i,a,o,h,_=n[1].excerpt+"",f,c=n[0]&&fn(n),E=n[2].length&&hn(n);return{c(){c&&c.c(),e=M(),t=k("div"),s=k("p"),r=k("a"),i=A(l),o=M(),h=k("p"),f=M(),E&&E.c(),p(r,"class","pagefind-ui__result-link svelte-j9e30"),p(r,"href",a=n[1].meta?.url||n[1].url),p(s,"class","pagefind-ui__result-title svelte-j9e30"),p(h,"class","pagefind-ui__result-excerpt svelte-j9e30"),p(t,"class","pagefind-ui__result-inner svelte-j9e30")},m(u,m){c&&c.m(u,m),y(u,e,m),y(u,t,m),b(t,s),b(s,r),b(r,i),b(t,o),b(t,h),h.innerHTML=_,b(t,f),E&&E.m(t,null)},p(u,m){u[0]?c?c.p(u,m):(c=fn(u),c.c(),c.m(e.parentNode,e)):c&&(c.d(1),c=null),m&2&&l!==(l=u[1].meta?.title+"")&&N(i,l),m&2&&a!==(a=u[1].meta?.url||u[1].url)&&p(r,"href",a),m&2&&_!==(_=u[1].excerpt+"")&&(h.innerHTML=_),u[2].length?E?E.p(u,m):(E=hn(u),E.c(),E.m(t,null)):E&&(E.d(1),E=null)},d(u){c&&c.d(u),u&&C(e),u&&C(t),E&&E.d()}}}function _n(n){let e;return{c(){e=k("div"),p(e,"class","pagefind-ui__result-thumb pagefind-ui__loading svelte-j9e30")},m(t,s){y(t,e,s)},d(t){t&&C(e)}}}function fn(n){let e,t=n[1].meta.image&&dn(n);return{c(){e=k("div"),t&&t.c(),p(e,"class","pagefind-ui__result-thumb svelte-j9e30")},m(s,r){y(s,e,r),t&&t.m(e,null)},p(s,r){s[1].meta.image?t?t.p(s,r):(t=dn(s),t.c(),t.m(e,null)):t&&(t.d(1),t=null)},d(s){s&&C(e),t&&t.d()}}}function dn(n){let e,t,s;return{c(){e=k("img"),p(e,"class","pagefind-ui__result-image svelte-j9e30"),le(e.src,t=n[1].meta?.image)||p(e,"src",t),p(e,"alt",s=n[1].meta?.image_alt||n[1].meta?.title)},m(r,l){y(r,e,l)},p(r,l){l&2&&!le(e.src,t=r[1].meta?.image)&&p(e,"src",t),l&2&&s!==(s=r[1].meta?.image_alt||r[1].meta?.title)&&p(e,"alt",s)},d(r){r&&C(e)}}}function hn(n){let e,t=n[2],s=[];for(let r=0;rn.toLocaleUpperCase();function ks(n,e,t){let{show_images:s=!0}=e,{process_result:r=null}=e,{result:l={data:async()=>{}}}=e,i=["title","image","image_alt","url"],a,o=[],h=async f=>{t(1,a=await f.data()),t(1,a=r?.(a)??a),t(2,o=Object.entries(a.meta).filter(([c])=>!i.includes(c)))},_=(f=30)=>". ".repeat(Math.floor(10+Math.random()*f));return n.$$set=f=>{"show_images"in f&&t(0,s=f.show_images),"process_result"in f&&t(4,r=f.process_result),"result"in f&&t(5,l=f.result)},n.$$.update=()=>{if(n.$$.dirty&32)e:h(l)},[s,a,o,_,r,l]}var ct=class extends q{constructor(e){super(),J(this,e,ks,Ts,G,{show_images:0,process_result:4,result:5})}},gn=ct;function En(n,e,t){let s=n.slice();return s[11]=e[t][0],s[12]=e[t][1],s}function bn(n,e,t){let s=n.slice();return s[15]=e[t],s}function Cs(n){let e,t,s,r,l,i=n[0]&&Rn(n);return{c(){i&&i.c(),e=M(),t=k("div"),s=k("p"),s.textContent=`${n[5](30)}`,r=M(),l=k("p"),l.textContent=`${n[5](40)}`,p(s,"class","pagefind-ui__result-title pagefind-ui__loading svelte-4xnkmf"),p(l,"class","pagefind-ui__result-excerpt pagefind-ui__loading svelte-4xnkmf"),p(t,"class","pagefind-ui__result-inner svelte-4xnkmf")},m(a,o){i&&i.m(a,o),y(a,e,o),y(a,t,o),b(t,s),b(t,r),b(t,l)},p(a,o){a[0]?i||(i=Rn(a),i.c(),i.m(e.parentNode,e)):i&&(i.d(1),i=null)},d(a){i&&i.d(a),a&&C(e),a&&C(t)}}}function ys(n){let e,t,s,r,l=n[1].meta?.title+"",i,a,o,h,_,f=n[0]&&Tn(n),c=n[4]&&Cn(n),E=n[3],u=[];for(let d=0;dn.toLocaleUpperCase();function vs(n,e,t){let{show_images:s=!0}=e,{process_result:r=null}=e,{result:l={data:async()=>{}}}=e,i=["title","image","image_alt","url"],a,o=[],h=[],_=!1,f=(u,m)=>{if(u.length<=m)return u;let d=[...u].sort((R,T)=>T.locations.length-R.locations.length).slice(0,3).map(R=>R.url);return u.filter(R=>d.includes(R.url))},c=async u=>{t(1,a=await u.data()),t(1,a=r?.(a)??a),t(2,o=Object.entries(a.meta).filter(([m])=>!i.includes(m))),Array.isArray(a.sub_results)&&(t(4,_=a.sub_results?.[0]?.url===(a.meta?.url||a.url)),_?t(3,h=f(a.sub_results.slice(1),3)):t(3,h=f([...a.sub_results],3)))},E=(u=30)=>". ".repeat(Math.floor(10+Math.random()*u));return n.$$set=u=>{"show_images"in u&&t(0,s=u.show_images),"process_result"in u&&t(6,r=u.process_result),"result"in u&&t(7,l=u.result)},n.$$.update=()=>{if(n.$$.dirty&128)e:c(l)},[s,a,o,h,_,E,r,l]}var _t=class extends q{constructor(e){super(),J(this,e,vs,Ss,G,{show_images:0,process_result:6,result:7})}},An=_t;function wn(n,e,t){let s=n.slice();return s[9]=e[t][0],s[10]=e[t][1],s[11]=e,s[12]=t,s}function Fn(n,e,t){let s=n.slice();return s[13]=e[t][0],s[14]=e[t][1],s[15]=e,s[16]=t,s}function Hn(n){let e,t,s=n[3]("filters_label",n[4],n[5])+"",r,l,i=Object.entries(n[1]),a=[];for(let o=0;on.toLocaleUpperCase();function As(n,e,t){let{available_filters:s=null}=e,{show_empty_filters:r=!0}=e,{translate:l=()=>""}=e,{automatic_translations:i={}}=e,{translations:a={}}=e,o={},h=!1,_=!1;function f(c,E){o[`${c}:${E}`]=this.checked,t(0,o)}return n.$$set=c=>{"available_filters"in c&&t(1,s=c.available_filters),"show_empty_filters"in c&&t(2,r=c.show_empty_filters),"translate"in c&&t(3,l=c.translate),"automatic_translations"in c&&t(4,i=c.automatic_translations),"translations"in c&&t(5,a=c.translations)},n.$$.update=()=>{if(n.$$.dirty&130){e:if(s&&!h){t(7,h=!0);let c=Object.entries(s||{});c.length===1&&Object.entries(c[0][1])?.length<=6&&t(6,_=!0)}}},[o,s,r,l,i,a,_,h,f]}var ft=class extends q{constructor(e){super(),J(this,e,As,Ms,G,{available_filters:1,show_empty_filters:2,translate:3,automatic_translations:4,translations:5,selected_filters:0})}get selected_filters(){return this.$$.ctx[0]}},Dn=ft;var dt={};v(dt,{comments:()=>Fs,default:()=>Os,direction:()=>Hs,strings:()=>Ns,thanks_to:()=>ws});var ws="Jan Claasen ",Fs="",Hs="ltr",Ns={placeholder:"Soek",clear_search:"Opruim",load_more:"Laai nog resultate",search_label:"Soek hierdie webwerf",filters_label:"Filters",zero_results:"Geen resultate vir [SEARCH_TERM]",many_results:"[COUNT] resultate vir [SEARCH_TERM]",one_result:"[COUNT] resultate vir [SEARCH_TERM]",alt_search:"Geen resultate vir [SEARCH_TERM]. Toon resultate vir [DIFFERENT_TERM] in plaas daarvan",search_suggestion:"Geen resultate vir [SEARCH_TERM]. Probeer eerder een van die volgende terme:",searching:"Soek vir [SEARCH_TERM]"},Os={thanks_to:ws,comments:Fs,direction:Hs,strings:Ns};var ht={};v(ht,{comments:()=>zs,default:()=>Is,direction:()=>Ds,strings:()=>Us,thanks_to:()=>js});var js="Maruf Alom ",zs="",Ds="ltr",Us={placeholder:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",clear_search:"\u09AE\u09C1\u099B\u09C7 \u09AB\u09C7\u09B2\u09C1\u09A8",load_more:"\u0986\u09B0\u09CB \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A6\u09C7\u0996\u09C1\u09A8",search_label:"\u098F\u0987 \u0993\u09DF\u09C7\u09AC\u09B8\u09BE\u0987\u099F\u09C7 \u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",filters_label:"\u09AB\u09BF\u09B2\u09CD\u099F\u09BE\u09B0",zero_results:"[SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09DF\u09BE \u09AF\u09BE\u09DF\u09A8\u09BF",many_results:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09DF\u09BE \u0997\u09BF\u09DF\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",one_result:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09DF\u09BE \u0997\u09BF\u09DF\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",alt_search:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09DF\u09BE \u09AF\u09BE\u09DF\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF. \u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09C7 [DIFFERENT_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u09A6\u09C7\u0996\u09BE\u09A8\u09CB \u09B9\u099A\u09CD\u099B\u09C7",search_suggestion:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09DF\u09BE \u09AF\u09BE\u09DF\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u09AC\u09BF\u09B7\u09DF\u09C7. \u09A8\u09BF\u09A8\u09CD\u09AE\u09C7\u09B0 \u09AC\u09BF\u09B7\u09DF\u09AC\u09B8\u09CD\u09A4\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09A6\u09C7\u0996\u09C1\u09A8:",searching:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u099A\u09B2\u099B\u09C7 [SEARCH_TERM]..."},Is={thanks_to:js,comments:zs,direction:Ds,strings:Us};var mt={};v(mt,{comments:()=>Ls,default:()=>Ws,direction:()=>qs,strings:()=>Bs,thanks_to:()=>Ps});var Ps="Pablo Villaverde ",Ls="",qs="ltr",Bs={placeholder:"Cerca",clear_search:"Netejar",load_more:"Veure m\xE9es resultats",search_label:"Cerca en aquest lloc",filters_label:"Filtres",zero_results:"No es van trobar resultats per [SEARCH_TERM]",many_results:"[COUNT] resultats trobats per [SEARCH_TERM]",one_result:"[COUNT] resultat trobat per [SEARCH_TERM]",alt_search:"No es van trobar resultats per [SEARCH_TERM]. Mostrant al seu lloc resultats per [DIFFERENT_TERM]",search_suggestion:"No es van trobar resultats per [SEARCH_TERM]. Proveu una de les cerques seg\xFCents:",searching:"Cercant [SEARCH_TERM]..."},Ws={thanks_to:Ps,comments:Ls,direction:qs,strings:Bs};var pt={};v(pt,{comments:()=>Gs,default:()=>Ys,direction:()=>Ks,strings:()=>Js,thanks_to:()=>Vs});var Vs="Jonas Smedegaard ",Gs="",Ks="ltr",Js={placeholder:"S\xF8g",clear_search:"Nulstil",load_more:"Indl\xE6s flere resultater",search_label:"S\xF8g p\xE5 dette website",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v et af disse s\xF8geord i stedet:",searching:"S\xF8ger efter [SEARCH_TERM]..."},Ys={thanks_to:Vs,comments:Gs,direction:Ks,strings:Js};var gt={};v(gt,{comments:()=>Zs,default:()=>$s,direction:()=>Qs,strings:()=>xs,thanks_to:()=>Xs});var Xs="Jan Claasen ",Zs="",Qs="ltr",xs={placeholder:"Suche",clear_search:"L\xF6schen",load_more:"Mehr Ergebnisse laden",search_label:"Suche diese Seite",filters_label:"Filter",zero_results:"Keine Ergebnisse f\xFCr [SEARCH_TERM]",many_results:"[COUNT] Ergebnisse f\xFCr [SEARCH_TERM]",one_result:"[COUNT] Ergebnis f\xFCr [SEARCH_TERM]",alt_search:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Stattdessen werden Ergebnisse f\xFCr [DIFFERENT_TERM] angezeigt",search_suggestion:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Versuchen Sie eine der folgenden Suchen:",searching:"Suche f\xFCr [SEARCH_TERM]"},$s={thanks_to:Xs,comments:Zs,direction:Qs,strings:xs};var Et={};v(Et,{comments:()=>tr,default:()=>rr,direction:()=>nr,strings:()=>sr,thanks_to:()=>er});var er="Liam Bigelow ",tr="",nr="ltr",sr={placeholder:"Search",clear_search:"Clear",load_more:"Load more results",search_label:"Search this site",filters_label:"Filters",zero_results:"No results for [SEARCH_TERM]",many_results:"[COUNT] results for [SEARCH_TERM]",one_result:"[COUNT] result for [SEARCH_TERM]",alt_search:"No results for [SEARCH_TERM]. Showing results for [DIFFERENT_TERM] instead",search_suggestion:"No results for [SEARCH_TERM]. Try one of the following searches:",searching:"Searching for [SEARCH_TERM]..."},rr={thanks_to:er,comments:tr,direction:nr,strings:sr};var bt={};v(bt,{comments:()=>ir,default:()=>ur,direction:()=>ar,strings:()=>or,thanks_to:()=>lr});var lr="Pablo Villaverde ",ir="",ar="ltr",or={placeholder:"Buscar",clear_search:"Limpiar",load_more:"Ver m\xE1s resultados",search_label:"Buscar en este sitio",filters_label:"Filtros",zero_results:"No se encontraron resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",alt_search:"No se encontraron resultados para [SEARCH_TERM]. Mostrando en su lugar resultados para [DIFFERENT_TERM]",search_suggestion:"No se encontraron resultados para [SEARCH_TERM]. Prueba una de las siguientes b\xFAsquedas:",searching:"Buscando [SEARCH_TERM]..."},ur={thanks_to:lr,comments:ir,direction:ar,strings:or};var Rt={};v(Rt,{comments:()=>_r,default:()=>hr,direction:()=>fr,strings:()=>dr,thanks_to:()=>cr});var cr="Valtteri Laitinen ",_r="",fr="ltr",dr={placeholder:"Haku",clear_search:"Tyhjenn\xE4",load_more:"Lataa lis\xE4\xE4 tuloksia",search_label:"Hae t\xE4lt\xE4 sivustolta",filters_label:"Suodattimet",zero_results:"Ei tuloksia haulle [SEARCH_TERM]",many_results:"[COUNT] tulosta haulle [SEARCH_TERM]",one_result:"[COUNT] tulos haulle [SEARCH_TERM]",alt_search:"Ei tuloksia haulle [SEARCH_TERM]. N\xE4ytet\xE4\xE4n tulokset sen sijaan haulle [DIFFERENT_TERM]",search_suggestion:"Ei tuloksia haulle [SEARCH_TERM]. Kokeile jotain seuraavista:",searching:"Haetaan [SEARCH_TERM]..."},hr={thanks_to:cr,comments:_r,direction:fr,strings:dr};var Tt={};v(Tt,{comments:()=>pr,default:()=>br,direction:()=>gr,strings:()=>Er,thanks_to:()=>mr});var mr="Nicolas Friedli ",pr="",gr="ltr",Er={placeholder:"Rechercher",clear_search:"Nettoyer",load_more:"Charger plus de r\xE9sultats",search_label:"Recherche sur ce site",filters_label:"Filtres",zero_results:"Pas de r\xE9sultat pour [SEARCH_TERM]",many_results:"[COUNT] r\xE9sultats pour [SEARCH_TERM]",one_result:"[COUNT] r\xE9sultat pour [SEARCH_TERM]",alt_search:"Pas de r\xE9sultat pour [SEARCH_TERM]. Montre les r\xE9sultats pour [DIFFERENT_TERM] \xE0 la place",search_suggestion:"Pas de r\xE9sultat pour [SEARCH_TERM]. Essayer une des recherches suivantes:",searching:"Recherche [SEARCH_TERM]..."},br={thanks_to:mr,comments:pr,direction:gr,strings:Er};var kt={};v(kt,{comments:()=>Tr,default:()=>yr,direction:()=>kr,strings:()=>Cr,thanks_to:()=>Rr});var Rr="Pablo Villaverde ",Tr="",kr="ltr",Cr={placeholder:"Buscar",clear_search:"Limpar",load_more:"Ver m\xE1is resultados",search_label:"Buscar neste sitio",filters_label:"Filtros",zero_results:"Non se atoparon resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados atopados para [SEARCH_TERM]",one_result:"[COUNT] resultado atopado para [SEARCH_TERM]",alt_search:"Non se atoparon resultados para [SEARCH_TERM]. Amosando no seu lugar resultados para [DIFFERENT_TERM]",search_suggestion:"Non se atoparon resultados para [SEARCH_TERM]. Probe unha das seguintes pesquisas:",searching:"Buscando [SEARCH_TERM]..."},yr={thanks_to:Rr,comments:Tr,direction:kr,strings:Cr};var Ct={};v(Ct,{comments:()=>vr,default:()=>wr,direction:()=>Mr,strings:()=>Ar,thanks_to:()=>Sr});var Sr="Amit Yadav ",vr="",Mr="ltr",Ar={placeholder:"\u0916\u094B\u091C\u0947\u0902",clear_search:"\u0938\u093E\u092B \u0915\u0930\u0947\u0902",load_more:"\u0914\u0930 \u0905\u0927\u093F\u0915 \u092A\u0930\u093F\u0923\u093E\u092E \u0932\u094B\u0921 \u0915\u0930\u0947\u0902",search_label:"\u0907\u0938 \u0938\u093E\u0907\u091F \u092E\u0947\u0902 \u0916\u094B\u091C\u0947\u0902",filters_label:"\u092B\u093C\u093F\u0932\u094D\u091F\u0930",zero_results:"\u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E",many_results:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u0947",one_result:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u093E",alt_search:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0907\u0938\u0915\u0947 \u092C\u091C\u093E\u092F [DIFFERENT_TERM] \u0915\u0947 \u0932\u093F\u090F \u092A\u0930\u093F\u0923\u093E\u092E \u0926\u093F\u0916\u093E \u0930\u0939\u093E \u0939\u0948",search_suggestion:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0928\u093F\u092E\u094D\u0928\u0932\u093F\u0916\u093F\u0924 \u0916\u094B\u091C\u094B\u0902 \u092E\u0947\u0902 \u0938\u0947 \u0915\u094B\u0908 \u090F\u0915 \u0906\u091C\u093C\u092E\u093E\u090F\u0902:",searching:"[SEARCH_TERM] \u0915\u0940 \u0916\u094B\u091C \u0915\u0940 \u091C\u093E \u0930\u0939\u0940 \u0939\u0948..."},wr={thanks_to:Sr,comments:vr,direction:Mr,strings:Ar};var yt={};v(yt,{comments:()=>Hr,default:()=>jr,direction:()=>Nr,strings:()=>Or,thanks_to:()=>Fr});var Fr="Diomed ",Hr="",Nr="ltr",Or={placeholder:"Tra\u017Ei",clear_search:"O\u010Disti",load_more:"U\u010Ditaj vi\u0161e rezultata",search_label:"Pretra\u017Ei ovu stranicu",filters_label:"Filteri",zero_results:"Nema rezultata za [SEARCH_TERM]",many_results:"[COUNT] rezultata za [SEARCH_TERM]",one_result:"[COUNT] rezultat za [SEARCH_TERM]",alt_search:"Nema rezultata za [SEARCH_TERM]. Prikazujem rezultate za [DIFFERENT_TERM]",search_suggestion:"Nema rezultata za [SEARCH_TERM]. Poku\u0161aj s jednom od ovih pretraga:",searching:"Pretra\u017Eujem [SEARCH_TERM]..."},jr={thanks_to:Fr,comments:Hr,direction:Nr,strings:Or};var St={};v(St,{comments:()=>Dr,default:()=>Pr,direction:()=>Ur,strings:()=>Ir,thanks_to:()=>zr});var zr="Adam Laki ",Dr="",Ur="ltr",Ir={placeholder:"Keres\xE9s",clear_search:"T\xF6rl\xE9s",load_more:"Tov\xE1bbi tal\xE1latok bet\xF6lt\xE9se",search_label:"Keres\xE9s az oldalon",filters_label:"Sz\u0171r\xE9s",zero_results:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",many_results:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",one_result:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",alt_search:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Tal\xE1latok mutat\xE1sa ink\xE1bb a(z) [DIFFERENT_TERM] kifejez\xE9sre",search_suggestion:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Pr\xF3b\xE1ld meg a k\xF6vetkez\u0151 keres\xE9sek egyik\xE9t:",searching:"Keres\xE9s a(z) [SEARCH_TERM] kifejez\xE9sre..."},Pr={thanks_to:zr,comments:Dr,direction:Ur,strings:Ir};var vt={};v(vt,{comments:()=>qr,default:()=>Vr,direction:()=>Br,strings:()=>Wr,thanks_to:()=>Lr});var Lr="Nixentric",qr="",Br="ltr",Wr={placeholder:"Cari",clear_search:"Bersihkan",load_more:"Muat lebih banyak hasil",search_label:"Telusuri situs ini",filters_label:"Filter",zero_results:"[SEARCH_TERM] tidak ditemukan",many_results:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",one_result:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",alt_search:"[SEARCH_TERM] tidak ditemukan. Menampilkan hasil [DIFFERENT_TERM] sebagai gantinya",search_suggestion:"[SEARCH_TERM] tidak ditemukan. Coba salah satu pencarian berikut ini:",searching:"Mencari [SEARCH_TERM]..."},Vr={thanks_to:Lr,comments:qr,direction:Br,strings:Wr};var Mt={};v(Mt,{comments:()=>Kr,default:()=>Xr,direction:()=>Jr,strings:()=>Yr,thanks_to:()=>Gr});var Gr="Cosette Bruhns Alonso, Andrew Janco ",Kr="",Jr="ltr",Yr={placeholder:"Cerca",clear_search:"Cancella la cronologia",load_more:"Mostra pi\xF9 risultati",search_label:"Cerca nel sito",filters_label:"Filtri di ricerca",zero_results:"Nessun risultato per [SEARCH_TERM]",many_results:"[COUNT] risultati per [SEARCH_TERM]",one_result:"[COUNT] risultato per [SEARCH_TERM]",alt_search:"Nessun risultato per [SEARCH_TERM]. Mostrando risultati per [DIFFERENT_TERM] come alternativa.",search_suggestion:"Nessun risultato per [SEARCH_TERM]. Prova una delle seguenti ricerche:",searching:"Cercando [SEARCH_TERM]..."},Xr={thanks_to:Gr,comments:Kr,direction:Jr,strings:Yr};var At={};v(At,{comments:()=>Qr,default:()=>el,direction:()=>xr,strings:()=>$r,thanks_to:()=>Zr});var Zr="Tate",Qr="",xr="ltr",$r={placeholder:"\u691C\u7D22",clear_search:"\u6D88\u3059",load_more:"\u3082\u3063\u3068\u8AAD\u307F\u8FBC\u3080",search_label:"\u3053\u306E\u30B5\u30A4\u30C8\u3092\u691C\u7D22",filters_label:"\u30D5\u30A3\u30EB\u30BF",zero_results:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u4EF6\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F",many_results:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",one_result:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",alt_search:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u4EF6\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002[DIFFERENT_TERM]\u306E\u691C\u7D22\u7D50\u679C\u3092\u8868\u793A\u3057\u3066\u3044\u307E\u3059",search_suggestion:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u4EF6\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u6B21\u306E\u3044\u305A\u308C\u304B\u306E\u691C\u7D22\u3092\u8A66\u3057\u3066\u304F\u3060\u3055\u3044",searching:"[SEARCH_TERM]\u3092\u691C\u7D22\u3057\u3066\u3044\u307E\u3059"},el={thanks_to:Zr,comments:Qr,direction:xr,strings:$r};var wt={};v(wt,{comments:()=>nl,default:()=>ll,direction:()=>sl,strings:()=>rl,thanks_to:()=>tl});var tl="",nl="",sl="ltr",rl={placeholder:"Rapu",clear_search:"Whakakore",load_more:"Whakauta \u0113tahi otinga k\u0113",search_label:"Rapu",filters_label:"T\u0101tari",zero_results:"Otinga kore ki [SEARCH_TERM]",many_results:"[COUNT] otinga ki [SEARCH_TERM]",one_result:"[COUNT] otinga ki [SEARCH_TERM]",alt_search:"Otinga kore ki [SEARCH_TERM]. Otinga k\u0113 ki [DIFFERENT_TERM]",search_suggestion:"Otinga kore ki [SEARCH_TERM]. whakam\u0101tau ki ng\u0101 mea atu:",searching:"Rapu ki [SEARCH_TERM]..."},ll={thanks_to:tl,comments:nl,direction:sl,strings:rl};var Ft={};v(Ft,{comments:()=>al,default:()=>cl,direction:()=>ol,strings:()=>ul,thanks_to:()=>il});var il="Paul van Brouwershaven",al="",ol="ltr",ul={placeholder:"Zoeken",clear_search:"Reset",load_more:"Meer resultaten laden",search_label:"Doorzoek deze site",filters_label:"Filters",zero_results:"Geen resultaten voor [SEARCH_TERM]",many_results:"[COUNT] resultaten voor [SEARCH_TERM]",one_result:"[COUNT] resultaat voor [SEARCH_TERM]",alt_search:"Geen resultaten voor [SEARCH_TERM]. In plaats daarvan worden resultaten voor [DIFFERENT_TERM] weergegeven",search_suggestion:"Geen resultaten voor [SEARCH_TERM]. Probeer een van de volgende zoekopdrachten:",searching:"Zoeken naar [SEARCH_TERM]..."},cl={thanks_to:il,comments:al,direction:ol,strings:ul};var Ht={};v(Ht,{comments:()=>fl,default:()=>ml,direction:()=>dl,strings:()=>hl,thanks_to:()=>_l});var _l="Christopher Wingate",fl="",dl="ltr",hl={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last flere resultater",search_label:"S\xF8k p\xE5 denne siden",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v en av disse s\xF8keordene i stedet:",searching:"S\xF8ker etter [SEARCH_TERM]"},ml={thanks_to:_l,comments:fl,direction:dl,strings:hl};var Nt={};v(Nt,{comments:()=>gl,default:()=>Rl,direction:()=>El,strings:()=>bl,thanks_to:()=>pl});var pl="",gl="",El="ltr",bl={placeholder:"Szukaj",clear_search:"Wyczy\u015B\u0107",load_more:"Za\u0142aduj wi\u0119cej",search_label:"Przeszukaj t\u0119 stron\u0119",filters_label:"Filtry",zero_results:"Brak wynik\xF3w dla [SEARCH_TERM]",many_results:"[COUNT] wynik\xF3w dla [SEARCH_TERM]",one_result:"[COUNT] wynik dla [SEARCH_TERM]",alt_search:"Brak wynik\xF3w dla [SEARCH_TERM]. Wy\u015Bwietlam wyniki dla [DIFFERENT_TERM]",search_suggestion:"Brak wynik\xF3w dla [SEARCH_TERM]. Pokrewne wyniki wyszukiwania:",searching:"Szukam [SEARCH_TERM]..."},Rl={thanks_to:pl,comments:gl,direction:El,strings:bl};var Ot={};v(Ot,{comments:()=>kl,default:()=>Sl,direction:()=>Cl,strings:()=>yl,thanks_to:()=>Tl});var Tl="Jonatah",kl="",Cl="ltr",yl={placeholder:"Pesquisar",clear_search:"Limpar",load_more:"Ver mais resultados",search_label:"Pesquisar",filters_label:"Filtros",zero_results:"Nenhum resultado encontrado para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",alt_search:"Nenhum resultado encontrado para [SEARCH_TERM]. Exibindo resultados para [DIFFERENT_TERM]",search_suggestion:"Nenhum resultado encontrado para [SEARCH_TERM]. Tente uma das seguintes pesquisas:",searching:"Pesquisando por [SEARCH_TERM]..."},Sl={thanks_to:Tl,comments:kl,direction:Cl,strings:yl};var jt={};v(jt,{comments:()=>Ml,default:()=>Fl,direction:()=>Al,strings:()=>wl,thanks_to:()=>vl});var vl="Aleksandr Gordeev",Ml="",Al="ltr",wl={placeholder:"\u041F\u043E\u0438\u0441\u043A",clear_search:"\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u043F\u043E\u043B\u0435",load_more:"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0435",search_label:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0441\u0430\u0439\u0442\u0443",filters_label:"\u0424\u0438\u043B\u044C\u0442\u0440\u044B",zero_results:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",alt_search:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043A\u0430\u0437\u0430\u043D\u044B \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [DIFFERENT_TERM]",search_suggestion:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043E\u0434\u0438\u043D \u0438\u0437 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432",searching:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]"},Fl={thanks_to:vl,comments:Ml,direction:Al,strings:wl};var zt={};v(zt,{comments:()=>Nl,default:()=>zl,direction:()=>Ol,strings:()=>jl,thanks_to:()=>Hl});var Hl="Andrija Sagicc",Nl="",Ol="ltr",jl={placeholder:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430",clear_search:"\u0411\u0440\u0438\u0441\u0430\u045A\u0435",load_more:"\u041F\u0440\u0438\u043A\u0430\u0437 \u0432\u0438\u0448\u0435 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",search_label:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0441\u0430\u0458\u0442\u0430",filters_label:"\u0424\u0438\u043B\u0442\u0435\u0440\u0438",zero_results:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",alt_search:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u0440\u0438\u043A\u0430\u0437 \u0434\u043E\u0434\u0430\u0442\u043D\u0438\u043A \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [DIFFERENT_TERM]",search_suggestion:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u043E\u043A\u0443\u0448\u0430\u0458\u0442\u0435 \u0441\u0430 \u043D\u0435\u043A\u043E\u043C \u043E\u0434 \u0441\u043B\u0435\u0434\u0435\u045B\u0438\u0445 \u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0430:",searching:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0442\u0435\u0440\u043C\u0438\u043D\u0430 [SEARCH_TERM]..."},zl={thanks_to:Hl,comments:Nl,direction:Ol,strings:jl};var Dt={};v(Dt,{comments:()=>Ul,default:()=>Ll,direction:()=>Il,strings:()=>Pl,thanks_to:()=>Dl});var Dl="Montazar Al-Jaber ",Ul="",Il="ltr",Pl={placeholder:"S\xF6k",clear_search:"Rensa",load_more:"Visa fler tr\xE4ffar",search_label:"S\xF6k p\xE5 denna sida",filters_label:"Filter",zero_results:"[SEARCH_TERM] gav inga tr\xE4ffar",many_results:"[SEARCH_TERM] gav [COUNT] tr\xE4ffar",one_result:"[SEARCH_TERM] gav [COUNT] tr\xE4ff",alt_search:"[SEARCH_TERM] gav inga tr\xE4ffar. Visar resultat f\xF6r [DIFFERENT_TERM] ist\xE4llet",search_suggestion:"[SEARCH_TERM] gav inga tr\xE4ffar. F\xF6rs\xF6k igen med en av f\xF6ljande s\xF6kord:",searching:"S\xF6ker efter [SEARCH_TERM]..."},Ll={thanks_to:Dl,comments:Ul,direction:Il,strings:Pl};var Ut={};v(Ut,{comments:()=>Bl,default:()=>Gl,direction:()=>Wl,strings:()=>Vl,thanks_to:()=>ql});var ql="",Bl="",Wl="ltr",Vl={placeholder:"\u0BA4\u0BC7\u0B9F\u0BC1\u0B95",clear_search:"\u0B85\u0BB4\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0B95",load_more:"\u0BAE\u0BC7\u0BB2\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BC8\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1\u0B95",search_label:"\u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BB3\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BCD \u0BA4\u0BC7\u0B9F\u0BC1\u0B95",filters_label:"\u0BB5\u0B9F\u0BBF\u0B95\u0B9F\u0BCD\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BCD",zero_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8",many_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 [COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",one_result:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1",alt_search:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD\u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8, \u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0B92\u0BA4\u0BCD\u0BA4 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD [DIFFERENT_TERM]",search_suggestion:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD \u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8.\u0B87\u0BA4\u0BB1\u0BCD\u0B95\u0BC1 \u0BAA\u0BA4\u0BBF\u0BB2\u0BC0\u0B9F\u0BBE\u0BA9 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC8 \u0BA4\u0BC7\u0B9F\u0BC1\u0B95:",searching:"[SEARCH_TERM] \u0BA4\u0BC7\u0B9F\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0B95\u0BBF\u0BA9\u0BCD\u0BB1\u0BA4\u0BC1"},Gl={thanks_to:ql,comments:Bl,direction:Wl,strings:Vl};var It={};v(It,{comments:()=>Jl,default:()=>Zl,direction:()=>Yl,strings:()=>Xl,thanks_to:()=>Kl});var Kl="Taylan \xD6zg\xFCr Bildik",Jl="",Yl="ltr",Xl={placeholder:"Ara\u015Ft\u0131r",clear_search:"Temizle",load_more:"Daha fazla sonu\xE7",search_label:"Site genelinde arama",filters_label:"Filtreler",zero_results:"[SEARCH_TERM] i\xE7in sonu\xE7 yok",many_results:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",one_result:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",alt_search:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Bunun yerine [DIFFERENT_TERM] i\xE7in sonu\xE7lar g\xF6steriliyor",search_suggestion:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Alternatif olarak a\u015Fa\u011F\u0131daki kelimelerden birini deneyebilirsiniz:",searching:"[SEARCH_TERM] ara\u015Ft\u0131r\u0131l\u0131yor..."},Zl={thanks_to:Kl,comments:Jl,direction:Yl,strings:Xl};var Pt={};v(Pt,{comments:()=>xl,default:()=>ti,direction:()=>$l,strings:()=>ei,thanks_to:()=>Ql});var Ql="Long Nhat Nguyen",xl="",$l="ltr",ei={placeholder:"T\xECm ki\u1EBFm",clear_search:"X\xF3a",load_more:"Nhi\u1EC1u k\u1EBFt qu\u1EA3 h\u01A1n",search_label:"T\xECm ki\u1EBFm trong trang n\xE0y",filters_label:"B\u1ED9 l\u1ECDc",zero_results:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",many_results:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",one_result:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",alt_search:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Ki\u1EC3m th\u1ECB k\u1EBFt qu\u1EA3 thay th\u1EBF v\u1EDBi [DIFFERENT_TERM]",search_suggestion:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Th\u1EED m\u1ED9t trong c\xE1c t\xECm ki\u1EBFm:",searching:"\u0110ang t\xECm ki\u1EBFm cho [SEARCH_TERM]..."},ti={thanks_to:Ql,comments:xl,direction:$l,strings:ei};var Lt={};v(Lt,{comments:()=>si,default:()=>ii,direction:()=>ri,strings:()=>li,thanks_to:()=>ni});var ni="Amber Song",si="",ri="ltr",li={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]..."},ii={thanks_to:ni,comments:si,direction:ri,strings:li};var qt={};v(qt,{comments:()=>oi,default:()=>_i,direction:()=>ui,strings:()=>ci,thanks_to:()=>ai});var ai="Amber Song",oi="",ui="ltr",ci={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F09\u66F4\u591A\u7D50\u679C",search_label:"\u7AD9\u5167\u641C\u7D22",filters_label:"\u7BE9\u9078",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",many_results:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",one_result:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u6539\u70BA\u986F\u793A [DIFFERENT_TERM] \u7684\u76F8\u95DC\u7D50\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u8ACB\u5617\u8A66\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]..."},_i={thanks_to:ai,comments:oi,direction:ui,strings:ci};var Bt={};v(Bt,{comments:()=>di,default:()=>pi,direction:()=>hi,strings:()=>mi,thanks_to:()=>fi});var fi="Amber Song",di="",hi="ltr",mi={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]..."},pi={thanks_to:fi,comments:di,direction:hi,strings:mi};var gi=[dt,ht,mt,pt,gt,Et,bt,Rt,Tt,kt,Ct,yt,St,vt,Mt,At,wt,Ft,Ht,Nt,Ot,jt,zt,Dt,Ut,It,Pt,Lt,qt,Bt],Un=gi,In=["../../translations/af.json","../../translations/bn.json","../../translations/ca.json","../../translations/da.json","../../translations/de.json","../../translations/en.json","../../translations/es.json","../../translations/fi.json","../../translations/fr.json","../../translations/gl.json","../../translations/hi.json","../../translations/hr.json","../../translations/hu.json","../../translations/id.json","../../translations/it.json","../../translations/ja.json","../../translations/mi.json","../../translations/nl.json","../../translations/no.json","../../translations/pl.json","../../translations/pt.json","../../translations/ru.json","../../translations/sr.json","../../translations/sv.json","../../translations/ta.json","../../translations/tr.json","../../translations/vi.json","../../translations/zh-cn.json","../../translations/zh-tw.json","../../translations/zh.json"];function Pn(n,e,t){let s=n.slice();return s[48]=e[t],s}function Ln(n){let e,t,s;function r(i){n[34](i)}let l={show_empty_filters:n[4],available_filters:n[16],translate:n[18],automatic_translations:n[17],translations:n[5]};return n[7]!==void 0&&(l.selected_filters=n[7]),e=new Dn({props:l}),re.push(()=>an(e,"selected_filters",r)),{c(){Ze(e.$$.fragment)},m(i,a){he(e,i,a),s=!0},p(i,a){let o={};a[0]&16&&(o.show_empty_filters=i[4]),a[0]&65536&&(o.available_filters=i[16]),a[0]&131072&&(o.automatic_translations=i[17]),a[0]&32&&(o.translations=i[5]),!t&&a[0]&128&&(t=!0,o.selected_filters=i[7],nn(()=>t=!1)),e.$set(o)},i(i){s||(z(e.$$.fragment,i),s=!0)},o(i){I(e.$$.fragment,i),s=!1},d(i){oe(e,i)}}}function qn(n){let e,t,s,r,l=[Ri,bi],i=[];function a(o,h){return o[12]?0:1}return t=a(n,[-1,-1]),s=i[t]=l[t](n),{c(){e=k("div"),s.c(),p(e,"class","pagefind-ui__results-area svelte-e9gkc3")},m(o,h){y(o,e,h),i[t].m(e,null),r=!0},p(o,h){let _=t;t=a(o,h),t===_?i[t].p(o,h):(ie(),I(i[_],1,1,()=>{i[_]=null}),ae(),s=i[t],s?s.p(o,h):(s=i[t]=l[t](o),s.c()),z(s,1),s.m(e,null))},i(o){r||(z(s),r=!0)},o(o){I(s),r=!1},d(o){o&&C(e),i[t].d()}}}function bi(n){let e,t,s,r=[],l=new Map,i,a,o;function h(m,d){return m[11].results.length===0?Ci:m[11].results.length===1?ki:Ti}let _=h(n,[-1,-1]),f=_(n),c=n[11].results.slice(0,n[15]),E=m=>m[48].id;for(let m=0;mn[15]&&Wn(n);return{c(){e=k("p"),f.c(),t=M(),s=k("ol");for(let m=0;mm[15]?u?u.p(m,d):(u=Wn(m),u.c(),u.m(a.parentNode,a)):u&&(u.d(1),u=null)},i(m){if(!o){for(let d=0;d{o[c]=null}),ae(),r=o[s],r?r.p(e,f):(r=o[s]=a[s](e),r.c()),z(r,1),r.m(l.parentNode,l))},i(_){i||(z(r),i=!0)},o(_){I(r),i=!1},d(_){_&&C(t),o[s].d(_),_&&C(l)}}}function Wn(n){let e,t=n[18]("load_more",n[17],n[5])+"",s,r,l;return{c(){e=k("button"),s=A(t),p(e,"type","button"),p(e,"class","pagefind-ui__button svelte-e9gkc3")},m(i,a){y(i,e,a),b(e,s),r||(l=K(e,"click",n[20]),r=!0)},p(i,a){a[0]&131104&&t!==(t=i[18]("load_more",i[17],i[5])+"")&&N(s,t)},d(i){i&&C(e),r=!1,l()}}}function Vn(n){let e,t=n[18]("searching",n[17],n[5]).replace(/\[SEARCH_TERM\]/,n[14])+"",s;return{c(){e=k("p"),s=A(t),p(e,"class","pagefind-ui__message svelte-e9gkc3")},m(r,l){y(r,e,l),b(e,s)},p(r,l){l[0]&147488&&t!==(t=r[18]("searching",r[17],r[5]).replace(/\[SEARCH_TERM\]/,r[14])+"")&&N(s,t)},d(r){r&&C(e)}}}function vi(n){let e,t,s,r,l,i,a=n[18]("clear_search",n[17],n[5])+"",o,h,_,f,c,E,u,m,d=n[10]&&Ln(n),R=n[13]&&qn(n);return{c(){e=k("div"),t=k("form"),s=k("input"),l=M(),i=k("button"),o=A(a),h=M(),_=k("div"),d&&d.c(),f=M(),R&&R.c(),p(s,"class","pagefind-ui__search-input svelte-e9gkc3"),p(s,"type","text"),p(s,"placeholder",r=n[18]("placeholder",n[17],n[5])),p(s,"autocapitalize","none"),p(s,"enterkeyhint","search"),p(i,"class","pagefind-ui__search-clear svelte-e9gkc3"),W(i,"pagefind-ui__suppressed",!n[6]),p(_,"class","pagefind-ui__drawer svelte-e9gkc3"),W(_,"pagefind-ui__hidden",!n[13]),p(t,"class","pagefind-ui__form svelte-e9gkc3"),p(t,"role","search"),p(t,"aria-label",c=n[18]("search_label",n[17],n[5])),p(t,"action","javascript:void(0);"),p(e,"class","pagefind-ui svelte-e9gkc3"),W(e,"pagefind-ui--reset",n[0])},m(T,S){y(T,e,S),b(e,t),b(t,s),it(s,n[6]),n[31](s),b(t,l),b(t,i),b(i,o),n[32](i),b(t,h),b(t,_),d&&d.m(_,null),b(_,f),R&&R.m(_,null),E=!0,u||(m=[K(s,"focus",n[19]),K(s,"keydown",n[29]),K(s,"input",n[30]),K(i,"click",n[33]),K(t,"submit",Mi)],u=!0)},p(T,S){(!E||S[0]&131104&&r!==(r=T[18]("placeholder",T[17],T[5])))&&p(s,"placeholder",r),S[0]&64&&s.value!==T[6]&&it(s,T[6]),(!E||S[0]&131104)&&a!==(a=T[18]("clear_search",T[17],T[5])+"")&&N(o,a),(!E||S[0]&64)&&W(i,"pagefind-ui__suppressed",!T[6]),T[10]?d?(d.p(T,S),S[0]&1024&&z(d,1)):(d=Ln(T),d.c(),z(d,1),d.m(_,f)):d&&(ie(),I(d,1,1,()=>{d=null}),ae()),T[13]?R?(R.p(T,S),S[0]&8192&&z(R,1)):(R=qn(T),R.c(),z(R,1),R.m(_,null)):R&&(ie(),I(R,1,1,()=>{R=null}),ae()),(!E||S[0]&8192)&&W(_,"pagefind-ui__hidden",!T[13]),(!E||S[0]&131104&&c!==(c=T[18]("search_label",T[17],T[5])))&&p(t,"aria-label",c),(!E||S[0]&1)&&W(e,"pagefind-ui--reset",T[0])},i(T){E||(z(d),z(R),E=!0)},o(T){I(d),I(R),E=!1},d(T){T&&C(e),n[31](null),n[32](null),d&&d.d(),R&&R.d(),u=!1,V(m)}}}var Mi=n=>n.preventDefault();function Ai(n,e,t){let s={},r=In.map(g=>g.match(/([^\/]+)\.json$/)[1]);for(let g=0;gO[g]??H[g]??"";at(()=>{let g=document?.querySelector?.("html")?.getAttribute?.("lang")||"en",H=Qe(g.toLocaleLowerCase());t(17,Yt=s[`${H.language}-${H.script}-${H.region}`]||s[`${H.language}-${H.region}`]||s[`${H.language}`]||s.en)}),ot(()=>{w?.destroy?.(),w=null});let Xt=async()=>{if(!U&&(t(10,U=!0),!w)){let g;try{g=await import(`${l}pagefind.js`)}catch(O){console.error(O),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to PagefindUI",`[DEBUG: Loaded from ${document?.currentScript?.src??"no known script location"}]`].join(` +`))}_||t(22,_=h?12:30);let H={...m||{},excerptLength:_};await g.options(H);for(let O of d){if(!O.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let L=O.bundlePath;delete O.bundlePath,await g.mergeIndex(L,O)}w=g,Jn()}},Jn=async()=>{w&&(Jt=await w.filters(),(!ue||!Object.keys(ue).length)&&t(16,ue=Jt))},Yn=g=>{let H={};return Object.entries(g).filter(([,O])=>O).forEach(([O])=>{let[L,ls]=O.split(/:(.*)$/);H[L]=H[L]||[],H[L].push(ls)}),H},ce,Xn=async(g,H)=>{if(!g){t(13,$e=!1),ce&&clearTimeout(ce);return}let O=Yn(H),L=()=>Zn(g,O);u>0&&g?(ce&&clearTimeout(ce),ce=setTimeout(L,u),await Zt(),w.preload(g,{filters:O})):L(),Qn()},Zt=async()=>{for(;!w;)Xt(),await new Promise(g=>setTimeout(g,50))},Zn=async(g,H)=>{t(14,Kt=g||""),typeof c=="function"&&(g=c(g)),t(12,Z=!0),t(13,$e=!0),await Zt();let O=++Gt,L=await w.search(g,{filters:H});Gt===O&&(L.filters&&Object.keys(L.filters)?.length&&t(16,ue=L.filters),t(11,P=L),t(12,Z=!1),t(15,et=i))},Qn=()=>{let g=X.offsetWidth;g!=F&&t(8,B.style.paddingRight=`${g+2}px`,B)},xn=g=>{g?.preventDefault(),t(15,et+=i)},$n=g=>{g.key==="Escape"&&(t(6,S=""),B.blur()),g.key==="Enter"&&g.preventDefault()};function es(){S=this.value,t(6,S),t(21,R)}function ts(g){re[g?"unshift":"push"](()=>{B=g,t(8,B)})}function ns(g){re[g?"unshift":"push"](()=>{X=g,t(9,X)})}let ss=()=>{t(6,S=""),B.blur()};function rs(g){Ge=g,t(7,Ge)}return n.$$set=g=>{"base_path"in g&&t(23,l=g.base_path),"page_size"in g&&t(24,i=g.page_size),"reset_styles"in g&&t(0,a=g.reset_styles),"show_images"in g&&t(1,o=g.show_images),"show_sub_results"in g&&t(2,h=g.show_sub_results),"excerpt_length"in g&&t(22,_=g.excerpt_length),"process_result"in g&&t(3,f=g.process_result),"process_term"in g&&t(25,c=g.process_term),"show_empty_filters"in g&&t(4,E=g.show_empty_filters),"debounce_timeout_ms"in g&&t(26,u=g.debounce_timeout_ms),"pagefind_options"in g&&t(27,m=g.pagefind_options),"merge_index"in g&&t(28,d=g.merge_index),"trigger_search_term"in g&&t(21,R=g.trigger_search_term),"translations"in g&&t(5,T=g.translations)},n.$$.update=()=>{if(n.$$.dirty[0]&2097152)e:R&&(t(6,S=R),t(21,R=""));if(n.$$.dirty[0]&192)e:Xn(S,Ge)},[a,o,h,f,E,T,S,Ge,B,X,U,P,Z,$e,Kt,et,ue,Yt,Kn,Xt,xn,R,_,l,i,c,u,m,d,$n,es,ts,ns,ss,rs]}var Wt=class extends q{constructor(e){super(),J(this,e,Ai,vi,G,{base_path:23,page_size:24,reset_styles:0,show_images:1,show_sub_results:2,excerpt_length:22,process_result:3,process_term:25,show_empty_filters:4,debounce_timeout_ms:26,pagefind_options:27,merge_index:28,trigger_search_term:21,translations:5},null,[-1,-1])}},Gn=Wt;var Vt;try{Vt=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind-)?ui.js.*$/)[1]}catch{Vt="/pagefind/"}var xe=class{constructor(e){this._pfs=null;let t=e.element??"[data-pagefind-ui]",s=e.bundlePath??Vt,r=e.pageSize??5,l=e.resetStyles??!0,i=e.showImages??!0,a=e.showSubResults??!1,o=e.excerptLength??0,h=e.processResult??null,_=e.processTerm??null,f=e.showEmptyFilters??!0,c=e.debounceTimeoutMs??300,E=e.mergeIndex??[],u=e.translations??[];delete e.element,delete e.bundlePath,delete e.pageSize,delete e.resetStyles,delete e.showImages,delete e.showSubResults,delete e.excerptLength,delete e.processResult,delete e.processTerm,delete e.showEmptyFilters,delete e.debounceTimeoutMs,delete e.mergeIndex,delete e.translations;let m=t instanceof HTMLElement?t:document.querySelector(t);m?this._pfs=new Gn({target:m,props:{base_path:s,page_size:r,reset_styles:l,show_images:i,show_sub_results:a,excerpt_length:o,process_result:h,process_term:_,show_empty_filters:f,debounce_timeout_ms:c,merge_index:E,translations:u,pagefind_options:e}}):console.error(`Pagefind UI couldn't find the selector ${t}`)}triggerSearch(e){this._pfs.$$set({trigger_search_term:e})}destroy(){this._pfs.$destroy()}};window.PagefindUI=xe;})(); diff --git a/docs/_/pagefind/pagefind.js b/docs/_/pagefind/pagefind.js new file mode 100644 index 00000000..94ab9237 --- /dev/null +++ b/docs/_/pagefind/pagefind.js @@ -0,0 +1,9 @@ +const pagefind_version="1.0.4";let wasm_bindgen;(function(){const __exports={};let script_src;if(typeof document==='undefined'){script_src=location.href}else{script_src=new URL("UNHANDLED",location.href).toString()}let wasm;let cachedUint8Memory0=null;function getUint8Memory0(){if(cachedUint8Memory0===null||cachedUint8Memory0.byteLength===0){cachedUint8Memory0=new Uint8Array(wasm.memory.buffer)}return cachedUint8Memory0}let WASM_VECTOR_LEN=0;function passArray8ToWasm0(arg,malloc){const ptr=malloc(arg.length*1);getUint8Memory0().set(arg,ptr/1);WASM_VECTOR_LEN=arg.length;return ptr}__exports.init_pagefind=function(metadata_bytes){const ptr0=passArray8ToWasm0(metadata_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.init_pagefind(ptr0,len0);return ret};__exports.load_index_chunk=function(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_index_chunk(ptr,ptr0,len0);return ret};__exports.load_filter_chunk=function(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_filter_chunk(ptr,ptr0,len0);return ret};const cachedTextEncoder=new TextEncoder('utf-8');const encodeString=(typeof cachedTextEncoder.encodeInto==='function'?function(arg,view){return cachedTextEncoder.encodeInto(arg,view)}:function(arg,view){const buf=cachedTextEncoder.encode(arg);view.set(buf);return{read:arg.length,written:buf.length}});function passStringToWasm0(arg,malloc,realloc){if(realloc===undefined){const buf=cachedTextEncoder.encode(arg);const ptr=malloc(buf.length);getUint8Memory0().subarray(ptr,ptr+buf.length).set(buf);WASM_VECTOR_LEN=buf.length;return ptr}let len=arg.length;let ptr=malloc(len);const mem=getUint8Memory0();let offset=0;for(;offset0x7F)break;mem[ptr+offset]=code}if(offset!==len){if(offset!==0){arg=arg.slice(offset)}ptr=realloc(ptr,len,len=offset+arg.length*3);const view=getUint8Memory0().subarray(ptr+offset,ptr+len);const ret=encodeString(arg,view);offset+=ret.written}WASM_VECTOR_LEN=offset;return ptr}__exports.add_synthetic_filter=function(ptr,filter){const ptr0=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.add_synthetic_filter(ptr,ptr0,len0);return ret};let cachedInt32Memory0=null;function getInt32Memory0(){if(cachedInt32Memory0===null||cachedInt32Memory0.byteLength===0){cachedInt32Memory0=new Int32Array(wasm.memory.buffer)}return cachedInt32Memory0}const cachedTextDecoder=new TextDecoder('utf-8',{ignoreBOM:true,fatal:true});cachedTextDecoder.decode();function getStringFromWasm0(ptr,len){return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr,ptr+len))}__exports.request_indexes=function(ptr,query){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;wasm.request_indexes(retptr,ptr,ptr0,len0);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.request_filter_indexes=function(ptr,filters){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(filters,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;wasm.request_filter_indexes(retptr,ptr,ptr0,len0);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.request_all_filter_indexes=function(ptr){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);wasm.request_all_filter_indexes(retptr,ptr);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.filters=function(ptr){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);wasm.filters(retptr,ptr);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.search=function(ptr,query,filter,sort,exact){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ptr1=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len1=WASM_VECTOR_LEN;const ptr2=passStringToWasm0(sort,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len2=WASM_VECTOR_LEN;wasm.search(retptr,ptr,ptr0,len0,ptr1,len1,ptr2,len2,exact);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};async function load(module,imports){if(typeof Response==='function'&&module instanceof Response){if(typeof WebAssembly.instantiateStreaming==='function'){try{return await WebAssembly.instantiateStreaming(module,imports)}catch(e){if(module.headers.get('Content-Type')!='application/wasm'){console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",e)}else{throw e}}}const bytes=await module.arrayBuffer();return await WebAssembly.instantiate(bytes,imports)}else{const instance=await WebAssembly.instantiate(module,imports);if(instance instanceof WebAssembly.Instance){return{instance,module}}else{return instance}}}function getImports(){const imports={};imports.wbg={};return imports}function initMemory(imports,maybe_memory){}function finalizeInit(instance,module){wasm=instance.exports;init.__wbindgen_wasm_module=module;cachedInt32Memory0=null;cachedUint8Memory0=null;return wasm}function initSync(module){const imports=getImports();initMemory(imports);if(!(module instanceof WebAssembly.Module)){module=new WebAssembly.Module(module)}const instance=new WebAssembly.Instance(module,imports);return finalizeInit(instance,module)}async function init(input){if(typeof input==='undefined'){input=script_src.replace(/\.js$/,'_bg.wasm')}const imports=getImports();if(typeof input==='string'||(typeof Request==='function'&&input instanceof Request)||(typeof URL==='function'&&input instanceof URL)){input=fetch(input)}initMemory(imports);const{instance,module}=await load(await input,imports);return finalizeInit(instance,module)}wasm_bindgen=Object.assign(init,{initSync},__exports)})();var u8=Uint8Array;var u16=Uint16Array;var u32=Uint32Array;var fleb=new u8([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]);var fdeb=new u8([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]);var clim=new u8([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);var freb=function(eb,start){var b=new u16(31);for(var i2=0;i2<31;++i2){b[i2]=start+=1<>>1|(i&21845)<<1;x=(x&52428)>>>2|(x&13107)<<2;x=(x&61680)>>>4|(x&3855)<<4;rev[i]=((x&65280)>>>8|(x&255)<<8)>>>1}var x;var i;var hMap=function(cd,mb,r){var s=cd.length;var i2=0;var l=new u16(mb);for(;i2>>rvb]=sv}}}}else{co=new u16(s);for(i2=0;i2>>15-cd[i2]}}}return co};var flt=new u8(288);for(i=0;i<144;++i)flt[i]=8;var i;for(i=144;i<256;++i)flt[i]=9;var i;for(i=256;i<280;++i)flt[i]=7;var i;for(i=280;i<288;++i)flt[i]=8;var i;var fdt=new u8(32);for(i=0;i<32;++i)fdt[i]=5;var i;var flrm=hMap(flt,9,1);var fdrm=hMap(fdt,5,1);var max=function(a){var m=a[0];for(var i2=1;i2m)m=a[i2]}return m};var bits=function(d,p,m){var o=p/8|0;return(d[o]|d[o+1]<<8)>>(p&7)&m};var bits16=function(d,p){var o=p/8|0;return(d[o]|d[o+1]<<8|d[o+2]<<16)>>(p&7)};var shft=function(p){return(p+7)/8|0};var slc=function(v,s,e){if(s==null||s<0)s=0;if(e==null||e>v.length)e=v.length;var n=new(v.BYTES_PER_ELEMENT==2?u16:v.BYTES_PER_ELEMENT==4?u32:u8)(e-s);n.set(v.subarray(s,e));return n};var ec=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"];var err=function(ind,msg,nt){var e=new Error(msg||ec[ind]);e.code=ind;if(Error.captureStackTrace)Error.captureStackTrace(e,err);if(!nt)throw e;return e};var inflt=function(dat,buf,st){var sl=dat.length;if(!sl||st&&st.f&&!st.l)return buf||new u8(0);var noBuf=!buf||st;var noSt=!st||st.i;if(!st)st={};if(!buf)buf=new u8(sl*3);var cbuf=function(l2){var bl=buf.length;if(l2>bl){var nbuf=new u8(Math.max(bl*2,l2));nbuf.set(buf);buf=nbuf}};var final=st.f||0,pos=st.p||0,bt=st.b||0,lm=st.l,dm=st.d,lbt=st.m,dbt=st.n;var tbts=sl*8;do{if(!lm){final=bits(dat,pos,1);var type=bits(dat,pos+1,3);pos+=3;if(!type){var s=shft(pos)+4,l=dat[s-4]|dat[s-3]<<8,t=s+l;if(t>sl){if(noSt)err(0);break}if(noBuf)cbuf(bt+l);buf.set(dat.subarray(s,t),bt);st.b=bt+=l,st.p=pos=t*8,st.f=final;continue}else if(type==1)lm=flrm,dm=fdrm,lbt=9,dbt=5;else if(type==2){var hLit=bits(dat,pos,31)+257,hcLen=bits(dat,pos+10,15)+4;var tl=hLit+bits(dat,pos+5,31)+1;pos+=14;var ldt=new u8(tl);var clt=new u8(19);for(var i2=0;i2>>4;if(s<16){ldt[i2++]=s}else{var c=0,n=0;if(s==16)n=3+bits(dat,pos,3),pos+=2,c=ldt[i2-1];else if(s==17)n=3+bits(dat,pos,7),pos+=3;else if(s==18)n=11+bits(dat,pos,127),pos+=7;while(n--)ldt[i2++]=c}}var lt=ldt.subarray(0,hLit),dt=ldt.subarray(hLit);lbt=max(lt);dbt=max(dt);lm=hMap(lt,lbt,1);dm=hMap(dt,dbt,1)}else err(1);if(pos>tbts){if(noSt)err(0);break}}if(noBuf)cbuf(bt+131072);var lms=(1<>>4;pos+=c&15;if(pos>tbts){if(noSt)err(0);break}if(!c)err(2);if(sym<256)buf[bt++]=sym;else if(sym==256){lpos=pos,lm=null;break}else{var add=sym-254;if(sym>264){var i2=sym-257,b=fleb[i2];add=bits(dat,pos,(1<>>4;if(!d)err(3);pos+=d&15;var dt=fd[dsym];if(dsym>3){var b=fdeb[dsym];dt+=bits16(dat,pos)&(1<tbts){if(noSt)err(0);break}if(noBuf)cbuf(bt+131072);var end=bt+add;for(;bt>3&1)+(flg>>4&1);zs>0;zs-=!d[st++]);return st+(flg&2)};var gzl=function(d){var l=d.length;return(d[l-4]|d[l-3]<<8|d[l-2]<<16|d[l-1]<<24)>>>0};function gunzipSync(data,out){return inflt(data.subarray(gzs(data),-8),out||new u8(gzl(data)))}var td=typeof TextDecoder!="undefined"&&new TextDecoder();var tds=0;try{td.decode(et,{stream:true});tds=1}catch(e){}var gz_default=gunzipSync;var calculate_excerpt_region=(word_positions,excerpt_length)=>{if(word_positions.length===0){return 0}let words=[];for(const word of word_positions){words[word.location]=words[word.location]||0;words[word.location]+=word.balanced_score}if(words.length<=excerpt_length){return 0}let densest=words.slice(0,excerpt_length).reduce((partialSum,a)=>partialSum+a,0);let working_sum=densest;let densest_at=[0];for(let i2=0;i2densest){densest=working_sum;densest_at=[i2]}else if(working_sum===densest&&densest_at[densest_at.length-1]===i2-1){densest_at.push(i2)}}let midpoint=densest_at[Math.floor(densest_at.length/2)];return midpoint};var build_excerpt=(content,start,length,locations,not_before,not_from)=>{let is_zws_delimited=content.includes("\u200B");let fragment_words=[];if(is_zws_delimited){fragment_words=content.split("\u200B")}else{fragment_words=content.split(/[\r\n\s]+/g)}for(let word of locations){if(fragment_words[word]?.startsWith(``)){continue}fragment_words[word]=`${fragment_words[word]}`}let endcap=not_from??fragment_words.length;let startcap=not_before??0;if(endcap-startcapendcap){start=endcap-length}if(start{const anchors=fragment.anchors.filter((a)=>/h\d/i.test(a.element)&&a.text?.length&&/\S/.test(a.text)).sort((a,b)=>a.location-b.location);const results=[];let current_anchor_position=0;let current_anchor={title:fragment.meta["title"],url:fragment.url,weighted_locations:[],locations:[],excerpt:""};const add_result=(end_range)=>{if(current_anchor.locations.length){const relative_weighted_locations=current_anchor.weighted_locations.map((l)=>{return{weight:l.weight,balanced_score:l.balanced_score,location:l.location-current_anchor_position}});const excerpt_start=calculate_excerpt_region(relative_weighted_locations,desired_excerpt_length)+current_anchor_position;const excerpt_length=end_range?Math.min(end_range-excerpt_start,desired_excerpt_length):desired_excerpt_length;current_anchor.excerpt=build_excerpt(fragment.raw_content??"",excerpt_start,excerpt_length,current_anchor.locations,current_anchor_position,end_range);results.push(current_anchor)}};for(let word of fragment.weighted_locations){if(!anchors.length||word.location=anchors[0].location){next_anchor=anchors.shift()}let anchored_url=fragment.url;try{const url_is_fq=/^((https?:)?\/\/)/.test(anchored_url);if(url_is_fq){let fq_url=new URL(anchored_url);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString()}else{if(!/^\//.test(anchored_url)){anchored_url=`/${anchored_url}`}let fq_url=new URL(`https://example.com${anchored_url}`);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString().replace(/^https:\/\/example.com/,"")}}catch(e){console.error(`Pagefind: Couldn't process ${anchored_url} for a search result`)}current_anchor_position=next_anchor.location;current_anchor={title:next_anchor.text,url:anchored_url,anchor:next_anchor,weighted_locations:[word],locations:[word.location],excerpt:""}}}add_result(anchors[0]?.location);return results};var asyncSleep=async(ms=100)=>{return new Promise((r)=>setTimeout(r,ms))};var PagefindInstance=class{constructor(opts={}){this.version=pagefind_version;this.backend=wasm_bindgen;this.decoder=new TextDecoder("utf-8");this.wasm=null;this.basePath=opts.basePath||"/pagefind/";this.primary=opts.primary||false;if(this.primary&&!opts.basePath){this.initPrimary()}if(/[^\/]$/.test(this.basePath)){this.basePath=`${this.basePath}/`}if(window?.location?.origin&&this.basePath.startsWith(window.location.origin)){this.basePath=this.basePath.replace(window.location.origin,"")}this.baseUrl=opts.baseUrl||this.defaultBaseUrl();if(!/^(\/|https?:\/\/)/.test(this.baseUrl)){this.baseUrl=`/${this.baseUrl}`}this.indexWeight=opts.indexWeight??1;this.excerptLength=opts.excerptLength??30;this.mergeFilter=opts.mergeFilter??{};this.highlightParam=opts.highlightParam??null;this.loaded_chunks={};this.loaded_filters={};this.loaded_fragments={};this.raw_ptr=null;this.searchMeta=null;this.languages=null}initPrimary(){let derivedBasePath=import.meta.url.match(/^(.*\/)pagefind.js.*$/)?.[1];if(derivedBasePath){this.basePath=derivedBasePath}else{console.warn(["Pagefind couldn't determine the base of the bundle from the import path. Falling back to the default.","Set a basePath option when initialising Pagefind to ignore this message."].join("\n"))}}defaultBaseUrl(){let default_base=this.basePath.match(/^(.*\/)_?pagefind/)?.[1];return default_base||"/"}async options(options2){const opts=["basePath","baseUrl","indexWeight","excerptLength","mergeFilter","highlightParam"];for(const[k,v]of Object.entries(options2)){if(k==="mergeFilter"){let filters2=this.stringifyFilters(v);let ptr=await this.getPtr();this.raw_ptr=this.backend.add_synthetic_filter(ptr,filters2)}else if(opts.includes(k)){if(k==="basePath"&&typeof v==="string")this.basePath=v;if(k==="baseUrl"&&typeof v==="string")this.baseUrl=v;if(k==="indexWeight"&&typeof v==="number")this.indexWeight=v;if(k==="excerptLength"&&typeof v==="number")this.excerptLength=v;if(k==="mergeFilter"&&typeof v==="object")this.mergeFilter=v;if(k==="highlightParam"&&typeof v==="string")this.highlightParam=v}else{console.warn(`Unknown Pagefind option ${k}. Allowed options: [${opts.join(", ")}]`)}}}decompress(data,file="unknown file"){if(this.decoder.decode(data.slice(0,12))==="pagefind_dcd"){return data.slice(12)}data=gz_default(data);if(this.decoder.decode(data.slice(0,12))!=="pagefind_dcd"){console.error(`Decompressing ${file} appears to have failed: Missing signature`);return data}return data.slice(12)}async init(language,opts){await this.loadEntry();let index=this.findIndex(language);let lang_wasm=index.wasm?index.wasm:"unknown";let resources=[this.loadMeta(index.hash)];if(opts.load_wasm===true){resources.push(this.loadWasm(lang_wasm))}await Promise.all(resources);this.raw_ptr=this.backend.init_pagefind(new Uint8Array(this.searchMeta));if(Object.keys(this.mergeFilter)?.length){let filters2=this.stringifyFilters(this.mergeFilter);let ptr=await this.getPtr();this.raw_ptr=this.backend.add_synthetic_filter(ptr,filters2)}}async loadEntry(){try{let entry_response=await fetch(`${this.basePath}pagefind-entry.json?ts=${Date.now()}`);let entry_json=await entry_response.json();this.languages=entry_json.languages;if(entry_json.version!==this.version){if(this.primary){console.warn(["Pagefind JS version doesn't match the version in your search index.",`Pagefind JS: ${this.version}. Pagefind index: ${entry_json.version}`,"If you upgraded Pagefind recently, you likely have a cached pagefind.js file.","If you encounter any search errors, try clearing your cache."].join("\n"))}else{console.warn(["Merging a Pagefind index from a different version than the main Pagefind instance.",`Main Pagefind JS: ${this.version}. Merged index (${this.basePath}): ${entry_json.version}`,"If you encounter any search errors, make sure that both sites are running the same version of Pagefind."].join("\n"))}}}catch(e){console.error(`Failed to load Pagefind metadata: +${e?.toString()}`);throw new Error("Failed to load Pagefind metadata")}}findIndex(language){if(this.languages){let index=this.languages[language];if(index)return index;index=this.languages[language.split("-")[0]];if(index)return index;let topLang=Object.values(this.languages).sort((a,b)=>b.page_count-a.page_count);if(topLang[0])return topLang[0]}throw new Error("Pagefind Error: No language indexes found.")}async loadMeta(index){try{let compressed_resp=await fetch(`${this.basePath}pagefind.${index}.pf_meta`);let compressed_meta=await compressed_resp.arrayBuffer();this.searchMeta=this.decompress(new Uint8Array(compressed_meta),"Pagefind metadata")}catch(e){console.error(`Failed to load the meta index: +${e?.toString()}`)}}async loadWasm(language){try{const wasm_url=`${this.basePath}wasm.${language}.pagefind`;let compressed_resp=await fetch(wasm_url);let compressed_wasm=await compressed_resp.arrayBuffer();const final_wasm=this.decompress(new Uint8Array(compressed_wasm),"Pagefind WebAssembly");if(!final_wasm){throw new Error("No WASM after decompression")}this.wasm=await this.backend(final_wasm)}catch(e){console.error(`Failed to load the Pagefind WASM: +${e?.toString()}`);throw new Error(`Failed to load the Pagefind WASM: +${e?.toString()}`)}}async _loadGenericChunk(url,method){try{let compressed_resp=await fetch(url);let compressed_chunk=await compressed_resp.arrayBuffer();let chunk=this.decompress(new Uint8Array(compressed_chunk),url);let ptr=await this.getPtr();this.raw_ptr=this.backend[method](ptr,chunk)}catch(e){console.error(`Failed to load the index chunk ${url}: +${e?.toString()}`)}}async loadChunk(hash){if(!this.loaded_chunks[hash]){const url=`${this.basePath}index/${hash}.pf_index`;this.loaded_chunks[hash]=this._loadGenericChunk(url,"load_index_chunk")}return await this.loaded_chunks[hash]}async loadFilterChunk(hash){if(!this.loaded_filters[hash]){const url=`${this.basePath}filter/${hash}.pf_filter`;this.loaded_filters[hash]=this._loadGenericChunk(url,"load_filter_chunk")}return await this.loaded_filters[hash]}async _loadFragment(hash){let compressed_resp=await fetch(`${this.basePath}fragment/${hash}.pf_fragment`);let compressed_fragment=await compressed_resp.arrayBuffer();let fragment=this.decompress(new Uint8Array(compressed_fragment),`Fragment ${hash}`);return JSON.parse(new TextDecoder().decode(fragment))}async loadFragment(hash,weighted_locations=[],search_term){if(!this.loaded_fragments[hash]){this.loaded_fragments[hash]=this._loadFragment(hash)}let fragment=await this.loaded_fragments[hash];fragment.weighted_locations=weighted_locations;fragment.locations=weighted_locations.map((l)=>l.location);if(!fragment.raw_content){fragment.raw_content=fragment.content.replace(//g,">");fragment.content=fragment.content.replace(/\u200B/g,"")}if(!fragment.raw_url){fragment.raw_url=fragment.url}fragment.url=this.processedUrl(fragment.raw_url,search_term);const excerpt_start=calculate_excerpt_region(weighted_locations,this.excerptLength);fragment.excerpt=build_excerpt(fragment.raw_content,excerpt_start,this.excerptLength,fragment.locations);fragment.sub_results=calculate_sub_results(fragment,this.excerptLength);return fragment}fullUrl(raw){if(/^(https?:)?\/\//.test(raw)){return raw}return`${this.baseUrl}/${raw}`.replace(/\/+/g,"/").replace(/^(https?:\/)/,"$1/")}processedUrl(url,search_term){const normalized=this.fullUrl(url);if(this.highlightParam===null){return normalized}let individual_terms=search_term.split(/\s+/);try{let processed=new URL(normalized);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term)}return processed.toString()}catch(e){try{let processed=new URL(`https://example.com${normalized}`);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term)}return processed.toString().replace(/^https:\/\/example\.com/,"")}catch(e2){return normalized}}}async getPtr(){while(this.raw_ptr===null){await asyncSleep(50)}if(!this.raw_ptr){console.error("Pagefind: WASM Error (No pointer)");throw new Error("Pagefind: WASM Error (No pointer)")}return this.raw_ptr}parseFilters(str){let output={};if(!str)return output;for(const block of str.split("__PF_FILTER_DELIM__")){let[filter,values]=block.split(/:(.*)$/);output[filter]={};if(values){for(const valueBlock of values.split("__PF_VALUE_DELIM__")){if(valueBlock){let extract=valueBlock.match(/^(.*):(\d+)$/);if(extract){let[,value,count]=extract;output[filter][value]=parseInt(count)??count}}}}}return output}stringifyFilters(obj={}){return JSON.stringify(obj)}stringifySorts(obj={}){let sorts=Object.entries(obj);for(let[sort,direction]of sorts){if(sorts.length>1){console.warn(`Pagefind was provided multiple sort options in this search, but can only operate on one. Using the ${sort} sort.`)}if(direction!=="asc"&&direction!=="desc"){console.warn(`Pagefind was provided a sort with unknown direction ${direction}. Supported: [asc, desc]`)}return`${sort}:${direction}`}return``}async filters(){let ptr=await this.getPtr();let filters2=this.backend.request_all_filter_indexes(ptr);let filter_chunks=filters2.split(" ").filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...filter_chunks]);ptr=await this.getPtr();let results=this.backend.filters(ptr);return this.parseFilters(results)}async preload(term,options2={}){await this.search(term,{...options2,preload:true})}async search(term,options2={}){options2={verbose:false,filters:{},sort:{},...options2};const log=(str)=>{if(options2.verbose)console.log(str)};log(`Starting search on ${this.basePath}`);let start=Date.now();let ptr=await this.getPtr();let filter_only=term===null;term=term??"";let exact_search=/^\s*".+"\s*$/.test(term);if(exact_search){log(`Running an exact search`)}term=term.toLowerCase().trim().replace(/[\.`~!@#\$%\^&\*\(\)\{\}\[\]\\\|:;'",<>\/\?\-]/g,"").replace(/\s{2,}/g," ").trim();log(`Normalized search term to ${term}`);if(!term?.length&&!filter_only){return{results:[],unfilteredResultCount:0,filters:{},totalFilters:{},timings:{preload:Date.now()-start,search:Date.now()-start,total:Date.now()-start}}}let sort_list=this.stringifySorts(options2.sort);log(`Stringified sort to ${sort_list}`);const filter_list=this.stringifyFilters(options2.filters);log(`Stringified filters to ${filter_list}`);let index_resp=this.backend.request_indexes(ptr,term);let filter_resp=this.backend.request_filter_indexes(ptr,filter_list);let chunks=index_resp.split(" ").filter((v)=>v).map((chunk)=>this.loadChunk(chunk));let filter_chunks=filter_resp.split(" ").filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...chunks,...filter_chunks]);log(`Loaded necessary chunks to run search`);if(options2.preload){log(`Preload \u2014 bailing out of search operation now.`);return null}ptr=await this.getPtr();let searchStart=Date.now();let result=this.backend.search(ptr,term,filter_list,sort_list,exact_search);log(`Got the raw search result: ${result}`);let[unfilteredResultCount,all_results,filters2,totalFilters]=result.split(/:([^:]*):(.*)__PF_UNFILTERED_DELIM__(.*)$/);let filterObj=this.parseFilters(filters2);let totalFilterObj=this.parseFilters(totalFilters);log(`Remaining filters: ${JSON.stringify(result)}`);let results=all_results.length?all_results.split(" "):[];let resultsInterface=results.map((result2)=>{let[hash,score,all_locations]=result2.split("@");log(`Processing result: + hash:${hash} + score:${score} + locations:${all_locations}`);let weighted_locations=all_locations.length?all_locations.split(",").map((l)=>{let[weight,balanced_score,location]=l.split(">");return{weight:parseInt(weight)/24,balanced_score:parseFloat(balanced_score),location:parseInt(location)}}):[];let locations=weighted_locations.map((l)=>l.location);return{id:hash,score:parseFloat(score)*this.indexWeight,words:locations,data:async()=>await this.loadFragment(hash,weighted_locations,term)}});const searchTime=Date.now()-searchStart;const realTime=Date.now()-start;log(`Found ${results.length} result${results.length == 1 ? "" : "s"} for "${term}" in ${Date.now() - searchStart}ms (${Date.now() - start}ms realtime)`);return{results:resultsInterface,unfilteredResultCount:parseInt(unfilteredResultCount),filters:filterObj,totalFilters:totalFilterObj,timings:{preload:realTime-searchTime,search:searchTime,total:realTime}}}};var Pagefind=class{constructor(options2={}){this.backend=wasm_bindgen;this.primaryLanguage="unknown";this.searchID=0;this.primary=new PagefindInstance({...options2,primary:true});this.instances=[this.primary];this.init(options2?.language)}async options(options2){await this.primary.options(options2)}async init(overrideLanguage){if(document?.querySelector){const langCode=document.querySelector("html")?.getAttribute("lang")||"unknown";this.primaryLanguage=langCode.toLocaleLowerCase()}await this.primary.init(overrideLanguage?overrideLanguage:this.primaryLanguage,{load_wasm:true})}async mergeIndex(indexPath,options2={}){if(this.primary.basePath.startsWith(indexPath)){console.warn(`Skipping mergeIndex ${indexPath} that appears to be the same as the primary index (${this.primary.basePath})`);return}let newInstance=new PagefindInstance({primary:false,basePath:indexPath});this.instances.push(newInstance);while(this.primary.wasm===null){await asyncSleep(50)}await newInstance.init(options2.language||this.primaryLanguage,{load_wasm:false});delete options2["language"];await newInstance.options(options2)}mergeFilters(filters2){const merged={};for(const searchFilter of filters2){for(const[filterKey,values]of Object.entries(searchFilter)){if(!merged[filterKey]){merged[filterKey]=values;continue}else{const filter=merged[filterKey];for(const[valueKey,count]of Object.entries(values)){filter[valueKey]=(filter[valueKey]||0)+count}}}}return merged}async filters(){let filters2=await Promise.all(this.instances.map((i2)=>i2.filters()));return this.mergeFilters(filters2)}async preload(term,options2={}){await Promise.all(this.instances.map((i2)=>i2.preload(term,options2)))}async debouncedSearch(term,options2,debounceTimeoutMs){const thisSearchID=++this.searchID;this.preload(term,options2);await asyncSleep(debounceTimeoutMs);if(thisSearchID!==this.searchID){return null}const searchResult=await this.search(term,options2);if(thisSearchID!==this.searchID){return null}return searchResult}async search(term,options2={}){let search2=await Promise.all(this.instances.map((i2)=>i2.search(term,options2)));const filters2=this.mergeFilters(search2.map((s)=>s.filters));const totalFilters=this.mergeFilters(search2.map((s)=>s.totalFilters));const results=search2.map((s)=>s.results).flat().sort((a,b)=>b.score-a.score);const timings=search2.map((s)=>s.timings);const unfilteredResultCount=search2.reduce((sum,s)=>sum+s.unfilteredResultCount,0);return{results,unfilteredResultCount,filters:filters2,totalFilters,timings}}};var pagefind=void 0;var initial_options=void 0;var init_pagefind=()=>{if(!pagefind){pagefind=new Pagefind(initial_options??{})}};var options=async(new_options)=>{if(pagefind){await pagefind.options(new_options)}else{initial_options=new_options}};var init=async()=>{init_pagefind()};var destroy=async()=>{pagefind=void 0;initial_options=void 0};var mergeIndex=async(indexPath,options2)=>{init_pagefind();return await pagefind.mergeIndex(indexPath,options2)};var search=async(term,options2)=>{init_pagefind();return await pagefind.search(term,options2)};var debouncedSearch=async(term,options2,debounceTimeoutMs=300)=>{init_pagefind();return await pagefind.debouncedSearch(term,options2,debounceTimeoutMs)};var preload=async(term,options2)=>{init_pagefind();return await pagefind.preload(term,options2)};var filters=async()=>{init_pagefind();return await pagefind.filters()};export{debouncedSearch,destroy,filters,init,mergeIndex,options,preload,search} \ No newline at end of file diff --git a/docs/_/pagefind/pagefind.unknown_12195f4b2ff197b.pf_meta b/docs/_/pagefind/pagefind.unknown_12195f4b2ff197b.pf_meta new file mode 100644 index 00000000..37f066b4 Binary files /dev/null and b/docs/_/pagefind/pagefind.unknown_12195f4b2ff197b.pf_meta differ diff --git a/docs/_/pagefind/pagefind.unknown_d32456ea962894a.pf_meta b/docs/_/pagefind/pagefind.unknown_d32456ea962894a.pf_meta new file mode 100644 index 00000000..29c058df Binary files /dev/null and b/docs/_/pagefind/pagefind.unknown_d32456ea962894a.pf_meta differ diff --git a/docs/_/pagefind/wasm.unknown.pagefind b/docs/_/pagefind/wasm.unknown.pagefind new file mode 100644 index 00000000..d7495cc3 Binary files /dev/null and b/docs/_/pagefind/wasm.unknown.pagefind differ diff --git a/docs/controller/index.html b/docs/controller/index.html new file mode 100644 index 00000000..9af0cca2 --- /dev/null +++ b/docs/controller/index.html @@ -0,0 +1,100 @@ + + + + + + + + + + + +controller + + + +

    package controller

    +
    import "backend/app/internal/controller"
    +

    Index

    +

    Functions

    +

    func GetSpecificationHandler

    +
    func GetSpecificationHandler(mongoClient *mongo.Client) gin.HandlerFunc
    +

    GetSpecificationHandler godoc +

    @Summary		Get OpenAPI specification
    +@Description	Retrieve a specific OpenAPI specification's content given a valid ID
    +@Tags			specification
    +@Accept			json
    +@Produce		json
    +@Param			id	path		string	true	"Specification ID"
    +@Success		200	{object}	models.MongoResponseWithApi
    +@Failure		400	{object}	models.HTTPError
    +@Failure		500	{object}	models.HTTPError
    +@Router			/specification/{id} [get]
    +
    +

    func NewHTTPError

    +
    func NewHTTPError(ctx *gin.Context, status int, message string)
    +

    func PostSpecificationHandler

    +
    func PostSpecificationHandler(mongoClient *mongo.Client, elasticClient *elasticsearch.Client) gin.HandlerFunc
    +

    PostSpecificationHandler godoc +

    @Summary		Insert OpenAPI specifications
    +@Description	Insert new OpenAPI specifications in the database.
    +@Tags			specification
    +@Accept			json
    +@Produce		json
    +@Param			specifications	body	models.SpecificationsRequest	true	"New Specifications"
    +@Success		200
    +@Failure		400	{object}	models.HTTPError
    +@Failure		500	{object}	models.HTTPError
    +@Router			/specification [post]
    +
    + +
    func Search(ctx *gin.Context)
    +

    Search godoc +

    	@Summary		Search OpenAPI specifications
    +	@Description	Retrieve OpenAPI specifications matching the given query
    +	@Tags			search
    +	@Accept			json
    +	@Produce		json
    +	@Param			fragment	body		models.EmbeddingRequest	true	"Search query"
    + TODO: Change this
    +	@Success		200			{string}	OK
    +	@Failure		400			{object}	models.HTTPError
    +	@Router			/search [post]
    +
    +

    func SetupRoutes

    +
    func SetupRoutes(router *gin.Engine, config *models.Config)
    +

    func SyncSpecificationsHandler

    +
    func SyncSpecificationsHandler(mongoClient *mongo.Client, elasticClient *elasticsearch.Client) gin.HandlerFunc
    +
    +
    +
    + + Generated with doc2go + +
    + + diff --git a/docs/elastic/index.html b/docs/elastic/index.html new file mode 100644 index 00000000..5f43bb5a --- /dev/null +++ b/docs/elastic/index.html @@ -0,0 +1,66 @@ + + + + + + + + + + + +elastic + + + +

    package elastic

    +
    import "backend/app/internal/elastic"
    +

    Index

    +

    Functions

    +

    func Connect

    +
    func Connect(config models.ElasticConfig) *elasticsearch.Client
    +

    Connect - used to connect to the elasticsearch database. It will return an elasticsearch client that can be used to +perform queries on the database. +

    func DeleteDocument

    +
    func DeleteDocument(client *elasticsearch.Client, id string, index string) error
    +

    DeleteDocument - delete a document in an index based on its id. An elasticsearch client, an id and an index need to +be passed to the function. +

    func InsertDocument

    +
    func InsertDocument(client *elasticsearch.Client, document models.EsRequest, index string) error
    +

    InsertDocument - send and save a document in an elasticsearch index. An elasticsearch client, document and index need +to be passed to the function. +

    func SearchDocument

    +
    func SearchDocument(client *elasticsearch.Client, query string, index string) (*models.EsSearchResponse, error)
    +

    SearchDocument - search a document in an index based on a query. An elasticsearch client, a query and an index need +to be passed to the function. +

    +
    +
    + + Generated with doc2go + +
    + + diff --git a/docs/embedding/index.html b/docs/embedding/index.html new file mode 100644 index 00000000..287989b0 --- /dev/null +++ b/docs/embedding/index.html @@ -0,0 +1,77 @@ + + + + + + + + + + + +embedding + + + +

    package embedding

    +
    import "backend/app/internal/embedding"
    +

    Index

    +

    Functions

    +

    func Embed

    +
    func Embed(fragments []string) (*models.EmbeddingResponse, error)
    +

    Embed use the Universal Sentence Encoder model to transform the array of fragments (string) into an array of +embeddings (512-dimension float32 embedding). A list of embeddings needs to be passed to the function. +

    func ExtractTags

    +
    func ExtractTags(fragments []string) []string
    +

    ExtractTags - extract the NL tags from a fragment (JSON document documenting a REST API), and return an array of +strings, one for each fragment. An array of fragments needs to be passed to the function. +

    func PerformPipeline

    +
    func PerformPipeline(fragments []string, isQuery bool) (*models.EmbeddingResponse, error)
    +

    PerformPipeline - fragments are preprocessed and embeddings are generated and returned. An array of fragments +(string) and a boolean indicating if the fragments are queries or not need to be passed to the function. +

    func PreprocessFragment

    +
    func PreprocessFragment(fragments []string, isQuery bool) []string
    +

    PreprocessFragment - fragments are preprocessed by running a standard NLP pipeline, composed of string cleaning, +stop-word removal, and stemming. An array of fragments (string), and a boolean indicating is the fragments are +queries or not need to be passed to the function. +

    func Stemming

    +
    func Stemming(fragments []string) []string
    +

    Stemming - stem all the words contained in the given strings. An array of strings needs to be passed to the +function. +

    func StopWordRemoval

    +
    func StopWordRemoval(fragments []string) []string
    +

    StopWordRemoval - remove all stopwords from the given strings. An array of strings needs to be passed to the +function. +

    +
    +
    + + Generated with doc2go + +
    + + diff --git a/docs/helpers/index.html b/docs/helpers/index.html new file mode 100644 index 00000000..a55bd630 --- /dev/null +++ b/docs/helpers/index.html @@ -0,0 +1,62 @@ + + + + + + + + + + + +helpers + + + +

    package helpers

    +
    import "backend/app/internal/helpers"
    +

    Index

    +

    Functions

    +

    func GetCertificate

    +
    func GetCertificate() []byte
    +

    GetCertificate - retrieve the elasticsearch cluster certificate. +

    Types

    +

    type Config

    +
    type Config = models.Config
    +

    func LoadConfigs

    +
    func LoadConfigs() Config
    +

    LoadConfigs - parse and store in a struct all the config values needed by the backend. +

    +
    +
    + + Generated with doc2go + +
    + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..c383a9a4 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,69 @@ + + + + + + + + + + + +backend/app/internal + + + +

    Directories

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    controller
    elastic
    embedding
    helpers
    models
    mongodb
    +
    +
    +
    + + Generated with doc2go + +
    + + diff --git a/docs/models/index.html b/docs/models/index.html new file mode 100644 index 00000000..f2eb55ed --- /dev/null +++ b/docs/models/index.html @@ -0,0 +1,192 @@ + + + + + + + + + + + +models + + + +

    package models

    +
    import "backend/app/internal/models"
    +

    Index

    +

    Types

    +

    type BackendConfig

    +
    type BackendConfig struct {
    +	Port int `yaml:"port"`
    +}
    +

    BackendConfig - all backend-related configurations. +

    type Config

    +
    type Config struct {
    +	Backend BackendConfig `yaml:"backend"`
    +	Mongo   MongoConfig   `yaml:"mongodb"`
    +	Elastic ElasticConfig `yaml:"elastic"`
    +}
    +

    Config - used to store all configurations needed by the backend. +

    type ElasticConfig

    +
    type ElasticConfig struct {
    +	Protocol string `yaml:"protocol"`
    +	Host     string `yaml:"host"`
    +	Port     int    `yaml:"port"`
    +	User     string `yaml:"user"`
    +	Password string `yaml:"password"`
    +}
    +

    ElasticConfig - all elasticsearch-related configurations. +

    type EmbeddingRequest

    +
    type EmbeddingRequest struct {
    +	Fragment string `json:"fragment"`
    +}
    +

    EmbeddingRequest - structure of the request to be sent to the embedding server. +

    type EmbeddingResponse

    +
    type EmbeddingResponse struct {
    +	Predictions [][]float32
    +}
    +

    EmbeddingResponse - structure of the response sent back by the embedding server. +

    type EsRequest

    +
    type EsRequest struct {
    +	MongoDocument MongoResponse `json:"metadata"`
    +	Embedding     []float32     `json:"embedding"`
    +}
    +

    EsRequest - structure of an elasticsearch document to be sent to the elasticsearch client. +

    type EsSearchResponse

    +
    type EsSearchResponse struct {
    +	Hits struct {
    +		Hits []Hit `json:"hits"`
    +	} `json:"hits"`
    +}
    +

    EsSearchResponse - structure of the response sent by the elasticsearch client +

    type HTTPError

    +
    type HTTPError struct {
    +	Code    int    `json:"code" example:"400"`
    +	Message string `json:"message" example:"Bad Request"`
    +}
    +

    HTTPError - structure of the error response sent by the backend +

    type Hit

    +
    type Hit struct {
    +	Id       string `json:"_id"`
    +	Index    string `json:"_index"`
    +	Document struct {
    +		MongoId   string    `json:"mongo_id"`
    +		Embedding []float32 `json:"embedding"`
    +	} `json:"_source"`
    +}
    +

    Hit - structure of an elasticsearch document +

    type MongoConfig

    +
    type MongoConfig struct {
    +	Protocol string `yaml:"protocol"`
    +	Host     string `yaml:"host"`
    +	Port     int    `yaml:"port"`
    +	User     string `yaml:"user"`
    +	Password string `yaml:"password"`
    +}
    +

    MongoConfig - all mongodb-related configurations. +

    type MongoResponse

    +
    type MongoResponse struct {
    +	MongoId    string `json:"mongo_id" bson:"_id"`
    +	Name       string `json:"name" bson:"_name"`
    +	ApiId      int    `json:"api_id" bson:"id"`
    +	ApiVersion string `json:"api_version" bson:"_version"`
    +	OASVersion string `json:"oas_version"`
    +	Commits    int    `json:"commits_n" bson:"commits"`
    +	Latest     bool   `json:"is_latest" bson:"latest"`
    +
    +	Specification bson.Raw `json:"-" bson:"api"`
    +	NameAlt       string   `json:"-" bson:"api_title"`
    +	ApiVersionAlt string   `json:"-" bson:"api_version"`
    +}
    +

    MongoResponse - structure of the Mongo document sent by the db +

    func (*MongoResponse) InitObject

    +
    func (b *MongoResponse) InitObject()
    +

    InitObject - function to fix the initiated object +

    type MongoResponseWithApi

    +
    type MongoResponseWithApi struct {
    +	MongoResponse MongoResponse `json:"metadata"`
    +	Specification string        `json:"specification"`
    +}
    +

    MongoResponseWithApi - structure containing both the mongo document and the embedding created by the backend +

    type Specification

    +
    type Specification map[string]interface{}
    +

    Specification - type of the single specification +

    type SpecificationsRequest

    +
    type SpecificationsRequest struct {
    +	Specifications []Specification `json:"specifications"`
    +}
    +

    SpecificationsRequest - structure of the request to be sent to the backend whenever new specifications are added +

    +
    +
    + + Generated with doc2go + +
    + + diff --git a/docs/mongodb/index.html b/docs/mongodb/index.html new file mode 100644 index 00000000..25e8f1d4 --- /dev/null +++ b/docs/mongodb/index.html @@ -0,0 +1,66 @@ + + + + + + + + + + + +mongodb + + + +

    package mongodb

    +
    import "backend/app/internal/mongodb"
    +

    Index

    +

    Functions

    +

    func Connect

    +
    func Connect(config models.MongoConfig) *mongo.Client
    +

    Connect - used to connect to the mongodb database. It will return a mongodb client that can be used to perform +queries on the database. +

    func InsertDocuments

    +
    func InsertDocuments(database *mongo.Database, documents []interface{}, collection string) (*mongo.InsertManyResult, error)
    +

    InsertDocuments - insert documents in a collection based on a query. A mongodb database, a document and a collection +need to be passed to the function. +

    func RetrieveDocument

    +
    func RetrieveDocument(database *mongo.Database, query bson.M, collection string) (bson.Raw, error)
    +

    RetrieveDocument - search a document in a collection based on a query. A mongodb database, a query and a collection +need to be passed to the function. +

    func RetrieveDocuments

    +
    func RetrieveDocuments(database *mongo.Database, query bson.D, collection string) (*mongo.Cursor, error)
    +

    RetrieveDocuments - search documents in a collection based on a query. A mongodb database, a query and a collection +need to be passed to the function. +

    +
    +
    + + Generated with doc2go + +
    + + diff --git a/go.mod b/go.mod index 3894b0f0..77b9499e 100644 --- a/go.mod +++ b/go.mod @@ -5,40 +5,64 @@ go 1.22 require ( github.com/bbalet/stopwords v1.0.0 github.com/dchest/stemmer v0.0.0-20161207102402-66719a20c4b5 - github.com/elastic/go-elasticsearch/v8 v8.12.1 + github.com/elastic/go-elasticsearch/v8 v8.13.1 github.com/gin-gonic/gin v1.9.1 + github.com/goccy/go-json v0.10.2 github.com/kelseyhightower/envconfig v1.4.0 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + github.com/swaggo/swag v1.16.3 github.com/writeas/go-strip-markdown v2.0.1+incompatible + go.mongodb.org/mongo-driver v1.15.0 gopkg.in/yaml.v2 v2.4.0 - github.com/bytedance/sonic v1.11.2 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect - github.com/elastic/elastic-transport-go/v8 v8.4.0 // indirect +) + +require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/bytedance/sonic v1.11.5 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.3 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/elastic/elastic-transport-go/v8 v8.5.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.19.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/crypto v0.20.0 // indirect - golang.org/x/net v0.21.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/protobuf v1.32.0 // indirect + golang.org/x/tools v0.19.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index afdbdbba..4a91ef4a 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,41 @@ +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo= github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= -github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.4 h1:8+OMLSSDDm2/qJc6ld5K5Sm62NK9VHcUKk0NzBoMAM4= +github.com/bytedance/sonic v1.11.4/go.mod h1:YrWEqYtlBPS6LUA0vpuG79a1trsh4Ae41uWUWUreHhE= +github.com/bytedance/sonic v1.11.5 h1:G00FYjjqll5iQ1PYXynbg/hyzqBqavH8Mo9/oTopd9k= +github.com/bytedance/sonic v1.11.5/go.mod h1:X2PC2giUdj/Cv2lliWFLk6c/DUQok5rViJSemeB0wDw= +github.com/bytedance/sonic/loader v0.1.0 h1:skjHJ2Bi9ibbq3Dwzh1w42MQ7wZJrXmEZr/uqUn3f0Q= +github.com/bytedance/sonic/loader v0.1.0/go.mod h1:UmRT+IRTGKz/DAkzcEGzyVqQFJ7H9BqwBO3pm9H/+HY= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= -github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/cloudwego/base64x v0.1.0 h1:Tg5q9tq1khq9Y9UwfoC6zkHK0FypN2GLDvhqFceOL8U= +github.com/cloudwego/base64x v0.1.0/go.mod h1:lM8nFiNbg74QgesNo6EAtv8N9tlRjBWExmHoNDa3PkU= +github.com/cloudwego/base64x v0.1.3 h1:b5J/l8xolB7dyDTTmhJP2oTs5LdrjyrUFuNxdfq5hAg= +github.com/cloudwego/base64x v0.1.3/go.mod h1:1+1K5BUHIQzyapgpF7LwvOGAEDicKtt1umPV+aN8pi8= +github.com/cloudwego/iasm v0.0.9 h1:DgNtfPjuz3YAQ0hmmiGg6DkDGj+foARFSwu7vKFPT1o= +github.com/cloudwego/iasm v0.0.9/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/stemmer v0.0.0-20161207102402-66719a20c4b5 h1:Y8zPZQaUm5jRBMBbvSoPbQa8HCCORmJ6tkkyvvgNucM= github.com/dchest/stemmer v0.0.0-20161207102402-66719a20c4b5/go.mod h1:19PoDJeUsXOb2qtHJB7Az1NI0hlRe5wQM77Vo7rbUY8= -github.com/elastic/elastic-transport-go/v8 v8.4.0 h1:EKYiH8CHd33BmMna2Bos1rDNMM89+hdgcymI+KzJCGE= -github.com/elastic/elastic-transport-go/v8 v8.4.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk= -github.com/elastic/go-elasticsearch/v8 v8.12.1 h1:QcuFK5LaZS0pSIj/eAEsxmJWmMo7tUs1aVBbzdIgtnE= -github.com/elastic/go-elasticsearch/v8 v8.12.1/go.mod h1:wSzJYrrKPZQ8qPuqAqc6KMR4HrBfHnZORvyL+FMFqq0= +github.com/elastic/elastic-transport-go/v8 v8.5.0 h1:v5membAl7lvQgBTexPRDBO/RdnlQX+FM9fUVDyXxvH0= +github.com/elastic/elastic-transport-go/v8 v8.5.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk= +github.com/elastic/go-elasticsearch/v8 v8.13.1 h1:du5F8IzUUyCkzxyHdrO9AtopcG95I/qwi2WK8Kf1xlg= +github.com/elastic/go-elasticsearch/v8 v8.13.1/go.mod h1:DIn7HopJs4oZC/w0WoJR13uMUxtHeq92eI5bqv5CRfI= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= @@ -30,6 +45,16 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -38,17 +63,31 @@ github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -56,46 +95,116 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= +github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= +github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw= github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= +go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= -golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scripts/evaluation/data/ground-truth.txt b/scripts/evaluation/data/ground-truth.txt new file mode 100644 index 00000000..dceb0f58 --- /dev/null +++ b/scripts/evaluation/data/ground-truth.txt @@ -0,0 +1,11 @@ +#1 weather forecast service [4 injected specifications] + - 663a01d3b43474523aefbef6 (Naval Weather Forecast Service API) - ChatGPT + - 663a01d3b43474523aefbef5 (Random Country Meteorological Service API) - ChatGPT + - 663a0b2eb43474523aefbef8 (Xweather OpenAI Plugin) + - 663a0575b43474523aefbef7 (weather.gov API) + +#2 american sports news articles [4 injected specifications] + - 663caaaf76481aa0487cfe1c (NFL News API) - ChatGPT + - 663cac6776481aa0487cfe1d (NBA News and Rankings API) - ChatGPT + - 663cb06576481aa0487cfe1e (Sports News Service API) - ChatGPT + - 663deba076481aa0487cfe1f (American Motorsports API) - ChatGPT diff --git a/scripts/evaluation/data/specs/bookings/booking-1.json b/scripts/evaluation/data/specs/bookings/booking-1.json new file mode 100644 index 00000000..bbda24f7 --- /dev/null +++ b/scripts/evaluation/data/specs/bookings/booking-1.json @@ -0,0 +1,310 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Restaurant Booking Platform API", + "description": "API for managing restaurant bookings", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://api.restaurantbookingplatform.com/v1" + } + ], + "paths": { + "/restaurants": { + "get": { + "summary": "Get all restaurants", + "description": "Returns a list of all available restaurants", + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Restaurant" + } + } + } + } + } + } + } + }, + "/restaurants/{id}/bookings": { + "get": { + "summary": "Get restaurant bookings", + "description": "Returns a list of bookings for a specific restaurant", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the restaurant", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Booking" + } + } + } + } + }, + "404": { + "description": "Restaurant not found" + } + } + }, + "post": { + "summary": "Create a booking", + "description": "Allows users to create a booking for a specific restaurant", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the restaurant", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingInput" + } + } + } + }, + "responses": { + "201": { + "description": "Booking created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + "404": { + "description": "Restaurant not found" + } + } + } + }, + "/bookings/{id}": { + "get": { + "summary": "Get booking by ID", + "description": "Returns details of a specific booking", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the booking", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + "404": { + "description": "Booking not found" + } + } + }, + "put": { + "summary": "Update booking by ID", + "description": "Allows users to update details of a specific booking", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the booking to update", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingInput" + } + } + } + }, + "responses": { + "200": { + "description": "Booking updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + "404": { + "description": "Booking not found" + } + } + }, + "delete": { + "summary": "Delete booking by ID", + "description": "Allows users to delete a specific booking", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the booking to delete", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Booking deleted successfully" + }, + "404": { + "description": "Booking not found" + } + } + } + }, + "/restaurants/search": { + "get": { + "summary": "Search restaurants", + "description": "Searches for restaurants based on given criteria", + "parameters": [ + { + "name": "query", + "in": "query", + "description": "Search query string", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Restaurant" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Restaurant": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Unique identifier for the restaurant" + }, + "name": { + "type": "string", + "description": "Name of the restaurant" + }, + "address": { + "type": "string", + "description": "Address of the restaurant" + }, + "phone": { + "type": "string", + "description": "Contact phone number of the restaurant" + } + }, + "required": ["id", "name", "address", "phone"] + }, + "Booking": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "Unique identifier for the booking" + }, + "restaurant_id": { + "type": "integer", + "description": "ID of the restaurant for which the booking is made" + }, + "customer_name": { + "type": "string", + "description": "Name of the customer who made the booking" + }, + "date": { + "type": "string", + "format": "date-time", + "description": "Date and time of the booking" + }, + "party_size": { + "type": "integer", + "description": "Number of people in the booking party" + } + }, + "required": ["id", "restaurant_id", "customer_name", "date", "party_size"] + }, + "BookingInput": { + "type": "object", + "properties": { + "customer_name": { + "type": "string", + "description": "Name of the customer who is making the booking" + }, + "date": { + "type": "string", + "format": "date-time", + "description": "Desired date and time for the booking" + }, + "party_size": { + "type": "integer", + "description": "Number of people in the booking party" + } + }, + "required": ["customer_name", "date", "party_size"] + } + } + } +} diff --git a/scripts/evaluation/data/specs/bookings/booking-2.json b/scripts/evaluation/data/specs/bookings/booking-2.json new file mode 100644 index 00000000..004ae8bf --- /dev/null +++ b/scripts/evaluation/data/specs/bookings/booking-2.json @@ -0,0 +1,321 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Forky API", + "description": "An API for booking tables at different restaurants", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://api.restaurantbooking.com/v1" + } + ], + "paths": { + "/restaurants": { + "get": { + "summary": "Get a list of restaurants", + "responses": { + "200": { + "description": "A list of restaurants", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Restaurant" + } + } + } + } + } + } + } + }, + "/restaurants/{restaurantId}": { + "get": { + "summary": "Get details of a restaurant by ID", + "parameters": [ + { + "name": "restaurantId", + "in": "path", + "required": true, + "description": "ID of the restaurant", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Details of the restaurant", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Restaurant" + } + } + } + } + } + } + }, + "/restaurants/{restaurantId}/tables": { + "get": { + "summary": "Get available tables at a restaurant", + "parameters": [ + { + "name": "restaurantId", + "in": "path", + "required": true, + "description": "ID of the restaurant", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of available tables", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Table" + } + } + } + } + } + } + } + }, + "/restaurants/{restaurantId}/tables/{tableId}/bookings": { + "get": { + "summary": "Get bookings for a specific table", + "parameters": [ + { + "name": "restaurantId", + "in": "path", + "required": true, + "description": "ID of the restaurant", + "schema": { + "type": "string" + } + }, + { + "name": "tableId", + "in": "path", + "required": true, + "description": "ID of the table", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Bookings for the table", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Booking" + } + } + } + } + } + } + } + }, + "/bookings/{id}": { + "get": { + "summary": "Get booking by ID", + "description": "Returns details of a specific booking", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the booking", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + "404": { + "description": "Booking not found" + } + } + }, + "put": { + "summary": "Update booking by ID", + "description": "Allows users to update details of a specific booking", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the booking to update", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": {} + } + }, + "responses": { + "200": { + "description": "Booking updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Booking" + } + } + } + }, + "404": { + "description": "Booking not found" + } + } + }, + "delete": { + "summary": "Delete booking by ID", + "description": "Allows users to delete a specific booking", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the booking to delete", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Booking deleted successfully" + }, + "404": { + "description": "Booking not found" + } + } + } + } + }, + "components": { + "schemas": { + "Restaurant": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "location": { + "type": "object", + "properties": { + "latitude": { + "type": "number" + }, + "longitude": { + "type": "number" + } + } + } + } + }, + "Table": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "capacity": { + "type": "integer" + }, + "isAvailable": { + "type": "boolean" + }, + "restaurantId": { + "type": "string" + } + } + }, + "Booking": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "restaurantId": { + "type": "string" + }, + "tableId": { + "type": "string" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "numberOfGuests": { + "type": "integer" + }, + "customerName": { + "type": "string" + }, + "customerEmail": { + "type": "string", + "format": "email" + } + } + }, + "BookingRequest": { + "type": "object", + "properties": { + "restaurantId": { + "type": "string" + }, + "tableId": { + "type": "string" + }, + "date": { + "type": "string", + "format": "date-time" + }, + "numberOfGuests": { + "type": "integer" + }, + "customerName": { + "type": "string" + }, + "customerEmail": { + "type": "string", + "format": "email" + } + } + } + } + } +} \ No newline at end of file diff --git a/scripts/evaluation/data/specs/bookings/booking-3.json b/scripts/evaluation/data/specs/bookings/booking-3.json new file mode 100644 index 00000000..b3c9d684 --- /dev/null +++ b/scripts/evaluation/data/specs/bookings/booking-3.json @@ -0,0 +1,356 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Restaurant API - OpenAPI 3.0", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://127.0.0.1:8000/api" + } + ], + "paths": { + "/orders": { + "post": { + "summary": "Create an order.", + "requestBody": { + "description": "Create an order on provided restaurant point with provided order ID", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "order_id": { + "type": "integer" + }, + "point_id": { + "type": "integer" + }, + "data": { + "type": "object" + } + }, + "example": { + "order_id": { + "$numberInt": "1" + }, + "point_id": { + "$numberInt": "1" + }, + "data": { + "burger": { + "$numberInt": "1" + }, + "hotdog": { + "$numberInt": "2" + } + } + } + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Order is already created" + }, + "201": { + "description": "Order created" + }, + "400": { + "description": "Order data is incorrect or printers are missing on the provided point" + }, + "405": { + "description": "Method not allowed" + } + } + } + }, + "/points/{point_id}/kitchen-print": { + "post": { + "summary": "Print rendered kitchen checks on provided point", + "parameters": [ + { + "name": "point_id", + "in": "path", + "description": "Restaurant point ID", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "There are no unprinted checks" + }, + "201": { + "description": "Checks are printed", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "/media/pdf/12345_kitchen", + "/media/pdf/54321_kitchen" + ] + } + } + } + }, + "405": { + "description": "Method not allowed" + } + } + } + }, + "/points/{point_id}/client-print": { + "post": { + "summary": "Print rendered client checks on provided point with provided order ID", + "parameters": [ + { + "name": "point_id", + "in": "path", + "description": "Restaurant point ID", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "requestBody": { + "description": "Provide order ID", + "content": { + "application/json": { + "schema": { + "type": "integer", + "example": { + "$numberInt": "12345" + } + } + } + } + }, + "responses": { + "200": { + "description": "There is no unprinted check with provided order id" + }, + "201": { + "description": "Check is printed", + "content": { + "application/json": { + "schema": { + "type": "string", + "example": "/media/pdf/12345_kitchen" + } + } + } + }, + "405": { + "description": "Method not allowed" + } + } + } + }, + "/table-booking": { + "post": { + "summary": "Create a table booking.", + "requestBody": { + "description": "Create a table booking for a specific restaurant point.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "point_id": { + "type": "integer" + }, + "booking_time": { + "type": "string", + "format": "date-time" + }, + "party_size": { + "type": "integer" + }, + "customer_name": { + "type": "string" + }, + "contact_number": { + "type": "string" + } + }, + "required": ["point_id", "booking_time", "party_size", "customer_name", "contact_number"] + } + } + } + }, + "responses": { + "201": { + "description": "Table booking created successfully." + }, + "400": { + "description": "Invalid data provided for table booking." + }, + "405": { + "description": "Method not allowed" + } + } + } + }, + "/table-booking/{booking_id}": { + "get": { + "summary": "Get details of a table booking.", + "parameters": [ + { + "name": "booking_id", + "in": "path", + "description": "ID of the table booking", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Table booking details retrieved successfully." + }, + "404": { + "description": "Table booking not found." + }, + "405": { + "description": "Method not allowed" + } + } + }, + "delete": { + "summary": "Cancel a table booking.", + "parameters": [ + { + "name": "booking_id", + "in": "path", + "description": "ID of the table booking", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Table booking cancelled successfully." + }, + "404": { + "description": "Table booking not found." + }, + "405": { + "description": "Method not allowed" + } + } + } + }, + "/seat-booking": { + "post": { + "summary": "Create a table booking.", + "requestBody": { + "description": "Create a table booking for a specific restaurant point.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "point_id": { + "type": "integer" + }, + "booking_time": { + "type": "string", + "format": "date-time" + }, + "party_size": { + "type": "integer" + }, + "customer_name": { + "type": "string" + }, + "contact_number": { + "type": "string" + } + }, + "required": ["point_id", "booking_time", "party_size", "customer_name", "contact_number"] + } + } + } + }, + "responses": { + "201": { + "description": "Table booking created successfully." + }, + "400": { + "description": "Invalid data provided for table booking." + }, + "405": { + "description": "Method not allowed" + } + } + } + }, + "/seat-booking/{booking_id}": { + "get": { + "summary": "Get details of a seat booking.", + "parameters": [ + { + "name": "booking_id", + "in": "path", + "description": "ID of the seat booking", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Table booking details retrieved successfully." + }, + "404": { + "description": "Table booking not found." + }, + "405": { + "description": "Method not allowed" + } + } + }, + "delete": { + "summary": "Cancel a seat booking.", + "parameters": [ + { + "name": "booking_id", + "in": "path", + "description": "ID of the seat booking", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Seat booking cancelled successfully." + }, + "404": { + "description": "Seat booking not found." + }, + "405": { + "description": "Method not allowed" + } + } + } + } + } +} \ No newline at end of file diff --git a/scripts/evaluation/data/specs/bookings/booking-4.json b/scripts/evaluation/data/specs/bookings/booking-4.json new file mode 100644 index 00000000..43437398 --- /dev/null +++ b/scripts/evaluation/data/specs/bookings/booking-4.json @@ -0,0 +1,161 @@ +{ + "info": { + "title": "Online Booking API", + "version": "1.0.0" + }, + "openapi": "3.0.0", + "paths": { + "/bookings": { + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "date": { + "description": "Appointment date.", + "format": "date", + "type": "string" + }, + "customer": { + "properties": { + "email": { + "description": "Customer email address.", + "format": "email", + "type": "string" + }, + "name": { + "description": "Customer name.", + "type": "string" + }, + "phone_number": { + "description": "Customer phone number.", + "type": "string" + } + }, + "type": "object" + }, + "service_id": { + "description": "ID of the restaurant to be booked.", + "type": "integer" + }, + "time": { + "format": "time", + "type": "string", + "description": "Booking time." + } + }, + "type": "object" + } + } + }, + "required": true + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "description": "Unique appointment ID.", + "type": "integer" + }, + "message": { + "description": "Confirmation message.", + "type": "string" + } + }, + "type": "object" + } + } + }, + "description": "Booking created successfully." + } + }, + "summary": "Create a new booking." + }, + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "type": "object", + "properties": { + "date": { + "description": "Booking date.", + "format": "date", + "type": "string" + }, + "id": { + "description": "Unique appointment ID.", + "type": "integer" + }, + "restaurant": { + "properties": { + "duration": { + "description": "Restaurant duration in minutes.", + "type": "integer" + }, + "id": { + "description": "Restaurant ID.", + "type": "integer" + }, + "name": { + "description": "Restaurant name.", + "type": "string" + } + }, + "type": "object" + }, + "service_provider": { + "properties": { + "name": { + "description": "Restaurant name.", + "type": "string" + }, + "id": { + "description": "Restaurant ID.", + "type": "integer" + } + }, + "type": "object" + }, + "time": { + "format": "time", + "type": "string", + "description": "Appointment time." + } + } + }, + "type": "array" + } + } + }, + "description": "List of available booking." + } + }, + "summary": "Retrieve available booking for a specific date and a specific restaurant.", + "parameters": [ + { + "description": "Date for booking (YYYY-MM-DD format).", + "in": "query", + "name": "date", + "required": true, + "schema": { + "format": "date", + "type": "string" + } + }, + { + "description": "ID of the specific restaurant.", + "in": "query", + "name": "restaurant_id" + } + ] + } + } + } +} \ No newline at end of file diff --git a/scripts/evaluation/data/specs/books/books-1.json b/scripts/evaluation/data/specs/books/books-1.json new file mode 100644 index 00000000..8ff68881 --- /dev/null +++ b/scripts/evaluation/data/specs/books/books-1.json @@ -0,0 +1,349 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Digital Library API", + "version": "1.0.0", + "description": "API for managing digital library resources" + }, + "servers": [ + { + "url": "https://api.examplelibrary.com/v1" + } + ], + "paths": { + "/books": { + "get": { + "summary": "Get a list of books", + "responses": { + "200": { + "description": "List of books", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } + }, + "post": { + "summary": "Add a new book", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookInput" + } + } + } + }, + "responses": { + "201": { + "description": "Book added successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } + }, + "/books/{bookId}": { + "get": { + "summary": "Get details of a specific book", + "parameters": [ + { + "name": "bookId", + "in": "path", + "description": "ID of the book", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Book details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + }, + "put": { + "summary": "Update an existing book", + "parameters": [ + { + "name": "bookId", + "in": "path", + "description": "ID of the book", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookInput" + } + } + } + }, + "responses": { + "200": { + "description": "Book updated successfully" + } + } + }, + "delete": { + "summary": "Delete a book", + "parameters": [ + { + "name": "bookId", + "in": "path", + "description": "ID of the book", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Book deleted successfully" + } + } + } + }, + "/books/{bookId}/borrow": { + "post": { + "summary": "Borrow a book", + "parameters": [ + { + "name": "bookId", + "in": "path", + "description": "ID of the book", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Book borrowed successfully" + } + } + } + }, + "/books/{bookId}/return": { + "post": { + "summary": "Return a borrowed book", + "parameters": [ + { + "name": "bookId", + "in": "path", + "description": "ID of the book", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Book returned successfully" + } + } + } + }, + "/authors": { + "get": { + "summary": "Get a list of authors", + "responses": { + "200": { + "description": "List of authors", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Author" + } + } + } + } + } + } + } + }, + "/authors/{authorId}/books": { + "get": { + "summary": "Get books by a specific author", + "parameters": [ + { + "name": "authorId", + "in": "path", + "description": "ID of the author", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "List of books by the author", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } + } + }, + "/publishers": { + "get": { + "summary": "Get a list of publishers", + "responses": { + "200": { + "description": "List of publishers", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Publisher" + } + } + } + } + } + } + } + }, + "/publishers/{publisherId}/books": { + "get": { + "summary": "Get books by a specific publisher", + "parameters": [ + { + "name": "publisherId", + "in": "path", + "description": "ID of the publisher", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "List of books by the publisher", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "BookInput": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "author": { + "type": "string" + }, + "published_year": { + "type": "integer" + } + } + }, + "Book": { + "type": "object", + "properties": { + "bookId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "author": { + "type": "string" + }, + "published_year": { + "type": "integer" + } + } + }, + "Author": { + "type": "object", + "properties": { + "authorId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "books": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Book" + } + } + } + }, + "Publisher": { + "type": "object", + "properties": { + "publisherId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "books": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/scripts/evaluation/data/specs/books/books-2.json b/scripts/evaluation/data/specs/books/books-2.json new file mode 100644 index 00000000..5bbaabb2 --- /dev/null +++ b/scripts/evaluation/data/specs/books/books-2.json @@ -0,0 +1,506 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "eBook Store API", + "version": "1.0.0", + "description": "API for managing eBooks, authors, publishers, and user authentication" + }, + "servers": [ + { + "url": "https://api.ebookstore.com/v1" + } + ], + "paths": { + "/ebooks": { + "get": { + "summary": "Get a list of eBooks", + "responses": { + "200": { + "description": "List of eBooks", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Ebook" + } + } + } + } + } + } + }, + "post": { + "summary": "Add a new eBook", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewEbook" + } + } + } + }, + "responses": { + "201": { + "description": "eBook added successfully" + } + } + } + }, + "/ebooks/{ebookId}": { + "get": { + "summary": "Get details of a specific eBook", + "parameters": [ + { + "name": "ebookId", + "in": "path", + "description": "ID of the eBook", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "eBook details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Ebook" + } + } + } + } + } + }, + "put": { + "summary": "Update an existing eBook", + "parameters": [ + { + "name": "ebookId", + "in": "path", + "description": "ID of the eBook", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EbookUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "eBook updated successfully" + } + } + }, + "delete": { + "summary": "Delete an eBook", + "parameters": [ + { + "name": "ebookId", + "in": "path", + "description": "ID of the eBook", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "eBook deleted successfully" + } + } + } + }, + "/authors": { + "get": { + "summary": "Get a list of authors", + "responses": { + "200": { + "description": "List of authors", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Author" + } + } + } + } + } + } + }, + "post": { + "summary": "Add a new author", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewAuthor" + } + } + } + }, + "responses": { + "201": { + "description": "Author added successfully" + } + } + } + }, + "/authors/{authorId}": { + "get": { + "summary": "Get details of a specific author", + "parameters": [ + { + "name": "authorId", + "in": "path", + "description": "ID of the author", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Author details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Author" + } + } + } + } + } + }, + "put": { + "summary": "Update an existing author", + "parameters": [ + { + "name": "authorId", + "in": "path", + "description": "ID of the author", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuthorUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Author updated successfully" + } + } + }, + "delete": { + "summary": "Delete an author", + "parameters": [ + { + "name": "authorId", + "in": "path", + "description": "ID of the author", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Author deleted successfully" + } + } + } + }, + "/publishers": { + "get": { + "summary": "Get a list of publishers", + "responses": { + "200": { + "description": "List of publishers", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Publisher" + } + } + } + } + } + } + }, + "post": { + "summary": "Add a new publisher", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewPublisher" + } + } + } + }, + "responses": { + "201": { + "description": "Publisher added successfully" + } + } + } + }, + "/publishers/{publisherId}": { + "get": { + "summary": "Get details of a specific publisher", + "parameters": [ + { + "name": "publisherId", + "in": "path", + "description": "ID of the publisher", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Publisher details", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Publisher" + } + } + } + } + } + }, + "put": { + "summary": "Update an existing publisher", + "parameters": [ + { + "name": "publisherId", + "in": "path", + "description": "ID of the publisher", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublisherUpdate" + } + } + } + }, + "responses": { + "200": { + "description": "Publisher updated successfully" + } + } + }, + "delete": { + "summary": "Delete a publisher", + "parameters": [ + { + "name": "publisherId", + "in": "path", + "description": "ID of the publisher", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Publisher deleted successfully" + } + } + } + }, + "/login": { + "post": { + "summary": "Authenticate user", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserLogin" + } + } + } + }, + "responses": { + "200": { + "description": "Authentication successful" + }, + "401": { + "description": "Unauthorized" + } + } + } + } + }, + "components": { + "schemas": { + "Ebook": { + "type": "object", + "properties": { + "ebookId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "published_year": { + "type": "integer" + }, + "price": { + "type": "number" + } + } + }, + "NewEbook": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "author": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "published_year": { + "type": "integer" + }, + "price": { + "type": "number" + } + } + }, + "EbookUpdate": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "published_year": { + "type": "integer" + }, + "price": { + "type": "number" + } + } + }, + "Author": { + "type": "object", + "properties": { + "authorId": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "NewAuthor": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "AuthorUpdate": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "Publisher": { + "type": "object", + "properties": { + "publisherId": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "NewPublisher": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "PublisherUpdate": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "UserLogin": { + "type": "object", + "properties": { + "username": { + "type": "string" + }, + "password": { + "type": "string" + } + } + } + } + } +} diff --git a/scripts/evaluation/data/specs/books/books-3.json b/scripts/evaluation/data/specs/books/books-3.json new file mode 100644 index 00000000..a1e62b5c --- /dev/null +++ b/scripts/evaluation/data/specs/books/books-3.json @@ -0,0 +1,341 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Your Personal Bookshelf", + "description": "An API for managing a digital library with endpoints for different book categories", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost:8000/api/v1", + "description": "Local development server" + } + ], + "paths": { + "/books/{category}": { + "get": { + "summary": "Get books by category", + "description": "Returns a list of books belonging to a specified category.", + "parameters": [ + { + "name": "category", + "in": "path", + "description": "The category of books to retrieve", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A list of books belonging to the specified category", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } + } + }, + "/books": { + "get": { + "summary": "Get all books", + "description": "Returns a list of all books available in the library.", + "responses": { + "200": { + "description": "A list of all books available in the library", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } + } + }, + "/books/{id}": { + "get": { + "summary": "Get book by ID", + "description": "Returns detailed information about a book based on its unique ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "The ID of the book to retrieve", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "Detailed information about the requested book", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Book" + } + } + } + } + } + } + }, + "/bookmarks": { + "get": { + "summary": "List all bookmarks", + "description": "Returns a list of all bookmarks saved for books in the library.", + "responses": { + "200": { + "description": "A list of all bookmarks saved for books in the library", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Bookmark" + } + } + } + } + } + } + }, + "post": { + "summary": "Save bookmark for a book", + "description": "Creates a bookmark for a specific book.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookmarkInput" + } + } + } + }, + "responses": { + "201": { + "description": "Bookmark created successfully" + } + } + }, + "put": { + "summary": "Modify bookmark", + "description": "Modifies an existing bookmark.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookmarkInput" + } + } + } + }, + "responses": { + "200": { + "description": "Bookmark modified successfully" + } + } + }, + "delete": { + "summary": "Delete bookmark", + "description": "Deletes a bookmark for a specific book.", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "The ID of the bookmark to delete", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Bookmark deleted successfully" + } + } + } + }, + "/notes": { + "get": { + "summary": "List all notes", + "description": "Returns a list of all notes saved for books in the library.", + "responses": { + "200": { + "description": "A list of all notes saved for books in the library", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Notes" + } + } + } + } + } + } + }, + "post": { + "summary": "Save notes for a book", + "description": "Creates notes for a specific book.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotesInput" + } + } + } + }, + "responses": { + "201": { + "description": "Notes created successfully" + } + } + }, + "put": { + "summary": "Modify notes", + "description": "Modifies existing notes.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotesInput" + } + } + } + }, + "responses": { + "200": { + "description": "Notes modified successfully" + } + } + }, + "delete": { + "summary": "Delete notes", + "description": "Deletes notes for a specific book.", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "The ID of the notes to delete", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "responses": { + "204": { + "description": "Notes deleted successfully" + } + } + } + } + }, + "components": { + "schemas": { + "Book": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The unique identifier of the book" + }, + "title": { + "type": "string", + "description": "The title of the book" + }, + "author": { + "type": "string", + "description": "The author of the book" + }, + "category": { + "type": "string", + "description": "The category of the book" + } + } + }, + "Bookmark": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The unique identifier of the bookmark" + }, + "bookId": { + "type": "integer", + "description": "The ID of the book for which the bookmark is saved" + }, + "page": { + "type": "integer", + "description": "The page number of the bookmark" + } + } + }, + "Notes": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "description": "The unique identifier of the notes" + }, + "bookId": { + "type": "integer", + "description": "The ID of the book for which the notes are saved" + }, + "content": { + "type": "string", + "description": "The content of the notes" + } + } + }, + "BookmarkInput": { + "type": "object", + "properties": { + "bookId": { + "type": "integer", + "description": "The ID of the book for which the bookmark is being saved" + }, + "page": { + "type": "integer", + "description": "The page number of the bookmark" + } + } + }, + "NotesInput": { + "type": "object", + "properties": { + "bookId": { + "type": "integer", + "description": "The ID of the book for which the notes are being saved" + }, + "content": { + "type": "string", + "description": "The content of the notes" + } + } + } + } + } +} diff --git a/scripts/evaluation/data/specs/books/books-4.json b/scripts/evaluation/data/specs/books/books-4.json new file mode 100644 index 00000000..a5ce815c --- /dev/null +++ b/scripts/evaluation/data/specs/books/books-4.json @@ -0,0 +1,753 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "USI Books Microservice", + "description": "Java + MySQL", + "license": { + "name": "Software Architecture 2018-II" + } + }, + "host": "192.168.0.10:3002", + "basePath": "/books-ms", + "schemes": [ + "http" + ], + "consumes": [ + "application/json", + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "paths": { + "/books": { + "post": { + "summary": "Creates a new book", + "operationId": "createBook", + "parameters": [ + { + "name": "book", + "in": "body", + "required": true, + "description": "Book Object", + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "publisher": { + "type": "string" + }, + "title": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "cover": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error" + } + } + }, + "get": { + "summary": "Returns all books", + "operationId": "findAllBooks", + "responses": { + "200": { + "description": "Ok", + "schema": { + "type": "object", + "required": [ + "list" + ], + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "publisher": { + "type": "string" + }, + "title": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "cover": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/books/title/{book_title}": { + "get": { + "summary": "Returns books with the given title", + "operationId": "findBookByTitle", + "parameters": [ + { + "name": "book_title", + "in": "path", + "description": "The title of the book to be returned", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "Ok", + "schema": { + "type": "object", + "required": [ + "list" + ], + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "publisher": { + "type": "string" + }, + "title": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "cover": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/books/{book_id}": { + "get": { + "summary": "Returns a book with the given id", + "operationId": "findBookById", + "parameters": [ + { + "name": "book_id", + "in": "path", + "description": "The id of the book to be returned", + "required": true, + "type": "number" + } + ], + "responses": { + "200": { + "description": "Ok", + "schema": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "publisher": { + "type": "string" + }, + "title": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "cover": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error" + } + } + }, + "put": { + "summary": "Updates a book with the given id", + "operationId": "updateBook", + "parameters": [ + { + "name": "book_id", + "in": "path", + "description": "The id of the Book to be updated", + "required": true, + "type": "number" + }, + { + "name": "Book", + "in": "body", + "required": true, + "description": "Book Object", + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + ], + "responses": { + "200": { + "description": "Ok", + "schema": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "publisher": { + "type": "string" + }, + "title": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "cover": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + } + } + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error" + } + } + }, + "delete": { + "summary": "Deletes a book with the given id", + "operationId": "deleteBook", + "parameters": [ + { + "name": "book_id", + "in": "path", + "description": "The id of the book to be deleted", + "required": true, + "type": "number" + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/file/download/{file_id}": { + "get": { + "summary": "Download file with the given id", + "operationId": "downloadFile", + "parameters": [ + { + "name": "file_id", + "in": "path", + "description": "The id of the book", + "required": true, + "type": "number" + } + ], + "responses": { + "200": { + "description": "Ok" + }, + "404": { + "description": "Not Found" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/file/book/{book_id}": { + "post": { + "summary": "Uploads cover of a book with given id", + "operationId": "uploadBookCover", + "parameters": [ + { + "name": "book_id", + "in": "path", + "description": "The id of the book", + "required": true, + "type": "number" + }, + { + "name": "file", + "in": "formData", + "description": "cover to upload", + "required": true, + "type": "file" + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + }, + "404": { + "description": "Not Found" + }, + "409": { + "description": "Conflict" + }, + "500": { + "description": "Internal Server Error" + } + } + } + } + }, + "definitions": { + "File": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + }, + "Book": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "publisher": { + "type": "string" + }, + "title": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "cover": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + } + }, + "BookInput": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "publisher": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "BooksList": { + "type": "object", + "required": [ + "list" + ], + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "publisher": { + "type": "string" + }, + "title": { + "type": "string" + }, + "numPages": { + "type": "number" + }, + "isbn": { + "type": "string" + }, + "plot": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "genres": { + "type": "array", + "items": { + "type": "string" + } + }, + "cover": { + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "fileType": { + "type": "string" + }, + "fileDownloadUri": { + "type": "string" + }, + "size": { + "type": "number" + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/scripts/evaluation/data/specs/sports-news/sports-news-1.json b/scripts/evaluation/data/specs/sports-news/sports-news-1.json new file mode 100644 index 00000000..30e7149b --- /dev/null +++ b/scripts/evaluation/data/specs/sports-news/sports-news-1.json @@ -0,0 +1,227 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "NFL News API", + "description": "API for accessing the american NFL news articles", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://nfl-news-api.com/api/v1" + } + ], + "paths": { + "/articles": { + "get": { + "summary": "Get NFL news articles", + "description": "Returns a list of NFL news articles.", + "responses": { + "200": { + "description": "Successful response with NFL news articles", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Article ID" + }, + "title": { + "type": "string", + "description": "Article title" + }, + "author": { + "type": "string", + "description": "Author of the article" + }, + "content": { + "type": "string", + "description": "Article content" + }, + "published_at": { + "type": "string", + "format": "date-time", + "description": "Date and time when the article was published" + } + } + } + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Articles" + ] + } + }, + "/articles/{id}": { + "get": { + "summary": "Get specific NFL news article", + "description": "Returns details of a specific NFL news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "Article ID", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response with NFL news article", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Article ID" + }, + "title": { + "type": "string", + "description": "Article title" + }, + "author": { + "type": "string", + "description": "Author of the article" + }, + "content": { + "type": "string", + "description": "Article content" + }, + "published_at": { + "type": "string", + "format": "date-time", + "description": "Date and time when the article was published" + } + } + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Articles" + ] + } + }, + "/articles/{id}/comments": { + "get": { + "summary": "Get comments for an article", + "description": "Returns comments for a specific NFL news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "Article ID", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successful response with comments", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Comment ID" + }, + "author": { + "type": "string", + "description": "Author of the comment" + }, + "content": { + "type": "string", + "description": "Comment content" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "Date and time when the comment was created" + } + } + } + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Comments" + ] + } + } + }, + "tags": [ + { + "name": "Articles" + }, + { + "name": "Comments" + } + ] +} diff --git a/scripts/evaluation/data/specs/sports-news/sports-news-2.json b/scripts/evaluation/data/specs/sports-news/sports-news-2.json new file mode 100644 index 00000000..05f6fe97 --- /dev/null +++ b/scripts/evaluation/data/specs/sports-news/sports-news-2.json @@ -0,0 +1,219 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "NBA News and Rankings API", + "description": "An API for accessing news articles and rankings for the American NBA league.", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://api.example.com" + } + ], + "paths": { + "/news": { + "get": { + "summary": "Get NBA news articles", + "description": "Retrieve the latest news articles about the NBA.", + "responses": { + "200": { + "description": "A list of NBA news articles", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NewsArticle" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a new NBA news article", + "description": "Submit a new news article about the NBA.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewsArticleInput" + } + } + } + }, + "responses": { + "201": { + "description": "The news article has been created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewsArticle" + } + } + } + } + } + } + }, + "/news/{id}": { + "get": { + "summary": "Get a specific NBA news article", + "description": "Retrieve a specific news article by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The requested NBA news article", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewsArticle" + } + } + } + } + } + }, + "put": { + "summary": "Update an existing NBA news article", + "description": "Update the content of an existing news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewsArticleInput" + } + } + } + }, + "responses": { + "200": { + "description": "The news article has been updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NewsArticle" + } + } + } + } + } + } + }, + "/rankings": { + "get": { + "summary": "Get NBA team rankings", + "description": "Retrieve the current rankings of NBA teams.", + "responses": { + "200": { + "description": "A list of NBA team rankings", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamRanking" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "NewsArticle": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "The unique identifier for the news article." + }, + "title": { + "type": "string", + "description": "The title of the news article." + }, + "content": { + "type": "string", + "description": "The content of the news article." + }, + "author": { + "type": "string", + "description": "The author of the news article." + }, + "published_at": { + "type": "string", + "format": "date-time", + "description": "The date and time when the news article was published." + } + } + }, + "NewsArticleInput": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the news article." + }, + "content": { + "type": "string", + "description": "The content of the news article." + }, + "author": { + "type": "string", + "description": "The author of the news article." + } + }, + "required": ["title", "content"] + }, + "TeamRanking": { + "type": "object", + "properties": { + "team_name": { + "type": "string", + "description": "The name of the NBA team." + }, + "rank": { + "type": "integer", + "description": "The current rank of the NBA team." + }, + "wins": { + "type": "integer", + "description": "The number of wins by the NBA team." + }, + "losses": { + "type": "integer", + "description": "The number of losses by the NBA team." + } + } + } + } + } +} diff --git a/scripts/evaluation/data/specs/sports-news/sports-news-3.json b/scripts/evaluation/data/specs/sports-news/sports-news-3.json new file mode 100644 index 00000000..5622186b --- /dev/null +++ b/scripts/evaluation/data/specs/sports-news/sports-news-3.json @@ -0,0 +1,553 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Sports News Service API", + "description": "An API for accessing news articles across various sports leagues including NFL, NBA, WNBA, NASCAR, etc.", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://api.sportsnews.com" + } + ], + "paths": { + "/news": { + "get": { + "summary": "Get sports news articles", + "description": "Retrieve the latest news articles across all sports leagues.", + "responses": { + "200": { + "description": "A list of sports news articles", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a new sports news article", + "description": "Submit a new news article about any sports league.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "201": { + "description": "The news article has been created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + } + }, + "/news/{id}": { + "get": { + "summary": "Get a specific sports news article", + "description": "Retrieve a specific news article by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The requested sports news article", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + }, + "put": { + "summary": "Update an existing sports news article", + "description": "Update the content of an existing news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "200": { + "description": "The news article has been updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + }, + "delete": { + "summary": "Delete an existing sports news article", + "description": "Delete an existing news article by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The news article has been deleted successfully" + } + } + } + }, + "/nfl/news": { + "post": { + "summary": "Create a new NFL news article", + "description": "Submit a new news article about the NFL.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "201": { + "description": "The news article has been created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + } + }, + "/nfl/news/{id}": { + "put": { + "summary": "Update an existing NFL news article", + "description": "Update the content of an existing NFL news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "200": { + "description": "The news article has been updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + }, + "delete": { + "summary": "Delete an existing NFL news article", + "description": "Delete an existing NFL news article by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The news article has been deleted successfully" + } + } + } + }, + "/nba/news": { + "post": { + "summary": "Create a new NBA news article", + "description": "Submit a new news article about the NBA.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "201": { + "description": "The news article has been created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + } + }, + "/nba/news/{id}": { + "put": { + "summary": "Update an existing NBA news article", + "description": "Update the content of an existing NBA news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "200": { + "description": "The news article has been updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + }, + "delete": { + "summary": "Delete an existing NBA news article", + "description": "Delete an existing NBA news article by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The news article has been deleted successfully" + } + } + } + }, + "/wnba/news": { + "post": { + "summary": "Create a new WNBA news article", + "description": "Submit a new news article about the WNBA.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "201": { + "description": "The news article has been created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + } + }, + "/wnba/news/{id}": { + "put": { + "summary": "Update an existing WNBA news article", + "description": "Update the content of an existing WNBA news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "200": { + "description": "The news article has been updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + }, + "delete": { + "summary": "Delete an existing WNBA news article", + "description": "Delete an existing WNBA news article by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The news article has been deleted successfully" + } + } + } + }, + "/nascar/news": { + "post": { + "summary": "Create a new NASCAR news article", + "description": "Submit a new news article about NASCAR.", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "201": { + "description": "The news article has been created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + } + }, + "/nascar/news/{id}": { + "put": { + "summary": "Update an existing NASCAR news article", + "description": "Update the content of an existing NASCAR news article.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticleInput" + } + } + } + }, + "responses": { + "200": { + "description": "The news article has been updated successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SportsNewsArticle" + } + } + } + } + } + }, + "delete": { + "summary": "Delete an existing NASCAR news article", + "description": "Delete an existing NASCAR news article by its ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "ID of the news article", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "The news article has been deleted successfully" + } + } + } + } + }, + "components": { + "schemas": { + "SportsNewsArticle": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "The unique identifier for the news article." + }, + "title": { + "type": "string", + "description": "The title of the news article." + }, + "content": { + "type": "string", + "description": "The content of the news article." + }, + "author": { + "type": "string", + "description": "The author of the news article." + }, + "published_at": { + "type": "string", + "format": "date-time", + "description": "The date and time when the news article was published." + }, + "sport": { + "type": "string", + "description": "The sports league the news article belongs to." + } + } + }, + "SportsNewsArticleInput": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the news article." + }, + "content": { + "type": "string", + "description": "The content of the news article." + }, + "author": { + "type": "string", + "description": "The author of the news article." + }, + "sport": { + "type": "string", + "description": "The sports league the news article belongs to." + } + }, + "required": ["title", "content", "sport"] + } + } + } +} diff --git a/scripts/evaluation/data/specs/sports-news/sports-news-4.json b/scripts/evaluation/data/specs/sports-news/sports-news-4.json new file mode 100644 index 00000000..060d8d76 --- /dev/null +++ b/scripts/evaluation/data/specs/sports-news/sports-news-4.json @@ -0,0 +1,378 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "American Motorsports API", + "version": "1.0.0", + "description": "API for managing data related to American motorsports such as NASCAR and the Indianapolis 500." + }, + "paths": { + "/races": { + "get": { + "summary": "Get all races", + "responses": { + "200": { + "description": "List of races" + } + } + }, + "post": { + "summary": "Add a new race", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "date": { + "type": "string", + "format": "date" + } + }, + "required": ["name", "location", "date"] + } + } + } + }, + "responses": { + "201": { + "description": "Race created successfully" + } + } + } + }, + "/races/{id}": { + "get": { + "summary": "Get a race by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Race found" + }, + "404": { + "description": "Race not found" + } + } + }, + "put": { + "summary": "Update a race by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "date": { + "type": "string", + "format": "date" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Race updated successfully" + }, + "404": { + "description": "Race not found" + } + } + }, + "delete": { + "summary": "Delete a race by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Race deleted successfully" + }, + "404": { + "description": "Race not found" + } + } + } + }, + "/drivers": { + "get": { + "summary": "Get all drivers", + "responses": { + "200": { + "description": "List of drivers" + } + } + }, + "post": { + "summary": "Add a new driver", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nationality": { + "type": "string" + }, + "team_id": { + "type": "string" + } + }, + "required": ["name", "nationality", "team_id"] + } + } + } + }, + "responses": { + "201": { + "description": "Driver created successfully" + } + } + } + }, + "/drivers/{id}": { + "get": { + "summary": "Get a driver by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Driver found" + }, + "404": { + "description": "Driver not found" + } + } + }, + "put": { + "summary": "Update a driver by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "nationality": { + "type": "string" + }, + "team_id": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Driver updated successfully" + }, + "404": { + "description": "Driver not found" + } + } + }, + "delete": { + "summary": "Delete a driver by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Driver deleted successfully" + }, + "404": { + "description": "Driver not found" + } + } + } + }, + "/teams": { + "get": { + "summary": "Get all teams", + "responses": { + "200": { + "description": "List of teams" + } + } + }, + "post": { + "summary": "Add a new team", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "country": { + "type": "string" + } + }, + "required": ["name", "country"] + } + } + } + }, + "responses": { + "201": { + "description": "Team created successfully" + } + } + } + }, + "/teams/{id}": { + "get": { + "summary": "Get a team by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Team found" + }, + "404": { + "description": "Team not found" + } + } + }, + "put": { + "summary": "Update a team by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "country": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Team updated successfully" + }, + "404": { + "description": "Team not found" + } + } + }, + "delete": { + "summary": "Delete a team by ID", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "Team deleted successfully" + }, + "404": { + "description": "Team not found" + } + } + } + } + } +} diff --git a/scripts/evaluation/data/specs/weather/weather-1.json b/scripts/evaluation/data/specs/weather/weather-1.json new file mode 100644 index 00000000..84c7658d --- /dev/null +++ b/scripts/evaluation/data/specs/weather/weather-1.json @@ -0,0 +1,187 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Random Country Meteorological Service API", + "description": "API for accessing daily weather forecasts and current weather conditions for a random country", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://random-country-meteorological-service.com/api/v1" + } + ], + "paths": { + "/forecast/{country_code}": { + "parameters": [ + { + "name": "country_code", + "in": "path", + "required": true, + "description": "ISO 3166-1 alpha-2 country code (e.g., US for United States)", + "schema": { + "type": "string" + } + } + ], + "get": { + "summary": "Get daily weather forecast for a specific country", + "description": "Returns daily weather forecast for the specified country.", + "responses": { + "200": { + "description": "Successful response with daily weather forecast", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "country": { + "type": "string", + "description": "Name of the country" + }, + "forecasts": { + "type": "array", + "description": "List of daily weather forecasts", + "items": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date", + "description": "Date of the forecast" + }, + "temperature": { + "type": "object", + "description": "Temperature details", + "properties": { + "min": { + "type": "number", + "format": "float", + "description": "Minimum temperature in Celsius" + }, + "max": { + "type": "number", + "format": "float", + "description": "Maximum temperature in Celsius" + } + } + }, + "weather": { + "type": "string", + "description": "Weather condition for the day" + } + } + } + } + } + } + } + } + }, + "400": { + "description": "Invalid country code provided", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Forecast" + ] + } + }, + "/current/{country_code}/{city}": { + "parameters": [ + { + "name": "country_code", + "in": "path", + "required": true, + "description": "ISO 3166-1 alpha-2 country code (e.g., US for United States)", + "schema": { + "type": "string" + } + }, + { + "name": "city", + "in": "path", + "required": true, + "description": "Name of the city", + "schema": { + "type": "string" + } + } + ], + "get": { + "summary": "Get current weather conditions for a specific city", + "description": "Returns current weather conditions for the specified city within the country.", + "responses": { + "200": { + "description": "Successful response with current weather conditions", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "country": { + "type": "string", + "description": "Name of the country" + }, + "city": { + "type": "string", + "description": "Name of the city" + }, + "temperature": { + "type": "number", + "format": "float", + "description": "Current temperature in Celsius" + }, + "weather": { + "type": "string", + "description": "Current weather condition" + } + } + } + } + } + }, + "400": { + "description": "Invalid country code or city provided", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Current Weather" + ] + } + } + }, + "tags": [ + { + "name": "Forecast" + }, + { + "name": "Current Weather" + } + ] +} \ No newline at end of file diff --git a/scripts/evaluation/data/specs/weather/weather-2.json b/scripts/evaluation/data/specs/weather/weather-2.json new file mode 100644 index 00000000..1c2b69e6 --- /dev/null +++ b/scripts/evaluation/data/specs/weather/weather-2.json @@ -0,0 +1,207 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Naval Weather Forecast Service API", + "description": "API for accessing weather forecasts for naval conditions", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://naval-weather-forecast-service.com/api/v1" + } + ], + "paths": { + "/forecast/naval": { + "get": { + "summary": "Get naval weather forecast", + "description": "Returns weather forecast specifically tailored for naval operations.", + "responses": { + "200": { + "description": "Successful response with naval weather forecast", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date", + "description": "Date of the forecast" + }, + "wind_speed": { + "type": "number", + "format": "float", + "description": "Wind speed in knots" + }, + "wave_height": { + "type": "number", + "format": "float", + "description": "Significant wave height in meters" + }, + "swell_height": { + "type": "number", + "format": "float", + "description": "Swell height in meters" + }, + "swell_direction": { + "type": "string", + "description": "Direction of the swell" + }, + "visibility": { + "type": "string", + "description": "Visibility conditions" + } + } + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Naval Forecast" + ] + } + }, + "/forecast/maritime": { + "get": { + "summary": "Get maritime weather forecast", + "description": "Returns weather forecast specifically tailored for maritime operations.", + "responses": { + "200": { + "description": "Successful response with maritime weather forecast", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date", + "description": "Date of the forecast" + }, + "wind_speed": { + "type": "number", + "format": "float", + "description": "Wind speed in knots" + }, + "wave_height": { + "type": "number", + "format": "float", + "description": "Significant wave height in meters" + }, + "visibility": { + "type": "string", + "description": "Visibility conditions" + } + } + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Maritime Forecast" + ] + } + }, + "/forecast/navy": { + "get": { + "summary": "Get navy weather forecast", + "description": "Returns weather forecast specifically tailored for navy operations.", + "responses": { + "200": { + "description": "Successful response with navy weather forecast", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date", + "description": "Date of the forecast" + }, + "wind_speed": { + "type": "number", + "format": "float", + "description": "Wind speed in knots" + }, + "wave_height": { + "type": "number", + "format": "float", + "description": "Significant wave height in meters" + }, + "swell_height": { + "type": "number", + "format": "float", + "description": "Swell height in meters" + }, + "swell_direction": { + "type": "string", + "description": "Direction of the swell" + }, + "visibility": { + "type": "string", + "description": "Visibility conditions" + } + } + } + } + } + }, + "400": { + "description": "Error response", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error message" + } + } + } + } + } + } + }, + "tags": [ + "Navy Forecast" + ] + } + } + } +} diff --git a/scripts/evaluation/data/specs/weather/weather-3.json b/scripts/evaluation/data/specs/weather/weather-3.json new file mode 100644 index 00000000..5b9aec13 --- /dev/null +++ b/scripts/evaluation/data/specs/weather/weather-3.json @@ -0,0 +1,6405 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "weather.gov API", + "description": "weather.gov API", + "version": "1.11.1" + }, + "servers": [ + { + "url": "https://api.weather.gov", + "description": "Production server" + } + ], + "paths": { + "/alerts": { + "get": { + "description": "Returns all alerts", + "operationId": "alerts_query", + "parameters": [ + { + "name": "active", + "in": "query", + "description": "List only active alerts (use /alerts/active endpoints instead)", + "deprecated": true, + "schema": { + "type": "boolean" + } + }, + { + "$ref": "#/components/parameters/QueryStartTime" + }, + { + "$ref": "#/components/parameters/QueryEndTime" + }, + { + "$ref": "#/components/parameters/AlertStatus" + }, + { + "$ref": "#/components/parameters/AlertMessageType" + }, + { + "$ref": "#/components/parameters/AlertEventName" + }, + { + "$ref": "#/components/parameters/AlertCode" + }, + { + "$ref": "#/components/parameters/AlertArea" + }, + { + "$ref": "#/components/parameters/AlertPoint" + }, + { + "$ref": "#/components/parameters/AlertRegion" + }, + { + "$ref": "#/components/parameters/AlertRegionType" + }, + { + "$ref": "#/components/parameters/AlertZone" + }, + { + "$ref": "#/components/parameters/AlertUrgency" + }, + { + "$ref": "#/components/parameters/AlertSeverity" + }, + { + "$ref": "#/components/parameters/AlertCertainty" + }, + { + "$ref": "#/components/parameters/Limit" + }, + { + "$ref": "#/components/parameters/PaginationCursor" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AlertCollection" + }, + "301": { + "description": "Certain common queries may be redirected to discrete URLs" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/alerts/active": { + "get": { + "description": "Returns all currently active alerts", + "operationId": "alerts_active", + "parameters": [ + { + "$ref": "#/components/parameters/AlertStatus" + }, + { + "$ref": "#/components/parameters/AlertMessageType" + }, + { + "$ref": "#/components/parameters/AlertEventName" + }, + { + "$ref": "#/components/parameters/AlertCode" + }, + { + "$ref": "#/components/parameters/AlertArea" + }, + { + "$ref": "#/components/parameters/AlertPoint" + }, + { + "$ref": "#/components/parameters/AlertRegion" + }, + { + "$ref": "#/components/parameters/AlertRegionType" + }, + { + "$ref": "#/components/parameters/AlertZone" + }, + { + "$ref": "#/components/parameters/AlertUrgency" + }, + { + "$ref": "#/components/parameters/AlertSeverity" + }, + { + "$ref": "#/components/parameters/AlertCertainty" + }, + { + "$ref": "#/components/parameters/Limit" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/AlertCollection" + }, + "301": { + "description": "Certain common queries may be redirected to discrete URLs" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/alerts/active/count": { + "get": { + "description": "Returns info on the number of active alerts", + "operationId": "alerts_active_count", + "responses": { + "200": { + "description": "A data structure showing the counts of active alerts broken down by various categories", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "type": "object", + "properties": { + "total": { + "minimum": 0, + "type": "integer", + "description": "The total number of active alerts" + }, + "land": { + "minimum": 0, + "type": "integer", + "description": "The total number of active alerts affecting land zones" + }, + "marine": { + "minimum": 0, + "type": "integer", + "description": "The total number of active alerts affecting marine zones" + }, + "regions": { + "type": "object", + "description": "Active alerts by marine region", + "additionalProperties": { + "minimum": 1, + "type": "integer" + } + }, + "areas": { + "type": "object", + "description": "Active alerts by area (state/territory)", + "additionalProperties": { + "minimum": 1, + "type": "integer" + } + }, + "zones": { + "type": "object", + "description": "Active alerts by NWS public zone or county code", + "additionalProperties": { + "minimum": 1, + "type": "integer" + } + } + } + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + } + }, + "/alerts/active/zone/{zoneId}": { + "get": { + "description": "Returns active alerts for the given NWS public zone or county", + "operationId": "alerts_active_zone", + "responses": { + "200": { + "$ref": "#/components/responses/AlertCollection" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/NWSZoneId" + } + ] + }, + "/alerts/active/area/{area}": { + "get": { + "description": "Returns active alerts for the given area (state or marine area)", + "operationId": "alerts_active_area", + "responses": { + "200": { + "$ref": "#/components/responses/AlertCollection" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "area", + "in": "path", + "description": "State/area ID", + "required": true, + "schema": { + "$ref": "#/components/schemas/AreaCode" + } + } + ] + }, + "/alerts/active/region/{region}": { + "get": { + "description": "Returns active alerts for the given marine region", + "operationId": "alerts_active_region", + "responses": { + "200": { + "$ref": "#/components/responses/AlertCollection" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "region", + "in": "path", + "description": "Marine region ID", + "required": true, + "schema": { + "$ref": "#/components/schemas/MarineRegionCode" + } + } + ] + }, + "/alerts/types": { + "get": { + "description": "Returns a list of alert types", + "operationId": "alerts_types", + "responses": { + "200": { + "description": "A list of recognized event types", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "type": "object", + "properties": { + "eventTypes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of recognized event types" + } + } + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + } + }, + "/alerts/{id}": { + "get": { + "description": "Returns a specific alert", + "operationId": "alerts_single", + "responses": { + "200": { + "description": "An alert record", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/AlertGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/AlertJsonLd" + } + }, + "application/cap+xml": { + "schema": { + "$ref": "#/components/schemas/AlertCap" + } + } + }, + "x-url-content-negotiation-extensions": { + "json": "application/geo+json", + "cap": "application/cap+xml" + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Alert identifier", + "required": true, + "schema": { + "$ref": "#/components/schemas/AlertId" + } + } + ] + }, + "/aviation/cwsus/{cwsuId}": { + "get": { + "description": "Returns metadata about a Center Weather Service Unit", + "operationId": "cwsu", + "parameters": [ + { + "$ref": "#/components/parameters/NWSCenterWeatherServiceUnitId" + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/CenterWeatherServiceUnitJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/aviation/cwsus/{cwsuId}/cwas": { + "get": { + "description": "Returns a list of Center Weather Advisories from a CWSU", + "operationId": "cwas", + "parameters": [ + { + "$ref": "#/components/parameters/NWSCenterWeatherServiceUnitId" + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/CenterWeatherAdvisoryCollectionGeoJson" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/aviation/cwsus/{cwsuId}/cwas/{date}/{sequence}": { + "get": { + "description": "Returns a list of Center Weather Advisories from a CWSU", + "operationId": "cwa", + "parameters": [ + { + "$ref": "#/components/parameters/NWSCenterWeatherServiceUnitId" + }, + { + "$ref": "#/components/parameters/Date" + }, + { + "name": "sequence", + "in": "path", + "description": "Sequence number", + "required": true, + "schema": { + "minimum": 100, + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/CenterWeatherAdvisoryGeoJson" + } + }, + "application/vnd.noaa.uswx+xml": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/aviation/sigmets": { + "get": { + "description": "Returns a list of SIGMET/AIRMETs", + "operationId": "sigmetQuery", + "parameters": [ + { + "$ref": "#/components/parameters/QueryStartTime" + }, + { + "$ref": "#/components/parameters/QueryEndTime" + }, + { + "$ref": "#/components/parameters/QueryDate" + }, + { + "name": "atsu", + "in": "query", + "description": "ATSU identifier", + "schema": { + "$ref": "#/components/schemas/ATSUIdentifier" + } + }, + { + "name": "sequence", + "in": "query", + "description": "SIGMET sequence number", + "schema": { + "$ref": "#/components/schemas/SigmetSequenceNumber" + } + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/SigmetCollectionGeoJson" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/aviation/sigmets/{atsu}": { + "get": { + "description": "Returns a list of SIGMET/AIRMETs for the specified ATSU", + "operationId": "sigmetsByATSU", + "parameters": [ + { + "$ref": "#/components/parameters/ATSUIdentifier" + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/SigmetCollectionGeoJson" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/aviation/sigmets/{atsu}/{date}": { + "get": { + "description": "Returns a list of SIGMET/AIRMETs for the specified ATSU for the specified date", + "operationId": "sigmetsByATSUByDate", + "parameters": [ + { + "$ref": "#/components/parameters/ATSUIdentifier" + }, + { + "$ref": "#/components/parameters/Date" + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/SigmetCollectionGeoJson" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/aviation/sigmets/{atsu}/{date}/{time}": { + "get": { + "description": "Returns a specific SIGMET/AIRMET", + "operationId": "sigmet", + "parameters": [ + { + "$ref": "#/components/parameters/ATSUIdentifier" + }, + { + "$ref": "#/components/parameters/Date" + }, + { + "$ref": "#/components/parameters/Time" + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/SigmetGeoJson" + } + }, + "application/vnd.noaa.uswx+xml": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/glossary": { + "get": { + "description": "Returns glossary terms", + "operationId": "glossary", + "responses": { + "200": { + "description": "A set of glossary terms", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "glossary": { + "type": "array", + "items": { + "type": "object", + "properties": { + "term": { + "type": "string", + "description": "The term being defined" + }, + "definition": { + "type": "string", + "description": "A definition for the term" + } + } + }, + "description": "A list of glossary terms" + } + } + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + } + }, + "/gridpoints/{wfo}/{x},{y}": { + "get": { + "description": "Returns raw numerical forecast data for a 2.5km grid area", + "operationId": "gridpoint", + "parameters": [], + "responses": { + "200": { + "description": "Gridpoint forecast data", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/GridpointGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/GridpointJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/GridpointWFO" + }, + { + "$ref": "#/components/parameters/GridpointX" + }, + { + "$ref": "#/components/parameters/GridpointY" + } + ] + }, + "/gridpoints/{wfo}/{x},{y}/forecast": { + "get": { + "description": "Returns a textual forecast for a 2.5km grid area", + "operationId": "gridpoint_forecast", + "parameters": [ + { + "$ref": "#/components/parameters/GridpointForecastFeatureFlags" + }, + { + "$ref": "#/components/parameters/GridpointForecastUnits" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GridpointForecast" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/GridpointWFO" + }, + { + "$ref": "#/components/parameters/GridpointX" + }, + { + "$ref": "#/components/parameters/GridpointY" + } + ] + }, + "/gridpoints/{wfo}/{x},{y}/forecast/hourly": { + "get": { + "description": "Returns a textual hourly forecast for a 2.5km grid area", + "operationId": "gridpoint_forecast_hourly", + "parameters": [ + { + "$ref": "#/components/parameters/GridpointForecastFeatureFlags" + }, + { + "$ref": "#/components/parameters/GridpointForecastUnits" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/GridpointForecast" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/GridpointWFO" + }, + { + "$ref": "#/components/parameters/GridpointX" + }, + { + "$ref": "#/components/parameters/GridpointY" + } + ] + }, + "/gridpoints/{wfo}/{x},{y}/stations": { + "get": { + "description": "Returns a list of observation stations usable for a given 2.5km grid area", + "operationId": "gridpoint_stations", + "responses": { + "200": { + "$ref": "#/components/responses/ObservationStationCollection" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/GridpointWFO" + }, + { + "$ref": "#/components/parameters/GridpointX" + }, + { + "$ref": "#/components/parameters/GridpointY" + }, + { + "$ref": "#/components/parameters/Limit" + }, + { + "$ref": "#/components/parameters/PaginationCursor" + } + ] + }, + "/icons/{set}/{timeOfDay}/{first}": { + "get": { + "description": "Returns a forecast icon. Icon services in API are deprecated.", + "operationId": "icons", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "image/png": { + "schema": { + "$ref": "#/components/schemas/BinaryFile" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "deprecated": true, + "parameters": [] + }, + "parameters": [ + { + "name": "set", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "timeOfDay", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "first", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "size", + "in": "query", + "description": "Font size", + "schema": { + "anyOf": [ + { + "enum": [ + "small", + "medium", + "large" + ], + "type": "string" + }, + { + "maximum": 500, + "minimum": 10, + "type": "integer" + } + ] + } + }, + { + "name": "fontsize", + "in": "query", + "description": "Font size", + "schema": { + "maximum": 24, + "minimum": 2, + "type": "integer" + } + } + ] + }, + "/icons/{set}/{timeOfDay}/{first}/{second}": { + "get": { + "description": "Returns a forecast icon. Icon services in API are deprecated.", + "operationId": "iconsDualCondition", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "image/png": { + "schema": { + "$ref": "#/components/schemas/BinaryFile" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "deprecated": true, + "parameters": [] + }, + "parameters": [ + { + "name": "set", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "timeOfDay", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "first", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "second", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "size", + "in": "query", + "description": "Font size", + "schema": { + "anyOf": [ + { + "enum": [ + "small", + "medium", + "large" + ], + "type": "string" + }, + { + "maximum": 500, + "minimum": 10, + "type": "integer" + } + ] + } + }, + { + "name": "fontsize", + "in": "query", + "description": "Font size", + "schema": { + "maximum": 24, + "minimum": 2, + "type": "integer" + } + } + ] + }, + "/icons": { + "get": { + "description": "Returns a list of icon codes and textual descriptions. Icon services in API are deprecated.", + "operationId": "icons_summary", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "required": [ + "icons" + ], + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "icons": { + "type": "object", + "additionalProperties": { + "required": [ + "description" + ], + "type": "object", + "properties": { + "description": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "deprecated": true, + "parameters": [] + } + }, + "/thumbnails/satellite/{area}": { + "get": { + "description": "Returns a thumbnail image for a satellite region. Image services in API are deprecated.", + "operationId": "satellite_thumbnails", + "responses": { + "200": { + "description": "An image file", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "image/jpeg": { + "schema": { + "$ref": "#/components/schemas/BinaryFile" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "deprecated": true, + "parameters": [] + }, + "parameters": [ + { + "name": "area", + "in": "path", + "description": ".", + "required": true, + "schema": { + "enum": [ + "a", + "e", + "g", + "h", + "p", + "s", + "w" + ], + "type": "string" + } + } + ] + }, + "/stations/{stationId}/observations": { + "get": { + "description": "Returns a list of observations for a given station", + "operationId": "station_observation_list", + "parameters": [ + { + "$ref": "#/components/parameters/QueryStartTime" + }, + { + "$ref": "#/components/parameters/QueryEndTime" + }, + { + "name": "limit", + "in": "query", + "description": "Limit", + "schema": { + "maximum": 500, + "minimum": 1, + "type": "integer" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObservationCollection" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/ObservationStationId" + } + ] + }, + "/stations/{stationId}/observations/latest": { + "get": { + "description": "Returns the latest observation for a station", + "operationId": "station_observation_latest", + "parameters": [ + { + "name": "require_qc", + "in": "query", + "description": "Require QC", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Observation" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/ObservationStationId" + } + ] + }, + "/stations/{stationId}/observations/{time}": { + "get": { + "description": "Returns a single observation.", + "operationId": "station_observation_time", + "parameters": [ + { + "name": "time", + "in": "path", + "description": "Timestamp of requested observation", + "required": true, + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Observation" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/ObservationStationId" + } + ] + }, + "/stations/{stationId}/tafs": { + "get": { + "description": "Returns Terminal Aerodrome Forecasts for the specified airport station.", + "operationId": "tafs", + "parameters": [ + { + "$ref": "#/components/parameters/ObservationStationId" + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/stations/{stationId}/tafs/{date}/{time}": { + "get": { + "description": "Returns a single Terminal Aerodrome Forecast.", + "operationId": "taf", + "parameters": [ + { + "$ref": "#/components/parameters/ObservationStationId" + }, + { + "$ref": "#/components/parameters/Date" + }, + { + "$ref": "#/components/parameters/Time" + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/vnd.wmo.iwxxm+xml": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/stations": { + "get": { + "description": "Returns a list of observation stations.", + "operationId": "obs_stations", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "Filter by observation station ID", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "state", + "in": "query", + "description": "Filter by state/marine area code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AreaCode" + } + } + }, + { + "$ref": "#/components/parameters/Limit" + }, + { + "$ref": "#/components/parameters/PaginationCursor" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/ObservationStationCollection" + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/stations/{stationId}": { + "get": { + "description": "Returns metadata about a given observation station", + "operationId": "obs_station", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ObservationStationGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ObservationStationJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/ObservationStationId" + } + ] + }, + "/offices/{officeId}": { + "get": { + "description": "Returns metadata about a NWS forecast office", + "operationId": "office", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/Office" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/NWSOfficeId" + } + ] + }, + "/offices/{officeId}/headlines/{headlineId}": { + "get": { + "description": "Returns a specific news headline for a given NWS office", + "operationId": "office_headline", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/OfficeHeadline" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/NWSOfficeId" + }, + { + "name": "headlineId", + "in": "path", + "description": "Headline record ID", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/offices/{officeId}/headlines": { + "get": { + "description": "Returns a list of news headlines for a given NWS office", + "operationId": "office_headlines", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/OfficeHeadlineCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/NWSOfficeId" + } + ] + }, + "/points/{point}": { + "get": { + "description": "Returns metadata about a given latitude/longitude point", + "operationId": "point", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/PointGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/PointJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/PathPoint" + } + ] + }, + "/points/{point}/stations": { + "get": { + "description": "Returns a list of observation stations for a given point", + "operationId": "point_stations", + "responses": { + "301": { + "description": "redirect to gridpoint stations" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "deprecated": true, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/PathPoint" + } + ] + }, + "/radar/servers": { + "get": { + "description": "Returns a list of radar servers", + "operationId": "radar_servers", + "parameters": [ + { + "name": "reportingHost", + "in": "query", + "description": "Show records from specific reporting host", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/radar/servers/{id}": { + "get": { + "description": "Returns metadata about a given radar server", + "operationId": "radar_server", + "parameters": [ + { + "name": "reportingHost", + "in": "query", + "description": "Show records from specific reporting host", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Server ID", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/radar/stations": { + "get": { + "description": "Returns a list of radar stations", + "operationId": "radar_stations", + "parameters": [ + { + "name": "stationType", + "in": "query", + "description": "Limit results to a specific station type or types", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "pattern": "^[A-Za-z0-9-]+$", + "type": "string" + } + } + }, + { + "name": "reportingHost", + "in": "query", + "description": "Show RDA and latency info from specific reporting host", + "schema": { + "type": "string" + } + }, + { + "name": "host", + "in": "query", + "description": "Show latency info from specific LDM host", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": {} + }, + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/radar/stations/{stationId}": { + "get": { + "description": "Returns metadata about a given radar station", + "operationId": "radar_station", + "parameters": [ + { + "name": "reportingHost", + "in": "query", + "description": "Show RDA and latency info from specific reporting host", + "schema": { + "type": "string" + } + }, + { + "name": "host", + "in": "query", + "description": "Show latency info from specific LDM host", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": {} + }, + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "name": "stationId", + "in": "path", + "description": "Radar station ID", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/radar/stations/{stationId}/alarms": { + "get": { + "description": "Returns metadata about a given radar station alarms", + "operationId": "radar_station_alarms", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "stationId", + "in": "path", + "description": "Radar station ID", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/radar/queues/{host}": { + "get": { + "description": "Returns metadata about a given radar queue", + "operationId": "radar_queue", + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "Record limit", + "schema": { + "maximum": 500, + "minimum": 1, + "type": "integer" + } + }, + { + "name": "arrived", + "in": "query", + "description": "Range for arrival time", + "schema": { + "$ref": "#/components/schemas/ISO8601Interval" + } + }, + { + "name": "created", + "in": "query", + "description": "Range for creation time", + "schema": { + "$ref": "#/components/schemas/ISO8601Interval" + } + }, + { + "name": "published", + "in": "query", + "description": "Range for publish time", + "schema": { + "$ref": "#/components/schemas/ISO8601Interval" + } + }, + { + "name": "station", + "in": "query", + "description": "Station identifier", + "schema": { + "type": "string" + } + }, + { + "name": "type", + "in": "query", + "description": "Record type", + "schema": { + "type": "string" + } + }, + { + "name": "feed", + "in": "query", + "description": "Originating product feed", + "schema": { + "type": "string" + } + }, + { + "name": "resolution", + "in": "query", + "description": "Resolution version", + "schema": { + "minimum": 1, + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "name": "host", + "in": "path", + "description": "LDM host", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/radar/profilers/{stationId}": { + "get": { + "description": "Returns metadata about a given radar wind profiler", + "operationId": "radar_profiler", + "parameters": [ + { + "name": "time", + "in": "query", + "description": "Time interval", + "schema": { + "$ref": "#/components/schemas/ISO8601Interval" + } + }, + { + "name": "interval", + "in": "query", + "description": "Averaging interval", + "schema": { + "$ref": "#/components/schemas/ISO8601Duration" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": {} + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "name": "stationId", + "in": "path", + "description": "Profiler station ID", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/products": { + "get": { + "description": "Returns a list of text products", + "operationId": "products_query", + "parameters": [ + { + "name": "location", + "in": "query", + "description": "Location id", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "start", + "in": "query", + "description": "Start time", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "end", + "in": "query", + "description": "End time", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "office", + "in": "query", + "description": "Issuing office", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "pattern": "^[A-Z]{4}$", + "type": "string" + } + } + }, + { + "name": "wmoid", + "in": "query", + "description": "WMO id code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "pattern": "^[A-Z]{4}\\d{2}$", + "type": "string" + } + } + }, + { + "name": "type", + "in": "query", + "description": "Product code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "pattern": "^\\w{3}$", + "type": "string" + } + } + }, + { + "name": "limit", + "in": "query", + "description": "Limit", + "schema": { + "maximum": 500, + "minimum": 1, + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProductCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/products/locations": { + "get": { + "description": "Returns a list of valid text product issuance locations", + "operationId": "product_locations", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProductLocationCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + } + }, + "/products/types": { + "get": { + "description": "Returns a list of valid text product types and codes", + "operationId": "product_types", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProductTypeCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + } + }, + "/products/{productId}": { + "get": { + "description": "Returns a specific text product", + "operationId": "product", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProduct" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "productId", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/products/types/{typeId}": { + "get": { + "description": "Returns a list of text products of a given type", + "operationId": "products_type", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProductCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "typeId", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/products/types/{typeId}/locations": { + "get": { + "description": "Returns a list of valid text product issuance locations for a given product type", + "operationId": "products_type_locations", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProductLocationCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "typeId", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/products/locations/{locationId}/types": { + "get": { + "description": "Returns a list of valid text product types for a given issuance location", + "operationId": "location_products", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProductTypeCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "locationId", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/products/types/{typeId}/locations/{locationId}": { + "get": { + "description": "Returns a list of text products of a given type for a given issuance location", + "operationId": "products_type_location", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/TextProductCollection" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "typeId", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "locationId", + "in": "path", + "description": ".", + "required": true, + "schema": { + "type": "string" + } + } + ] + }, + "/zones": { + "get": { + "description": "Returns a list of zones", + "operationId": "zone_list", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "Zone ID (forecast or county)", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NWSZoneID" + } + } + }, + { + "name": "area", + "in": "query", + "description": "State/marine area code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AreaCode" + } + } + }, + { + "name": "region", + "in": "query", + "description": "Region code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RegionCode" + } + } + }, + { + "name": "type", + "in": "query", + "description": "Zone type", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NWSZoneType" + } + } + }, + { + "name": "point", + "in": "query", + "description": "Point (latitude,longitude)", + "schema": { + "$ref": "#/components/schemas/PointString" + } + }, + { + "name": "include_geometry", + "in": "query", + "description": "Include geometry in results (true/false)", + "schema": { + "type": "boolean" + } + }, + { + "name": "limit", + "in": "query", + "description": "Limit", + "schema": { + "minimum": 1, + "type": "integer" + } + }, + { + "name": "effective", + "in": "query", + "description": "Effective date/time", + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ZoneCollectionGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ZoneCollectionJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + } + }, + "/zones/{type}": { + "get": { + "description": "Returns a list of zones of a given type", + "operationId": "zone_list_type", + "parameters": [ + { + "name": "id", + "in": "query", + "description": "Zone ID (forecast or county)", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NWSZoneID" + } + } + }, + { + "name": "area", + "in": "query", + "description": "State/marine area code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AreaCode" + } + } + }, + { + "name": "region", + "in": "query", + "description": "Region code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RegionCode" + } + } + }, + { + "name": "type", + "in": "query", + "description": "Zone type", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NWSZoneType" + } + } + }, + { + "name": "point", + "in": "query", + "description": "Point (latitude,longitude)", + "schema": { + "$ref": "#/components/schemas/PointString" + } + }, + { + "name": "include_geometry", + "in": "query", + "description": "Include geometry in results (true/false)", + "schema": { + "type": "boolean" + } + }, + { + "name": "limit", + "in": "query", + "description": "Limit", + "schema": { + "minimum": 1, + "type": "integer" + } + }, + { + "name": "effective", + "in": "query", + "description": "Effective date/time", + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ZoneCollectionGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ZoneCollectionJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "name": "type", + "in": "path", + "description": "Zone type", + "required": true, + "schema": { + "$ref": "#/components/schemas/NWSZoneType" + } + } + ] + }, + "/zones/{type}/{zoneId}": { + "get": { + "description": "Returns metadata about a given zone", + "operationId": "zone", + "parameters": [ + { + "name": "effective", + "in": "query", + "description": "Effective date/time", + "schema": { + "type": "string", + "format": "date-time" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ZoneGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ZoneJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "name": "type", + "in": "path", + "description": "Zone type", + "required": true, + "schema": { + "$ref": "#/components/schemas/NWSZoneType" + } + }, + { + "$ref": "#/components/parameters/NWSZoneId" + } + ] + }, + "/zones/{type}/{zoneId}/forecast": { + "get": { + "description": "Returns the current zone forecast for a given zone", + "operationId": "zone_forecast", + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ZoneForecastGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ZoneForecastJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "name": "type", + "in": "path", + "description": "Zone type", + "required": true, + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/NWSZoneId" + } + ] + }, + "/zones/forecast/{zoneId}/observations": { + "get": { + "description": "Returns a list of observations for a given zone", + "operationId": "zone_obs", + "parameters": [ + { + "name": "start", + "in": "query", + "description": "Start date/time", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "end", + "in": "query", + "description": "End date/time", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "name": "limit", + "in": "query", + "description": "Limit", + "schema": { + "maximum": 500, + "minimum": 1, + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "success", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ObservationCollectionGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ObservationCollectionJsonLd" + } + } + } + }, + "default": { + "$ref": "#/components/responses/Error" + } + } + }, + "parameters": [ + { + "$ref": "#/components/parameters/NWSZoneId" + } + ] + }, + "/zones/forecast/{zoneId}/stations": { + "get": { + "description": "Returns a list of observation stations for a given zone", + "operationId": "zone_stations", + "responses": { + "200": { + "$ref": "#/components/responses/ObservationStationCollection" + }, + "default": { + "$ref": "#/components/responses/Error" + } + }, + "parameters": [] + }, + "parameters": [ + { + "$ref": "#/components/parameters/NWSZoneId" + }, + { + "$ref": "#/components/parameters/Limit" + }, + { + "$ref": "#/components/parameters/PaginationCursor" + } + ] + } + }, + "components": { + "schemas": { + "Alert": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/AlertId" + }, + "areaDesc": { + "type": "string", + "description": "A textual description of the area affected by the alert." + }, + "geocode": { + "type": "object", + "properties": { + "UGC": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NWSZoneID" + }, + "description": "A list of NWS public zone or county identifiers." + }, + "SAME": { + "type": "array", + "items": { + "pattern": "^\\d{6}$", + "type": "string" + }, + "description": "A list of SAME (Specific Area Message Encoding) codes for affected counties." + } + }, + "description": "Lists of codes for NWS public zones and counties affected by the alert." + }, + "affectedZones": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + }, + "description": "An array of API links for zones affected by the alert. This is an API-specific extension field and is not part of the CAP specification.\n" + }, + "references": { + "type": "array", + "items": { + "type": "object", + "properties": { + "@id": { + "type": "string", + "description": "An API link to the prior alert.", + "format": "uri" + }, + "identifier": { + "$ref": "#/components/schemas/AlertId" + }, + "sender": { + "type": "string", + "description": "The sender of the prior alert." + }, + "sent": { + "type": "string", + "description": "The time the prior alert was sent.", + "format": "date-time" + } + } + }, + "description": "A list of prior alerts that this alert updates or replaces." + }, + "sent": { + "type": "string", + "description": "The time of the origination of the alert message.", + "format": "date-time" + }, + "effective": { + "type": "string", + "description": "The effective time of the information of the alert message.", + "format": "date-time" + }, + "onset": { + "type": "string", + "description": "The expected time of the beginning of the subject event of the alert message.", + "format": "date-time", + "nullable": true + }, + "expires": { + "type": "string", + "description": "The expiry time of the information of the alert message.", + "format": "date-time" + }, + "ends": { + "type": "string", + "description": "The expected end time of the subject event of the alert message.", + "format": "date-time", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/AlertStatus" + }, + "messageType": { + "$ref": "#/components/schemas/AlertMessageType" + }, + "category": { + "enum": [ + "Met", + "Geo", + "Safety", + "Security", + "Rescue", + "Fire", + "Health", + "Env", + "Transport", + "Infra", + "CBRNE", + "Other" + ], + "type": "string", + "description": "The code denoting the category of the subject event of the alert message." + }, + "severity": { + "$ref": "#/components/schemas/AlertSeverity" + }, + "certainty": { + "$ref": "#/components/schemas/AlertCertainty" + }, + "urgency": { + "$ref": "#/components/schemas/AlertUrgency" + }, + "event": { + "type": "string", + "description": "The text denoting the type of the subject event of the alert message." + }, + "sender": { + "type": "string", + "description": "Email address of the NWS webmaster." + }, + "senderName": { + "type": "string", + "description": "The text naming the originator of the alert message." + }, + "headline": { + "type": "string", + "description": "The text headline of the alert message.", + "nullable": true + }, + "description": { + "type": "string", + "description": "The text describing the subject event of the alert message." + }, + "instruction": { + "type": "string", + "description": "The text describing the recommended action to be taken by recipients of the alert message.\n", + "nullable": true + }, + "response": { + "enum": [ + "Shelter", + "Evacuate", + "Prepare", + "Execute", + "Avoid", + "Monitor", + "Assess", + "AllClear", + "None" + ], + "type": "string", + "description": "The code denoting the type of action recommended for the target audience.\nThis corresponds to responseType in the CAP specification.\n" + }, + "parameters": { + "type": "object", + "description": "System-specific additional parameters associated with the alert message.\nThe keys in this object correspond to parameter definitions in the NWS CAP specification.\n", + "additionalProperties": { + "type": "array", + "items": {} + } + } + }, + "description": "An object representing a public alert message.\nUnless otherwise noted, the fields in this object correspond to the National Weather Service CAP v1.2 specification, which extends the OASIS Common Alerting Protocol (CAP) v1.2 specification and USA Integrated Public Alert and Warning System (IPAWS) Profile v1.0. Refer to this documentation for more complete information.\nhttp://docs.oasis-open.org/emergency/cap/v1.2/CAP-v1.2-os.html http://docs.oasis-open.org/emergency/cap/v1.2/ipaws-profile/v1.0/cs01/cap-v1.2-ipaws-profile-cs01.html https://alerts.weather.gov/#technical-notes-v12\n" + }, + "AlertCollection": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "A title describing the alert collection" + }, + "updated": { + "type": "string", + "description": "The last time a change occurred to this collection", + "format": "date-time" + }, + "pagination": { + "$ref": "#/components/schemas/PaginationInfo" + } + } + }, + "AlertCollectionGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeatureCollection" + }, + { + "type": "object", + "properties": { + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Alert" + } + } + } + } + } + }, + { + "$ref": "#/components/schemas/AlertCollection" + } + ] + }, + "AlertCollectionJsonLd": { + "allOf": [ + { + "$ref": "#/components/schemas/AlertCollection" + }, + { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@graph": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Alert" + } + } + } + } + ] + }, + "AlertCap": { + "type": "object" + }, + "AlertGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Alert" + } + } + } + ] + }, + "AlertJsonLd": { + "type": "object", + "properties": { + "@graph": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Alert" + } + } + } + }, + "AlertId": { + "type": "string", + "description": "The identifier of the alert message." + }, + "AlertCertainty": { + "enum": [ + "Observed", + "Likely", + "Possible", + "Unlikely", + "Unknown" + ], + "type": "string" + }, + "AlertMessageType": { + "enum": [ + "Alert", + "Update", + "Cancel", + "Ack", + "Error" + ], + "type": "string" + }, + "AlertSeverity": { + "enum": [ + "Extreme", + "Severe", + "Moderate", + "Minor", + "Unknown" + ], + "type": "string" + }, + "AlertStatus": { + "enum": [ + "Actual", + "Exercise", + "System", + "Test", + "Draft" + ], + "type": "string" + }, + "AlertUrgency": { + "enum": [ + "Immediate", + "Expected", + "Future", + "Past", + "Unknown" + ], + "type": "string" + }, + "AlertAtomEntry": { + "type": "object", + "properties": { + "id": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "updated": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "published": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "summary": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "event": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "sent": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "effective": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "expires": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "status": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "msgType": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "category": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "urgency": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "severity": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "certainty": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "areaDesc": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "polygon": { + "type": "string", + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "geocode": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AlertXMLParameter" + }, + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + }, + "parameter": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AlertXMLParameter" + }, + "xml": { + "namespace": "urn:oasis:names:tc:emergency:cap:1.2" + } + } + }, + "description": "An alert entry in an Atom feed", + "xml": { + "name": "entry", + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "AlertXMLParameter": { + "type": "object", + "properties": { + "valueName": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "AlertAtomFeed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "generator": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "updated": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + } + }, + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "title": { + "type": "string", + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "entry": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AlertAtomEntry" + }, + "xml": { + "namespace": "http://www.w3.org/2005/Atom" + } + } + }, + "description": "An alert feed in Atom format", + "xml": { + "name": "feed", + "namespace": "http://www.w3.org/2005/Atom" + } + }, + "AreaCode": { + "oneOf": [ + { + "$ref": "#/components/schemas/StateTerritoryCode" + }, + { + "$ref": "#/components/schemas/MarineAreaCode" + } + ], + "description": "State/territory codes and marine area codes" + }, + "ATSUIdentifier": { + "pattern": "^[A-Z]{3,4}$", + "type": "string", + "description": "ATSU Identifier" + }, + "BinaryFile": { + "type": "string", + "format": "binary" + }, + "CenterWeatherAdvisoryCollectionGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeatureCollection" + }, + { + "type": "object", + "properties": { + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/CenterWeatherAdvisory" + } + } + } + } + } + } + ] + }, + "CenterWeatherAdvisoryGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/CenterWeatherAdvisory" + } + } + } + ] + }, + "CenterWeatherAdvisory": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "issueTime": { + "type": "string", + "format": "date-time" + }, + "cwsu": { + "$ref": "#/components/schemas/NWSCenterWeatherServiceUnitId" + }, + "sequence": { + "minimum": 101, + "type": "integer" + }, + "start": { + "type": "string", + "format": "date-time" + }, + "end": { + "type": "string", + "format": "date-time" + }, + "observedProperty": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "additionalProperties": false + }, + "CenterWeatherServiceUnitJsonLd": { + "$ref": "#/components/schemas/Office" + }, + "Date": { + "type": "string", + "description": "Date (in YYYY-MM-DD format).", + "format": "date" + }, + "GeometryString": { + "type": "string", + "description": "A geometry represented in Well-Known Text (WKT) format.", + "format": "wkt", + "nullable": true + }, + "GeoJsonBoundingBox": { + "minItems": 4, + "type": "array", + "items": { + "type": "number" + }, + "description": "A GeoJSON bounding box. Please refer to IETF RFC 7946 for information on the GeoJSON format." + }, + "GeoJsonCoordinate": { + "minItems": 2, + "type": "array", + "items": { + "type": "number" + }, + "description": "A GeoJSON coordinate. Please refer to IETF RFC 7946 for information on the GeoJSON format." + }, + "GeoJsonGeometry": { + "oneOf": [ + { + "title": "GeoJSON Point", + "required": [ + "type", + "coordinates" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "Point" + ], + "type": "string" + }, + "coordinates": { + "$ref": "#/components/schemas/GeoJsonCoordinate" + }, + "bbox": { + "$ref": "#/components/schemas/GeoJsonBoundingBox" + } + } + }, + { + "title": "GeoJSON LineString", + "required": [ + "type", + "coordinates" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "LineString" + ], + "type": "string" + }, + "coordinates": { + "$ref": "#/components/schemas/GeoJsonLineString" + }, + "bbox": { + "$ref": "#/components/schemas/GeoJsonBoundingBox" + } + } + }, + { + "title": "GeoJSON Polygon", + "required": [ + "type", + "coordinates" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "Polygon" + ], + "type": "string" + }, + "coordinates": { + "$ref": "#/components/schemas/GeoJsonPolygon" + }, + "bbox": { + "$ref": "#/components/schemas/GeoJsonBoundingBox" + } + } + }, + { + "title": "GeoJSON MultiPoint", + "required": [ + "type", + "coordinates" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "MultiPoint" + ], + "type": "string" + }, + "coordinates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GeoJsonCoordinate" + } + }, + "bbox": { + "$ref": "#/components/schemas/GeoJsonBoundingBox" + } + } + }, + { + "title": "GeoJSON MultiLineString", + "required": [ + "type", + "coordinates" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "MultiLineString" + ], + "type": "string" + }, + "coordinates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GeoJsonLineString" + } + }, + "bbox": { + "$ref": "#/components/schemas/GeoJsonBoundingBox" + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "required": [ + "type", + "coordinates" + ], + "type": "object", + "properties": { + "type": { + "enum": [ + "MultiPolygon" + ], + "type": "string" + }, + "coordinates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GeoJsonPolygon" + } + }, + "bbox": { + "$ref": "#/components/schemas/GeoJsonBoundingBox" + } + } + } + ], + "description": "A GeoJSON geometry object. Please refer to IETF RFC 7946 for information on the GeoJSON format.", + "nullable": true + }, + "GeoJsonFeature": { + "required": [ + "type", + "geometry", + "properties" + ], + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "id": { + "type": "string", + "format": "uri" + }, + "type": { + "enum": [ + "Feature" + ], + "type": "string" + }, + "geometry": { + "$ref": "#/components/schemas/GeoJsonGeometry" + }, + "properties": { + "type": "object" + } + }, + "description": "A GeoJSON feature. Please refer to IETF RFC 7946 for information on the GeoJSON format.", + "additionalProperties": false + }, + "GeoJsonFeatureCollection": { + "required": [ + "type", + "features" + ], + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "type": { + "enum": [ + "FeatureCollection" + ], + "type": "string" + }, + "features": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GeoJsonFeature" + } + } + }, + "description": "A GeoJSON feature collection. Please refer to IETF RFC 7946 for information on the GeoJSON format." + }, + "GeoJsonLineString": { + "minItems": 2, + "type": "array", + "items": { + "$ref": "#/components/schemas/GeoJsonCoordinate" + }, + "description": "A GeoJSON line string. Please refer to IETF RFC 7946 for information on the GeoJSON format." + }, + "GeoJsonPolygon": { + "type": "array", + "items": { + "minItems": 4, + "type": "array", + "items": { + "$ref": "#/components/schemas/GeoJsonCoordinate" + } + }, + "description": "A GeoJSON polygon. Please refer to IETF RFC 7946 for information on the GeoJSON format." + }, + "Gridpoint": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "@type": { + "enum": [ + "wx:Gridpoint" + ], + "type": "string" + }, + "updateTime": { + "type": "string", + "format": "date-time" + }, + "validTimes": { + "$ref": "#/components/schemas/ISO8601Interval" + }, + "elevation": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "forecastOffice": { + "type": "string", + "format": "uri" + }, + "gridId": { + "type": "string" + }, + "gridX": { + "minimum": 0, + "type": "integer" + }, + "gridY": { + "minimum": 0, + "type": "integer" + }, + "weather": { + "required": [ + "values" + ], + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "required": [ + "validTime", + "value" + ], + "type": "object", + "properties": { + "validTime": { + "$ref": "#/components/schemas/ISO8601Interval" + }, + "value": { + "type": "array", + "items": { + "required": [ + "coverage", + "weather", + "intensity", + "visibility", + "attributes" + ], + "type": "object", + "properties": { + "coverage": { + "enum": [ + "areas", + "brief", + "chance", + "definite", + "few", + "frequent", + "intermittent", + "isolated", + "likely", + "numerous", + "occasional", + "patchy", + "periods", + "scattered", + "slight_chance", + "widespread" + ], + "type": "string", + "nullable": true + }, + "weather": { + "enum": [ + "blowing_dust", + "blowing_sand", + "blowing_snow", + "drizzle", + "fog", + "freezing_fog", + "freezing_drizzle", + "freezing_rain", + "freezing_spray", + "frost", + "hail", + "haze", + "ice_crystals", + "ice_fog", + "rain", + "rain_showers", + "sleet", + "smoke", + "snow", + "snow_showers", + "thunderstorms", + "volcanic_ash", + "water_spouts" + ], + "type": "string", + "nullable": true + }, + "intensity": { + "enum": [ + "very_light", + "light", + "moderate", + "heavy" + ], + "type": "string", + "nullable": true + }, + "visibility": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "attributes": { + "type": "array", + "items": { + "enum": [ + "damaging_wind", + "dry_thunderstorms", + "flooding", + "gusty_wind", + "heavy_rain", + "large_hail", + "small_hail", + "tornadoes" + ], + "type": "string" + } + } + }, + "description": "A value object representing expected weather phenomena.", + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + } + }, + "hazards": { + "required": [ + "values" + ], + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "required": [ + "validTime", + "value" + ], + "type": "object", + "properties": { + "validTime": { + "$ref": "#/components/schemas/ISO8601Interval" + }, + "value": { + "type": "array", + "items": { + "required": [ + "phenomenon", + "significance", + "event_number" + ], + "type": "object", + "properties": { + "phenomenon": { + "pattern": "^\\w{2}$", + "type": "string", + "description": "Hazard code. This value will correspond to a P-VTEC phenomenon code as defined in NWS Directive 10-1703.\n" + }, + "significance": { + "pattern": "^\\w$", + "type": "string", + "description": "Significance code. This value will correspond to a P-VTEC significance code as defined in NWS Directive 10-1703.\nThis will most frequently be \"A\" for a watch or \"Y\" for an advisory.\n" + }, + "event_number": { + "type": "integer", + "description": "Event number. If this hazard refers to a national or regional center product (such as a Storm Prediction Center convective watch), this value will be the sequence number of that product.\n", + "nullable": true + } + }, + "description": "A value object representing an expected hazard." + } + } + }, + "additionalProperties": false + } + } + } + } + }, + "description": "Raw forecast data for a 2.5km grid square.\nThis is a list of all potential data layers that may appear. Some layers may not be present in all areas.\n* temperature\n* dewpoint\n* maxTemperature\n* minTemperature\n* relativeHumidity\n* apparentTemperature\n* heatIndex\n* windChill\n* wetBulbGlobeTemperature\n* skyCover\n* windDirection\n* windSpeed\n* windGust\n* weather\n* hazards: Watch and advisory products in effect\n* probabilityOfPrecipitation\n* quantitativePrecipitation\n* iceAccumulation\n* snowfallAmount\n* snowLevel\n* ceilingHeight\n* visibility\n* transportWindSpeed\n* transportWindDirection\n* mixingHeight\n* hainesIndex\n* lightningActivityLevel\n* twentyFootWindSpeed\n* twentyFootWindDirection\n* waveHeight\n* wavePeriod\n* waveDirection\n* primarySwellHeight\n* primarySwellDirection\n* secondarySwellHeight\n* secondarySwellDirection\n* wavePeriod2\n* windWaveHeight\n* dispersionIndex\n* pressure: Barometric pressure\n* probabilityOfTropicalStormWinds\n* probabilityOfHurricaneWinds\n* potentialOf15mphWinds\n* potentialOf25mphWinds\n* potentialOf35mphWinds\n* potentialOf45mphWinds\n* potentialOf20mphWindGusts\n* potentialOf30mphWindGusts\n* potentialOf40mphWindGusts\n* potentialOf50mphWindGusts\n* potentialOf60mphWindGusts\n* grasslandFireDangerIndex\n* probabilityOfThunder\n* davisStabilityIndex\n* atmosphericDispersionIndex\n* lowVisibilityOccurrenceRiskIndex\n* stability\n* redFlagThreatIndex\n", + "additionalProperties": { + "$ref": "#/components/schemas/GridpointQuantitativeValueLayer" + } + }, + "GridpointQuantitativeValueLayer": { + "required": [ + "values" + ], + "type": "object", + "properties": { + "uom": { + "$ref": "#/components/schemas/UnitOfMeasure" + }, + "values": { + "type": "array", + "items": { + "required": [ + "validTime", + "value" + ], + "type": "object", + "properties": { + "validTime": { + "$ref": "#/components/schemas/ISO8601Interval" + }, + "value": { + "type": "number", + "nullable": true + } + }, + "additionalProperties": false + } + } + }, + "description": "A gridpoint layer consisting of quantitative values (numeric values with associated units of measure).\n" + }, + "GridpointGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Gridpoint" + } + } + } + ] + }, + "GridpointJsonLd": { + "$ref": "#/components/schemas/Gridpoint" + }, + "GridpointForecast": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + }, + "units": { + "$ref": "#/components/schemas/GridpointForecastUnits" + }, + "forecastGenerator": { + "type": "string", + "description": "The internal generator class used to create the forecast text (used for NWS debugging)." + }, + "generatedAt": { + "type": "string", + "description": "The time this forecast data was generated.", + "format": "date-time" + }, + "updateTime": { + "type": "string", + "description": "The last update time of the data this forecast was generated from.", + "format": "date-time" + }, + "updated": { + "type": "string", + "description": "This property is deprecated (use updateTime instead).", + "format": "date-time", + "deprecated": true + }, + "validTimes": { + "$ref": "#/components/schemas/ISO8601Interval" + }, + "elevation": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "periods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GridpointForecastPeriod" + }, + "description": "An array of forecast periods." + } + }, + "description": "A multi-day forecast for a 2.5km grid square.", + "additionalProperties": false + }, + "GridpointForecastPeriod": { + "type": "object", + "properties": { + "number": { + "minimum": 1, + "type": "integer", + "description": "Sequential period number." + }, + "name": { + "type": "string", + "description": "A textual identifier for the period. This value will not be present for hourly forecasts.\n", + "example": "Tuesday Night" + }, + "startTime": { + "type": "string", + "description": "The starting time that this forecast period is valid for.", + "format": "date-time" + }, + "endTime": { + "type": "string", + "description": "The ending time that this forecast period is valid for.", + "format": "date-time" + }, + "isDaytime": { + "type": "boolean", + "description": "Indicates whether this period is daytime or nighttime." + }, + "temperature": { + "oneOf": [ + { + "$ref": "#/components/schemas/QuantitativeValue" + }, + { + "type": "integer" + } + ], + "description": "High/low temperature for the period, depending on whether the period is day or night.\nThis property as an integer value is deprecated. Future versions will express this value as a quantitative value object. To make use of the future standard format now, set the \"forecast_temperature_qv\" feature flag on the request.\n" + }, + "temperatureUnit": { + "enum": [ + "F", + "C" + ], + "type": "string", + "description": "The unit of the temperature value (Fahrenheit or Celsius).\nThis property is deprecated. Future versions will indicate the unit within the quantitative value object for the temperature property. To make use of the future standard format now, set the \"forecast_temperature_qv\" feature flag on the request.\n", + "deprecated": true + }, + "temperatureTrend": { + "enum": [ + "rising", + "falling" + ], + "type": "string", + "description": "If not null, indicates a non-diurnal temperature trend for the period (either rising temperature overnight, or falling temperature during the day)\n", + "nullable": true + }, + "probabilityOfPrecipitation": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "dewpoint": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "relativeHumidity": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "windSpeed": { + "oneOf": [ + { + "$ref": "#/components/schemas/QuantitativeValue" + }, + { + "type": "string" + } + ], + "description": "Wind speed for the period.\nThis property as an string value is deprecated. Future versions will express this value as a quantitative value object. To make use of the future standard format now, set the \"forecast_wind_speed_qv\" feature flag on the request.\n" + }, + "windGust": { + "oneOf": [ + { + "$ref": "#/components/schemas/QuantitativeValue" + }, + { + "type": "string" + } + ], + "description": "Peak wind gust for the period.\nThis property as an string value is deprecated. Future versions will express this value as a quantitative value object. To make use of the future standard format now, set the \"forecast_wind_speed_qv\" feature flag on the request.\n", + "nullable": true + }, + "windDirection": { + "enum": [ + "N", + "NNE", + "NE", + "ENE", + "E", + "ESE", + "SE", + "SSE", + "S", + "SSW", + "SW", + "WSW", + "W", + "WNW", + "NW", + "NNW" + ], + "type": "string", + "description": "The prevailing direction of the wind for the period, using a 16-point compass." + }, + "icon": { + "type": "string", + "description": "A link to an icon representing the forecast summary.", + "format": "uri", + "deprecated": true + }, + "shortForecast": { + "type": "string", + "description": "A brief textual forecast summary for the period." + }, + "detailedForecast": { + "type": "string", + "description": "A detailed textual forecast for the period." + } + }, + "description": "An object containing forecast information for a specific time period (generally 12-hour or 1-hour).\n", + "additionalProperties": false + }, + "GridpointForecastUnits": { + "enum": [ + "us", + "si" + ], + "type": "string", + "description": "Denotes the units used in the textual portions of the forecast.", + "default": "us" + }, + "GridpointForecastGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/GridpointForecast" + } + } + } + ] + }, + "GridpointForecastJsonLd": { + "allOf": [ + { + "$ref": "#/components/schemas/GridpointForecast" + }, + { + "required": [ + "@context", + "geometry" + ], + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + } + } + } + ] + }, + "ISO8601Duration": { + "pattern": "^P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+S)?)?$", + "type": "string", + "description": "A time duration in ISO 8601 format.", + "example": "P2DT12H" + }, + "ISO8601Interval": { + "oneOf": [ + { + "pattern": "^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:?\\d{2}?)|NOW)\\/(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:?\\d{2}?)|NOW)$", + "type": "string", + "example": "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z" + }, + { + "pattern": "^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:?\\d{2}?)|NOW)\\/P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+S)?)?$", + "type": "string", + "example": "2007-03-01T13:00:00Z/P1Y2M10DT2H30M" + }, + { + "pattern": "^P(\\d+Y)?(\\d+M)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+S)?)?\\/(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:?\\d{2}?)|NOW)$", + "type": "string", + "example": "P1Y2M10DT2H30M/2008-05-11T15:30:00Z" + } + ], + "description": "A time interval in ISO 8601 format. This can be one of:\n\n 1. Start and end time\n 2. Start time and duration\n 3. Duration and end time\nThe string \"NOW\" can also be used in place of a start/end time.\n" + }, + "JsonLdContext": { + "anyOf": [ + { + "type": "array", + "items": {} + }, + { + "type": "object" + } + ] + }, + "LandRegionCode": { + "enum": [ + "AR", + "CR", + "ER", + "PR", + "SR", + "WR" + ], + "type": "string", + "description": "Land region code. These correspond to the six NWS regional headquarters:\n* AR: Alaska Region\n* CR: Central Region\n* ER: Eastern Region\n* PR: Pacific Region\n* SR: Southern Region\n* WR: Western Region\n" + }, + "MarineAreaCode": { + "enum": [ + "AM", + "AN", + "GM", + "LC", + "LE", + "LH", + "LM", + "LO", + "LS", + "PH", + "PK", + "PM", + "PS", + "PZ", + "SL" + ], + "type": "string", + "description": "Marine area code as defined in NWS Directive 10-302:\n* AM: Western North Atlantic Ocean and along U.S. East Coast south of Currituck Beach Light NC following the coastline into Gulf of Mexico to Ocean Reef FL including the Caribbean\n* AN: Western North Atlantic Ocean and along U.S. East Coast from Canadian border south to Currituck Beach Light NC\n* GM: Gulf of Mexico and along the U.S. Gulf Coast from the Mexican border to Ocean Reef FL\n* LC: Lake St. Clair\n* LE: Lake Erie\n* LH: Lake Huron\n* LM: Lake Michigan\n* LO: Lake Ontario\n* LS: Lake Superior\n* PH: Central Pacific Ocean including Hawaiian waters\n* PK: North Pacific Ocean near Alaska and along Alaska coastline including the Bering Sea and the Gulf of Alaska\n* PM: Western Pacific Ocean including Mariana Island waters\n* PS: South Central Pacific Ocean including American Samoa waters\n* PZ: Eastern North Pacific Ocean and along U.S. West Coast from Canadian border to Mexican border\n* SL: St. Lawrence River above St. Regis\n" + }, + "MarineRegionCode": { + "enum": [ + "AL", + "AT", + "GL", + "GM", + "PA", + "PI" + ], + "type": "string", + "description": "Marine region code. These are groups of marine areas combined.\n* AL: Alaska waters (PK)\n* AT: Atlantic Ocean (AM, AN)\n* GL: Great Lakes (LC, LE, LH, LM, LO, LS, SL)\n* GM: Gulf of Mexico (GM)\n* PA: Eastern Pacific Ocean and U.S. West Coast (PZ)\n* PI: Central and Western Pacific (PH, PM, PS)\n" + }, + "MetarPhenomenon": { + "required": [ + "intensity", + "modifier", + "weather", + "rawString" + ], + "type": "object", + "properties": { + "intensity": { + "enum": [ + "light", + "heavy" + ], + "type": "string", + "nullable": true + }, + "modifier": { + "enum": [ + "patches", + "blowing", + "low_drifting", + "freezing", + "shallow", + "partial", + "showers" + ], + "type": "string", + "nullable": true + }, + "weather": { + "enum": [ + "fog_mist", + "dust_storm", + "dust", + "drizzle", + "funnel_cloud", + "fog", + "smoke", + "hail", + "snow_pellets", + "haze", + "ice_crystals", + "ice_pellets", + "dust_whirls", + "spray", + "rain", + "sand", + "snow_grains", + "snow", + "squalls", + "sand_storm", + "thunderstorms", + "unknown", + "volcanic_ash" + ], + "type": "string" + }, + "rawString": { + "type": "string" + }, + "inVicinity": { + "type": "boolean" + } + }, + "description": "An object representing a decoded METAR phenomenon string.", + "additionalProperties": false + }, + "MetarSkyCoverage": { + "enum": [ + "OVC", + "BKN", + "SCT", + "FEW", + "SKC", + "CLR", + "VV" + ], + "type": "string" + }, + "NWSCenterWeatherServiceUnitId": { + "enum": [ + "ZAB", + "ZAN", + "ZAU", + "ZBW", + "ZDC", + "ZDV", + "ZFA", + "ZFW", + "ZHU", + "ZID", + "ZJX", + "ZKC", + "ZLA", + "ZLC", + "ZMA", + "ZME", + "ZMP", + "ZNY", + "ZOA", + "ZOB", + "ZSE", + "ZTL" + ], + "type": "string", + "description": "Three-letter identifier for a Center Weather Service Unit (CWSU)." + }, + "NWSForecastOfficeId": { + "enum": [ + "AKQ", + "ALY", + "BGM", + "BOX", + "BTV", + "BUF", + "CAE", + "CAR", + "CHS", + "CLE", + "CTP", + "GSP", + "GYX", + "ILM", + "ILN", + "LWX", + "MHX", + "OKX", + "PBZ", + "PHI", + "RAH", + "RLX", + "RNK", + "ABQ", + "AMA", + "BMX", + "BRO", + "CRP", + "EPZ", + "EWX", + "FFC", + "FWD", + "HGX", + "HUN", + "JAN", + "JAX", + "KEY", + "LCH", + "LIX", + "LUB", + "LZK", + "MAF", + "MEG", + "MFL", + "MLB", + "MOB", + "MRX", + "OHX", + "OUN", + "SHV", + "SJT", + "SJU", + "TAE", + "TBW", + "TSA", + "ABR", + "APX", + "ARX", + "BIS", + "BOU", + "CYS", + "DDC", + "DLH", + "DMX", + "DTX", + "DVN", + "EAX", + "FGF", + "FSD", + "GID", + "GJT", + "GLD", + "GRB", + "GRR", + "ICT", + "ILX", + "IND", + "IWX", + "JKL", + "LBF", + "LMK", + "LOT", + "LSX", + "MKX", + "MPX", + "MQT", + "OAX", + "PAH", + "PUB", + "RIW", + "SGF", + "TOP", + "UNR", + "BOI", + "BYZ", + "EKA", + "FGZ", + "GGW", + "HNX", + "LKN", + "LOX", + "MFR", + "MSO", + "MTR", + "OTX", + "PDT", + "PIH", + "PQR", + "PSR", + "REV", + "SEW", + "SGX", + "SLC", + "STO", + "TFX", + "TWC", + "VEF", + "AER", + "AFC", + "AFG", + "AJK", + "ALU", + "GUM", + "HPA", + "HFO", + "PPG", + "STU", + "NH1", + "NH2", + "ONA", + "ONP" + ], + "type": "string", + "description": "Three-letter identifier for a NWS office." + }, + "NWSNationalHQId": { + "enum": [ + "NWS" + ], + "type": "string", + "description": "Three-letter identifier for NWS National HQ." + }, + "NWSOfficeId": { + "oneOf": [ + { + "$ref": "#/components/schemas/NWSForecastOfficeId" + }, + { + "$ref": "#/components/schemas/NWSRegionalHQId" + }, + { + "$ref": "#/components/schemas/NWSNationalHQId" + } + ] + }, + "NWSRegionalHQId": { + "enum": [ + "ARH", + "CRH", + "ERH", + "PRH", + "SRH", + "WRH" + ], + "type": "string", + "description": "Three-letter identifier for a NWS Regional HQ." + }, + "NWSZoneID": { + "pattern": "^(A[KLMNRSZ]|C[AOT]|D[CE]|F[LM]|G[AMU]|I[ADLN]|K[SY]|L[ACEHMOS]|M[ADEHINOPST]|N[CDEHJMVY]|O[HKR]|P[AHKMRSWZ]|S[CDL]|T[NX]|UT|V[AIT]|W[AIVY]|[HR]I)[CZ]\\d{3}$", + "type": "string", + "description": "UGC identifier for a NWS forecast zone or county.\nThe first two letters will correspond to either a state code or marine area code (see #/components/schemas/StateTerritoryCode and #/components/schemas/MarineAreaCode for lists of valid letter combinations).\nThe third letter will be Z for public/fire zone or C for county.\n" + }, + "NWSZoneType": { + "enum": [ + "land", + "marine", + "forecast", + "public", + "coastal", + "offshore", + "fire", + "county" + ], + "type": "string" + }, + "Observation": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "@type": { + "enum": [ + "wx:ObservationStation" + ], + "type": "string" + }, + "elevation": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "station": { + "type": "string", + "format": "uri" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "rawMessage": { + "type": "string" + }, + "textDescription": { + "type": "string" + }, + "icon": { + "type": "string", + "format": "uri", + "nullable": true, + "deprecated": true + }, + "presentWeather": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MetarPhenomenon" + } + }, + "temperature": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "dewpoint": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "windDirection": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "windSpeed": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "windGust": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "barometricPressure": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "seaLevelPressure": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "visibility": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "maxTemperatureLast24Hours": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "minTemperatureLast24Hours": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "precipitationLastHour": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "precipitationLast3Hours": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "precipitationLast6Hours": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "relativeHumidity": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "windChill": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "heatIndex": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "cloudLayers": { + "type": "array", + "items": { + "required": [ + "base", + "amount" + ], + "type": "object", + "properties": { + "base": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "amount": { + "$ref": "#/components/schemas/MetarSkyCoverage" + } + }, + "additionalProperties": false + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "ObservationGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Observation" + } + } + } + ] + }, + "ObservationJsonLd": { + "$ref": "#/components/schemas/Observation" + }, + "ObservationCollectionGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeatureCollection" + }, + { + "type": "object", + "properties": { + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Observation" + } + } + } + } + } + } + ] + }, + "ObservationCollectionJsonLd": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@graph": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Observation" + } + } + }, + "additionalProperties": false + }, + "ObservationStation": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "@type": { + "enum": [ + "wx:ObservationStation" + ], + "type": "string" + }, + "elevation": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "stationIdentifier": { + "type": "string" + }, + "name": { + "type": "string" + }, + "timeZone": { + "type": "string", + "format": "iana-time-zone-identifier" + }, + "forecast": { + "type": "string", + "description": "A link to the NWS public forecast zone containing this station.", + "format": "uri" + }, + "county": { + "type": "string", + "description": "A link to the NWS county zone containing this station.", + "format": "uri" + }, + "fireWeatherZone": { + "type": "string", + "description": "A link to the NWS fire weather forecast zone containing this station.", + "format": "uri" + } + }, + "additionalProperties": false + }, + "ObservationStationGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/ObservationStation" + } + } + } + ] + }, + "ObservationStationJsonLd": { + "allOf": [ + { + "$ref": "#/components/schemas/ObservationStation" + }, + { + "required": [ + "@context", + "geometry" + ], + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + } + } + } + ] + }, + "ObservationStationCollectionGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeatureCollection" + }, + { + "type": "object", + "properties": { + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/ObservationStation" + } + } + } + }, + "observationStations": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "pagination": { + "$ref": "#/components/schemas/PaginationInfo" + } + } + } + ] + }, + "ObservationStationCollectionJsonLd": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@graph": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ObservationStation" + } + }, + "observationStations": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "pagination": { + "$ref": "#/components/schemas/PaginationInfo" + } + }, + "additionalProperties": false + }, + "Office": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@type": { + "enum": [ + "GovernmentOrganization" + ], + "type": "string" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "address": { + "type": "object", + "properties": { + "@type": { + "enum": [ + "PostalAddress" + ], + "type": "string" + }, + "streetAddress": { + "type": "string" + }, + "addressLocality": { + "type": "string" + }, + "addressRegion": { + "type": "string" + }, + "postalCode": { + "type": "string" + } + }, + "additionalProperties": false + }, + "telephone": { + "type": "string" + }, + "faxNumber": { + "type": "string" + }, + "email": { + "type": "string" + }, + "sameAs": { + "type": "string", + "format": "uri" + }, + "nwsRegion": { + "type": "string" + }, + "parentOrganization": { + "type": "string", + "format": "uri" + }, + "responsibleCounties": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "responsibleForecastZones": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "responsibleFireZones": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "approvedObservationStations": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + } + }, + "additionalProperties": false + }, + "OfficeHeadline": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "id": { + "type": "string" + }, + "office": { + "type": "string", + "format": "uri" + }, + "important": { + "type": "boolean" + }, + "issuanceTime": { + "type": "string", + "format": "date-time" + }, + "link": { + "type": "string", + "format": "uri" + }, + "name": { + "type": "string" + }, + "title": { + "type": "string" + }, + "summary": { + "type": "string", + "nullable": true + }, + "content": { + "type": "string" + } + } + }, + "OfficeHeadlineCollection": { + "required": [ + "@context", + "@graph" + ], + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@graph": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OfficeHeadline" + } + } + }, + "additionalProperties": false + }, + "PaginationInfo": { + "required": [ + "next" + ], + "type": "object", + "properties": { + "next": { + "type": "string", + "description": "A link to the next page of records", + "format": "uri" + } + }, + "description": "Links for retrieving more data from paged data sets", + "additionalProperties": false + }, + "Point": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "@type": { + "enum": [ + "wx:Point" + ], + "type": "string" + }, + "cwa": { + "$ref": "#/components/schemas/NWSForecastOfficeId" + }, + "forecastOffice": { + "type": "string", + "format": "uri" + }, + "gridId": { + "$ref": "#/components/schemas/NWSForecastOfficeId" + }, + "gridX": { + "minimum": 0, + "type": "integer" + }, + "gridY": { + "minimum": 0, + "type": "integer" + }, + "forecast": { + "type": "string", + "format": "uri" + }, + "forecastHourly": { + "type": "string", + "format": "uri" + }, + "forecastGridData": { + "type": "string", + "format": "uri" + }, + "observationStations": { + "type": "string", + "format": "uri" + }, + "relativeLocation": { + "oneOf": [ + { + "$ref": "#/components/schemas/RelativeLocationGeoJson" + }, + { + "$ref": "#/components/schemas/RelativeLocationJsonLd" + } + ] + }, + "forecastZone": { + "type": "string", + "format": "uri" + }, + "county": { + "type": "string", + "format": "uri" + }, + "fireWeatherZone": { + "type": "string", + "format": "uri" + }, + "timeZone": { + "type": "string" + }, + "radarStation": { + "type": "string" + } + } + }, + "PointGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Point" + } + } + } + ] + }, + "PointJsonLd": { + "allOf": [ + { + "$ref": "#/components/schemas/Point" + }, + { + "required": [ + "@context", + "geometry" + ], + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + } + } + } + ] + }, + "PointString": { + "pattern": "^(-?\\d+(?:\\.\\d+)?),(-?\\d+(?:\\.\\d+)?)$", + "type": "string" + }, + "ProblemDetail": { + "required": [ + "type", + "title", + "status", + "detail", + "instance", + "correlationId" + ], + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "A URI reference (RFC 3986) that identifies the problem type. This is only an identifier and is not necessarily a resolvable URL.\n", + "format": "uri", + "default": "about:blank", + "example": "urn:noaa:nws:api:UnexpectedProblem" + }, + "title": { + "type": "string", + "description": "A short, human-readable summary of the problem type.", + "example": "Unexpected Problem" + }, + "status": { + "maximum": 999, + "minimum": 100, + "type": "number", + "description": "The HTTP status code (RFC 7231, Section 6) generated by the origin server for this occurrence of the problem.\n", + "example": 500 + }, + "detail": { + "type": "string", + "description": "A human-readable explanation specific to this occurrence of the problem.", + "example": "An unexpected problem has occurred." + }, + "instance": { + "type": "string", + "description": "A URI reference (RFC 3986) that identifies the specific occurrence of the problem. This is only an identifier and is not necessarily a resolvable URL.\n", + "format": "uri", + "example": "urn:noaa:nws:api:request:493c3a1d-f87e-407f-ae2c-24483f5aab63" + }, + "correlationId": { + "type": "string", + "description": "A unique identifier for the request, used for NWS debugging purposes. Please include this identifier with any correspondence to help us investigate your issue.\n", + "example": "493c3a1d-f87e-407f-ae2c-24483f5aab63" + } + }, + "description": "Detail about an error. This document conforms to RFC 7807 (Problem Details for HTTP APIs).", + "additionalProperties": true + }, + "QuantitativeValue": { + "type": "object", + "properties": { + "value": { + "type": "number", + "description": "A measured value", + "nullable": true + }, + "maxValue": { + "type": "number", + "description": "The maximum value of a range of measured values" + }, + "minValue": { + "type": "number", + "description": "The minimum value of a range of measured values" + }, + "unitCode": { + "$ref": "#/components/schemas/UnitOfMeasure" + }, + "qualityControl": { + "enum": [ + "Z", + "C", + "S", + "V", + "X", + "Q", + "G", + "B", + "T" + ], + "type": "string", + "description": "For values in observation records, the quality control flag from the MADIS system. The definitions of these flags can be found at https://madis.ncep.noaa.gov/madis_sfc_qc_notes.shtml\n" + } + }, + "description": "A structured value representing a measurement and its unit of measure. This object is a slighly modified version of the schema.org definition at https://schema.org/QuantitativeValue\n", + "additionalProperties": false + }, + "RegionCode": { + "oneOf": [ + { + "$ref": "#/components/schemas/LandRegionCode" + }, + { + "$ref": "#/components/schemas/MarineRegionCode" + } + ] + }, + "RelativeLocation": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "distance": { + "$ref": "#/components/schemas/QuantitativeValue" + }, + "bearing": { + "$ref": "#/components/schemas/QuantitativeValue" + } + } + }, + "RelativeLocationGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/RelativeLocation" + } + } + } + ] + }, + "RelativeLocationJsonLd": { + "allOf": [ + { + "$ref": "#/components/schemas/RelativeLocation" + }, + { + "required": [ + "geometry" + ], + "type": "object", + "properties": { + "geometry": { + "$ref": "#/components/schemas/GeometryString" + } + } + } + ] + }, + "Sigmet": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uri" + }, + "issueTime": { + "type": "string", + "format": "date-time" + }, + "fir": { + "type": "string", + "nullable": true + }, + "atsu": { + "$ref": "#/components/schemas/ATSUIdentifier" + }, + "sequence": { + "type": "string", + "nullable": true + }, + "phenomenon": { + "type": "string", + "format": "uri", + "nullable": true + }, + "start": { + "type": "string", + "format": "date-time" + }, + "end": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false + }, + "SigmetCollectionGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeatureCollection" + }, + { + "type": "object", + "properties": { + "features": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SigmetGeoJson" + } + } + } + } + ] + }, + "SigmetGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Sigmet" + } + } + } + ] + }, + "SigmetSequenceNumber": { + "type": "string" + }, + "StateTerritoryCode": { + "enum": [ + "AL", + "AK", + "AS", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DE", + "DC", + "FL", + "GA", + "GU", + "HI", + "ID", + "IL", + "IN", + "IA", + "KS", + "KY", + "LA", + "ME", + "MD", + "MA", + "MI", + "MN", + "MS", + "MO", + "MT", + "NE", + "NV", + "NH", + "NJ", + "NM", + "NY", + "NC", + "ND", + "OH", + "OK", + "OR", + "PA", + "PR", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VT", + "VI", + "VA", + "WA", + "WV", + "WI", + "WY", + "MP", + "PW", + "FM", + "MH" + ], + "type": "string" + }, + "TextProduct": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "id": { + "type": "string" + }, + "wmoCollectiveId": { + "type": "string" + }, + "issuingOffice": { + "type": "string" + }, + "issuanceTime": { + "type": "string", + "format": "date-time" + }, + "productCode": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productText": { + "type": "string" + } + }, + "additionalProperties": false + }, + "TextProductCollection": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@graph": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TextProduct" + } + } + }, + "additionalProperties": false + }, + "TextProductTypeCollection": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@graph": { + "type": "array", + "items": { + "required": [ + "productCode", + "productName" + ], + "type": "object", + "properties": { + "productCode": { + "type": "string" + }, + "productName": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "TextProductLocationCollection": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "locations": { + "type": "object", + "additionalProperties": { + "type": "string", + "nullable": true + } + } + }, + "additionalProperties": false + }, + "Time": { + "pattern": "^([01][0-9]|2[0-3])[0-5][0-9]$", + "type": "string", + "description": "A time (in HHMM format). This is always specified in UTC (Zulu) time." + }, + "UnitOfMeasure": { + "pattern": "^((wmo|uc|wmoUnit|nwsUnit):)?.*$", + "type": "string", + "description": "A string denoting a unit of measure, expressed in the format \"{unit}\" or \"{namespace}:{unit}\".\nUnits with the namespace \"wmo\" or \"wmoUnit\" are defined in the World Meteorological Organization Codes Registry at http://codes.wmo.int/common/unit and should be canonically resolvable to http://codes.wmo.int/common/unit/{unit}.\nUnits with the namespace \"nwsUnit\" are currently custom and do not align to any standard.\nUnits with no namespace or the namespace \"uc\" are compliant with the Unified Code for Units of Measure syntax defined at https://unitsofmeasure.org/. This also aligns with recent versions of the Geographic Markup Language (GML) standard, the IWXXM standard, and OGC Observations and Measurements v2.0 (ISO/DIS 19156).\nNamespaced units are considered deprecated. We will be aligning API to use the same standards as GML/IWXXM in the future.\n" + }, + "Zone": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + }, + "@id": { + "type": "string", + "format": "uri" + }, + "@type": { + "enum": [ + "wx:Zone" + ], + "type": "string" + }, + "id": { + "$ref": "#/components/schemas/NWSZoneID" + }, + "type": { + "$ref": "#/components/schemas/NWSZoneType" + }, + "name": { + "type": "string" + }, + "effectiveDate": { + "type": "string", + "format": "date-time" + }, + "expirationDate": { + "type": "string", + "format": "date-time" + }, + "state": { + "oneOf": [ + { + "$ref": "#/components/schemas/StateTerritoryCode" + }, + { + "enum": [ + "" + ], + "type": "string", + "nullable": true + } + ] + }, + "cwa": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NWSForecastOfficeId" + } + }, + "forecastOffices": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "timeZone": { + "type": "array", + "items": { + "type": "string", + "format": "iana-time-zone-identifier" + } + }, + "observationStations": { + "type": "array", + "items": { + "type": "string", + "format": "uri" + } + }, + "radarStation": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ZoneGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Zone" + } + } + } + ] + }, + "ZoneJsonLd": { + "$ref": "#/components/schemas/Zone" + }, + "ZoneCollectionGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeatureCollection" + }, + { + "type": "object", + "properties": { + "features": { + "type": "array", + "items": { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/Zone" + } + } + } + } + } + } + ] + }, + "ZoneCollectionJsonLd": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "@graph": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Zone" + } + } + }, + "additionalProperties": false + }, + "ZoneForecast": { + "type": "object", + "properties": { + "@context": { + "$ref": "#/components/schemas/JsonLdContext" + }, + "geometry": { + "$ref": "#/components/schemas/GeometryString" + }, + "zone": { + "type": "string", + "description": "An API link to the zone this forecast is for.", + "format": "uri" + }, + "updated": { + "type": "string", + "description": "The time this zone forecast product was published.", + "format": "date-time" + }, + "periods": { + "type": "array", + "items": { + "required": [ + "number", + "name", + "detailedForecast" + ], + "type": "object", + "properties": { + "number": { + "type": "integer", + "description": "A sequential identifier number." + }, + "name": { + "type": "string", + "description": "A textual description of the period.", + "example": "This Afternoon" + }, + "detailedForecast": { + "type": "string", + "description": "A detailed textual forecast for the period." + } + }, + "additionalProperties": false + }, + "description": "An array of forecast periods." + } + }, + "description": "An object representing a zone area forecast.", + "additionalProperties": false + }, + "ZoneForecastGeoJson": { + "allOf": [ + { + "$ref": "#/components/schemas/GeoJsonFeature" + }, + { + "type": "object", + "properties": { + "properties": { + "$ref": "#/components/schemas/ZoneForecast" + } + } + } + ] + }, + "ZoneForecastJsonLd": { + "$ref": "#/components/schemas/ZoneForecast" + } + }, + "responses": { + "AlertCollection": { + "description": "A collection of alerts.", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/AlertCollectionGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/AlertCollectionJsonLd" + } + }, + "application/atom+xml": { + "schema": { + "$ref": "#/components/schemas/AlertAtomFeed" + } + } + }, + "x-url-content-negotiation-extensions": { + "json": "application/geo+json", + "atom": "application/atom+xml" + } + }, + "Error": { + "description": "An error response.", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/problem+json": { + "schema": { + "$ref": "#/components/schemas/ProblemDetail" + } + } + } + }, + "GridpointForecast": { + "description": "A forecast for a gridpoint.", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/GridpointForecastGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/GridpointForecastJsonLd" + } + }, + "application/vnd.noaa.dwml+xml": { + "schema": {} + } + } + }, + "Observation": { + "description": "An observation record.", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ObservationGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ObservationJsonLd" + } + }, + "application/vnd.noaa.uswx+xml": { + "schema": {} + }, + "application/vnd.noaa.obs+xml": { + "schema": {} + } + } + }, + "ObservationCollection": { + "description": "A collection of observation records.", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ObservationCollectionGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ObservationCollectionJsonLd" + } + } + } + }, + "ObservationStationCollection": { + "description": "A collection of observation stations.", + "headers": { + "X-Correlation-Id": { + "$ref": "#/components/headers/CorrelationId" + }, + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "X-Server-Id": { + "$ref": "#/components/headers/ServerId" + } + }, + "content": { + "application/geo+json": { + "schema": { + "$ref": "#/components/schemas/ObservationStationCollectionGeoJson" + } + }, + "application/ld+json": { + "schema": { + "$ref": "#/components/schemas/ObservationStationCollectionJsonLd" + } + } + } + } + }, + "parameters": { + "AlertArea": { + "name": "area", + "in": "query", + "description": "State/territory code or marine area code\nThis parameter is incompatible with the following parameters: point, region, region_type, zone\n", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AreaCode" + } + } + }, + "AlertCertainty": { + "name": "certainty", + "in": "query", + "description": "Certainty (observed, likely, possible, unlikely, unknown)", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AlertCertainty" + } + } + }, + "AlertCode": { + "name": "code", + "in": "query", + "description": "Event code", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "pattern": "^\\w{3}$", + "type": "string" + } + } + }, + "AlertEventName": { + "name": "event", + "in": "query", + "description": "Event name", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "pattern": "^[A-Za-z0-9 ]+$", + "type": "string" + } + } + }, + "AlertMessageType": { + "name": "message_type", + "in": "query", + "description": "Message type (alert, update, cancel)", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "enum": [ + "alert", + "update", + "cancel" + ], + "type": "string" + } + } + }, + "AlertPoint": { + "name": "point", + "in": "query", + "description": "Point (latitude,longitude)\nThis parameter is incompatible with the following parameters: area, region, region_type, zone\n", + "schema": { + "$ref": "#/components/schemas/PointString" + } + }, + "AlertRegion": { + "name": "region", + "in": "query", + "description": "Marine region code\nThis parameter is incompatible with the following parameters: area, point, region_type, zone\n", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MarineRegionCode" + } + } + }, + "AlertRegionType": { + "name": "region_type", + "in": "query", + "description": "Region type (land or marine)\nThis parameter is incompatible with the following parameters: area, point, region, zone\n", + "schema": { + "enum": [ + "land", + "marine" + ], + "type": "string" + } + }, + "AlertSeverity": { + "name": "severity", + "in": "query", + "description": "Severity (extreme, severe, moderate, minor, unknown)", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AlertSeverity" + } + } + }, + "AlertStatus": { + "name": "status", + "in": "query", + "description": "Status (actual, exercise, system, test, draft)", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "enum": [ + "actual", + "exercise", + "system", + "test", + "draft" + ], + "type": "string" + } + } + }, + "AlertUrgency": { + "name": "urgency", + "in": "query", + "description": "Urgency (immediate, expected, future, past, unknown)", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AlertUrgency" + } + } + }, + "AlertZone": { + "name": "zone", + "in": "query", + "description": "Zone ID (forecast or county)\nThis parameter is incompatible with the following parameters: area, point, region, region_type\n", + "style": "form", + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NWSZoneID" + } + } + }, + "ATSUIdentifier": { + "name": "atsu", + "in": "path", + "description": "ATSU identifier", + "required": true, + "schema": { + "$ref": "#/components/schemas/ATSUIdentifier" + } + }, + "Date": { + "name": "date", + "in": "path", + "description": "Date (YYYY-MM-DD format)", + "required": true, + "schema": { + "$ref": "#/components/schemas/Date" + } + }, + "GridpointForecastFeatureFlags": { + "name": "Feature-Flags", + "in": "header", + "description": "Enable future and experimental features (see documentation for more info):\n* forecast_temperature_qv: Represent temperature as QuantitativeValue\n* forecast_wind_speed_qv: Represent wind speed as QuantitativeValue\n", + "required": false, + "style": "simple", + "explode": false, + "schema": { + "type": "array", + "items": { + "enum": [ + "forecast_temperature_qv", + "forecast_wind_speed_qv" + ], + "type": "string" + } + } + }, + "GridpointForecastUnits": { + "name": "units", + "in": "query", + "description": "Use US customary or SI (metric) units in textual output", + "schema": { + "$ref": "#/components/schemas/GridpointForecastUnits" + } + }, + "GridpointWFO": { + "name": "wfo", + "in": "path", + "description": "Forecast office ID", + "required": true, + "schema": { + "$ref": "#/components/schemas/NWSForecastOfficeId" + } + }, + "GridpointX": { + "name": "x", + "in": "path", + "description": "Forecast grid X coordinate", + "required": true, + "schema": { + "minimum": 0, + "type": "integer" + } + }, + "GridpointY": { + "name": "y", + "in": "path", + "description": "Forecast grid Y coordinate", + "required": true, + "schema": { + "minimum": 0, + "type": "integer" + } + }, + "Limit": { + "name": "limit", + "in": "query", + "description": "Limit", + "schema": { + "maximum": 500, + "minimum": 1, + "type": "integer", + "default": 500 + } + }, + "NWSCenterWeatherServiceUnitId": { + "name": "cwsuId", + "in": "path", + "description": "NWS CWSU ID", + "required": true, + "schema": { + "$ref": "#/components/schemas/NWSCenterWeatherServiceUnitId" + } + }, + "NWSOfficeId": { + "name": "officeId", + "in": "path", + "description": "NWS office ID", + "required": true, + "schema": { + "$ref": "#/components/schemas/NWSOfficeId" + } + }, + "NWSZoneId": { + "name": "zoneId", + "in": "path", + "description": "NWS public zone/county identifier", + "required": true, + "schema": { + "$ref": "#/components/schemas/NWSZoneID" + } + }, + "ObservationStationId": { + "name": "stationId", + "in": "path", + "description": "Observation station ID", + "required": true, + "schema": { + "type": "string" + } + }, + "PaginationCursor": { + "name": "cursor", + "in": "query", + "description": "Pagination cursor", + "schema": { + "type": "string" + } + }, + "PathPoint": { + "name": "point", + "in": "path", + "description": "Point (latitude, longitude)", + "required": true, + "schema": { + "$ref": "#/components/schemas/PointString" + } + }, + "QueryDate": { + "name": "date", + "in": "query", + "description": "Date (YYYY-MM-DD format)", + "schema": { + "$ref": "#/components/schemas/Date" + } + }, + "QueryStartTime": { + "name": "start", + "in": "query", + "description": "Start time", + "schema": { + "type": "string", + "format": "date-time" + } + }, + "QueryEndTime": { + "name": "end", + "in": "query", + "description": "End time", + "schema": { + "type": "string", + "format": "date-time" + } + }, + "SigmetSequenceNumber": { + "name": "sequence", + "in": "path", + "description": "SIGMET sequence number", + "required": true, + "schema": { + "$ref": "#/components/schemas/SigmetSequenceNumber" + } + }, + "Time": { + "name": "time", + "in": "path", + "description": "Time (HHMM format). This time is always specified in UTC (Zulu) time.", + "required": true, + "schema": { + "$ref": "#/components/schemas/Time" + } + } + }, + "headers": { + "CorrelationId": { + "description": "A unique identifier for the request, used for NWS debugging purposes. Please include this identifier with any correspondence to help us investigate your issue.\n", + "schema": { + "type": "string" + } + }, + "RequestId": { + "description": "A unique identifier for the request, used for NWS debugging purposes. Please include this identifier with any correspondence to help us investigate your issue.\n", + "schema": { + "type": "string" + } + }, + "ServerId": { + "description": "The identifier of the server that generated the response, used for NWS debugging purposes. Please include this identifier with any correspondence to help us investigate your issue.\n", + "schema": { + "type": "string" + } + } + }, + "securitySchemes": { + "userAgent": { + "type": "apiKey", + "description": "We require that all consumers of the API include a User-Agent header in requests. This is due to a high number of scripts exhibiting abusive behavior (intentional or unintentional). We recommend setting the value to something that identifies your application and includes a contact email. This will help us contact you if we notice unusual behavior and also aid in troubleshooting issues.\nThe API remains open and free to use and there are no limits imposed based on the User-Agent string.\nThis mechanism will be replaced with a more typical API key system at a later date.\n", + "name": "User-Agent", + "in": "header" + } + } + }, + "security": [ + { + "userAgent": [] + } + ], + "externalDocs": { + "description": "Full API documentation", + "url": "https://www.weather.gov/documentation/services-web-api" + } +} \ No newline at end of file diff --git a/scripts/evaluation/data/specs/weather/weather-4.json b/scripts/evaluation/data/specs/weather/weather-4.json new file mode 100644 index 00000000..b1d50130 --- /dev/null +++ b/scripts/evaluation/data/specs/weather/weather-4.json @@ -0,0 +1,539 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Xweather OpenAI Plugin", + "description": " This API Provides 3 public endpoints:/weather/summary/{location} : The current weather in a user specified location/weather/forecast/{location} : The next 4 days of weather forecasts in a user specified location /radar/{location} : The current weather radar image in a user specified locationFor any of these, location required and should be user specified - ask the user for a city if they haven't specified one. Valid location formats include 'city name, state', 'city name, country','latitude, longitude', 'zip code', or 'airport code'. Never provide a default location of New York.For /weather/forecast, the reply is the weather forecast for next 4 days, including the following information: date, maximum temperature, minimum temperature, wind direction, wind speed, precipitation, snow, weather conditions, warnings, and UV index. The API returns the next 4 days of weather forecasts. If the user asks for more than 4 days, return the next 4 days with a message showing that's all that's available. By default, return 4 days of weather forecast as a paragraph of text.For /weather/summary, the reply is the current weather, including the following information: date, temperature, what the temperature feels like to a person (feelsLike), wind direction, wind speed, maximum gust wind speed, precipitation, snow, weather conditions, active weather alerts, and UV index. If the user hasn't specified an output format, return the data as a paragraph of text.For /weather/forecast, and /weather/summary, provide local units: If the user specified location is in the United States, show only fahrenheit, inches and MPH. If the location is in the UK, show only celsius, mm, and MPH. If the location is in any other country, use celsius, mm, and KPH. Do not show units in other formats unless requested by the user./radar returns a current radar image as markdown. Provide the images in Markdown format so the user can see it. Do not add links, only images", + "version": "0.1.0" + }, + "paths": { + "/weather/summary/{location}": { + "get": { + "summary": "Weather Conditions", + "description": "Get the current weather conditions, including active weather alerts. Requires a user-provided location.", + "operationId": "get_summary_weather_summary__location__get", + "parameters": [ + { + "description": "A Valid AerisWeather API Location. The location may be a 'city name, state', 'city name, country', 'latitude, longitude', 'zip code', or 'airport code'", + "required": true, + "schema": { + "title": "Location", + "type": "string", + "description": "A Valid AerisWeather API Location. The location may be a 'city name, state', 'city name, country', 'latitude, longitude', 'zip code', or 'airport code'" + }, + "name": "location", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WeatherConditionsOutput" + } + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/weather/forecast/{location}": { + "get": { + "summary": "Weather Forecast for the next 4 days", + "description": "Get the weather forecast, including warnings, for the next 4 days. Requires a user-provided location.", + "operationId": "get_forecast_weather_forecast__location__get", + "parameters": [ + { + "description": "A Valid AerisWeather API Location. The location may be a 'city name, state', 'city name, country', 'latitude, longitude', 'zip code', or 'airport code'", + "required": true, + "schema": { + "title": "Location", + "type": "string", + "description": "A Valid AerisWeather API Location. The location may be a 'city name, state', 'city name, country', 'latitude, longitude', 'zip code', or 'airport code'" + }, + "name": "location", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WeatherForecastsOutput" + } + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/radar/{location}": { + "get": { + "summary": "Precipitation Radar", + "description": "Returns markdown for current weather radar image. Requires a user-provided location.", + "operationId": "get_radar_radar__location__get", + "parameters": [ + { + "description": "A Valid AerisWeather API Location. The location may be a 'city name, state', 'city name, country', 'latitude, longitude', 'zip code', or 'airport code'", + "required": true, + "schema": { + "title": "Location", + "type": "string", + "description": "A Valid AerisWeather API Location. The location may be a 'city name, state', 'city name, country', 'latitude, longitude', 'zip code', or 'airport code'" + }, + "name": "location", + "in": "path" + }, + { + "description": "A map zoom level between 0 and 15. The default is 7.", + "required": false, + "schema": { + "title": "Zoom", + "type": "integer", + "description": "A map zoom level between 0 and 15. The default is 7.", + "default": 7 + }, + "name": "zoom", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WeatherRadar" + } + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/version": { + "get": { + "summary": "Version", + "operationId": "version_version_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/": { + "get": { + "summary": "Heartbeat", + "operationId": "heartbeat__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + } + } + } + }, + "NotFoundError": { + "title": "NotFoundError", + "required": [ + "code", + "detail" + ], + "type": "object", + "properties": { + "code": { + "title": "Code", + "type": "string" + }, + "detail": { + "title": "Detail", + "type": "string" + } + } + }, + "ValidationError": { + "title": "ValidationError", + "required": [ + "loc", + "msg", + "type" + ], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + } + }, + "msg": { + "title": "Message", + "type": "string" + }, + "type": { + "title": "Error Type", + "type": "string" + } + } + }, + "WeatherAlerts": { + "title": "WeatherAlerts", + "required": [ + "name", + "beginDateTime", + "expireDateTIme" + ], + "type": "object", + "properties": { + "name": { + "title": "Name", + "type": "string", + "description": "The name of the alert" + }, + "beginDateTime": { + "title": "Begindatetime", + "type": "string", + "description": "The date and time that the alert begins in ISO 8601 format" + }, + "expireDateTIme": { + "title": "Expiredatetime", + "type": "string", + "description": "The date and time that the alert expires in ISO 8601 format" + } + } + }, + "WeatherConditions": { + "title": "WeatherConditions", + "required": [ + "dateTimeISO", + "tempC", + "tempF", + "feelsLikeC", + "feelsLikeF", + "windDir", + "windSpeedKPH", + "windGustKPH", + "precipRateMM", + "precipRateIN", + "weather", + "uvi" + ], + "type": "object", + "properties": { + "dateTimeISO": { + "title": "Datetimeiso", + "type": "string", + "description": "The date and time of the forecast in ISO 8601 format" + }, + "tempC": { + "title": "Tempc", + "type": "number", + "description": "The temperature in degrees Celsius" + }, + "tempF": { + "title": "Tempf", + "type": "number", + "description": "The temperature in degrees Fahrenheit" + }, + "feelsLikeC": { + "title": "Feelslikec", + "type": "number", + "description": "The temperature that it feels like in degrees Celsius" + }, + "feelsLikeF": { + "title": "Feelslikef", + "type": "number", + "description": "The temperature that it feels like in degrees Fahrenheit" + }, + "windDir": { + "title": "Winddir", + "type": "string", + "description": "The direction of the wind in degrees" + }, + "windSpeedKPH": { + "title": "Windspeedkph", + "type": "number", + "description": "The wind speed in kilometers per hour" + }, + "windGustKPH": { + "title": "Windgustkph", + "type": "number", + "description": "The wind gust speed in kilometers per hour" + }, + "precipRateMM": { + "title": "Precipratemm", + "type": "number", + "description": "The precipitation rate in millimeters per hour" + }, + "precipRateIN": { + "title": "Precipratein", + "type": "number", + "description": "The precipitation rate in inches per hour" + }, + "weather": { + "title": "Weather", + "type": "string", + "description": "The weather conditions" + }, + "uvi": { + "title": "Uvi", + "type": "integer", + "description": "The UV index" + } + } + }, + "WeatherConditionsOutput": { + "title": "WeatherConditionsOutput", + "required": [ + "alerts", + "conditions" + ], + "type": "object", + "properties": { + "alerts": { + "title": "Alerts", + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherAlerts" + }, + "description": "A list of active weather warnings, watches and advisories." + }, + "conditions": { + "title": "Conditions", + "allOf": [ + { + "$ref": "#/components/schemas/WeatherConditions" + } + ], + "description": "The current weather conditions" + } + } + }, + "WeatherForecast": { + "title": "WeatherForecast", + "required": [ + "dateTimeISO", + "maxTempC", + "maxTempF", + "minTempC", + "minTempF", + "windDir", + "windSpeedKPH", + "windSpeedMPH", + "precipMM", + "precipIN", + "snowCM", + "snowIN", + "weather", + "uvi" + ], + "type": "object", + "properties": { + "dateTimeISO": { + "title": "Datetimeiso", + "type": "string", + "description": "The date and time of the forecast in ISO 8601 format" + }, + "maxTempC": { + "title": "Maxtempc", + "type": "integer", + "description": "The maximum temperature in degrees Celsius" + }, + "maxTempF": { + "title": "Maxtempf", + "type": "integer", + "description": "The maximum temperature in degrees Fahrenheit" + }, + "minTempC": { + "title": "Mintempc", + "type": "integer", + "description": "The minimum temperature in degrees Celsius" + }, + "minTempF": { + "title": "Mintempf", + "type": "integer", + "description": "The minimum temperature in degrees Fahrenheit" + }, + "windDir": { + "title": "Winddir", + "type": "string", + "description": "The direction of the wind in degrees" + }, + "windSpeedKPH": { + "title": "Windspeedkph", + "type": "integer", + "description": "The wind speed in kilometers per hour" + }, + "windSpeedMPH": { + "title": "Windspeedmph", + "type": "integer", + "description": "The wind speed in miles per hour" + }, + "precipMM": { + "title": "Precipmm", + "type": "number", + "description": "The amount of precipitation in millimeters" + }, + "precipIN": { + "title": "Precipin", + "type": "number", + "description": "The amount of precipitation in inches" + }, + "snowCM": { + "title": "Snowcm", + "type": "number", + "description": "The amount of snow in centimeters" + }, + "snowIN": { + "title": "Snowin", + "type": "number", + "description": "The amount of snow in inches" + }, + "weather": { + "title": "Weather", + "type": "string", + "description": "The weather conditions" + }, + "uvi": { + "title": "Uvi", + "type": "integer", + "description": "The UV index" + } + } + }, + "WeatherForecastsOutput": { + "title": "WeatherForecastsOutput", + "required": [ + "forecasts" + ], + "type": "object", + "properties": { + "forecasts": { + "title": "Forecasts", + "type": "array", + "items": { + "$ref": "#/components/schemas/WeatherForecast" + } + } + } + }, + "WeatherRadar": { + "title": "WeatherRadar", + "required": [ + "radar", + "zoom" + ], + "type": "object", + "properties": { + "radar": { + "title": "Radar", + "type": "string", + "description": "The URL of the radar image" + }, + "zoom": { + "title": "Zoom", + "type": "integer", + "description": "The map zoom level of the radar image" + } + } + } + } + } +} \ No newline at end of file diff --git a/scripts/evaluation/scripts/es-scores.ipynb b/scripts/evaluation/scripts/es-scores.ipynb new file mode 100644 index 00000000..45c9b197 --- /dev/null +++ b/scripts/evaluation/scripts/es-scores.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2024-05-24T09:49:45.043918Z", + "start_time": "2024-05-24T09:49:45.035407Z" + } + }, + "source": [ + "import math\n", + "import requests\n", + "\n", + "from yaml import safe_load\n", + "from typing import Callable\n", + "from scipy.stats import sem\n", + "from matplotlib import pyplot" + ], + "outputs": [], + "execution_count": 264 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:49:45.051969Z", + "start_time": "2024-05-24T09:49:45.046137Z" + } + }, + "cell_type": "code", + "source": [ + "# Retrieve configuration file for elastic\n", + "with open(\"../../../config/debug.config.yml\") as config:\n", + " config_file = safe_load(config)[\"backend\"]" + ], + "id": "79edc88b1d8ce146", + "outputs": [], + "execution_count": 265 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:49:45.056151Z", + "start_time": "2024-05-24T09:49:45.053238Z" + } + }, + "cell_type": "code", + "source": "queries = [\"weather forecast service\", \"maps service\", \"digital book library\", \"tournaments and leaderboards\"]", + "id": "575dccd78edb0e52", + "outputs": [], + "execution_count": 266 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:49:45.060398Z", + "start_time": "2024-05-24T09:49:45.057292Z" + } + }, + "cell_type": "code", + "source": [ + "def perform_search_query(query: str) -> int:\n", + " url: str = f\"http://localhost:{config_file['port']}/api/v1/search?k=10\"\n", + " body: dict[str, str|list[str]] = {\n", + " \"fields\": [\"metadata\"]\n", + " }\n", + " \n", + " if query: body[\"fragment\"] = query\n", + " response = requests.post(url, json=body).json()\n", + " scores = [doc[\"metadata\"][\"score\"] for doc in response]\n", + " \n", + " return scores" + ], + "id": "2a74396c061d9f20", + "outputs": [], + "execution_count": 267 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:49:45.066631Z", + "start_time": "2024-05-24T09:49:45.062047Z" + } + }, + "cell_type": "code", + "source": [ + "def display_graph(query_list: list[str]):\n", + " fig, axes = pyplot.subplots(2, 2, figsize=(9, 7))\n", + " fig.suptitle(\"Elasticsearch score @K\")\n", + " \n", + " for idx, query in enumerate(query_list):\n", + " position_bin: str = bin(idx).replace(\"0b\", \"\")\n", + " position: list[int] = [0, int(position_bin)] if len(position_bin) == 1 else [int(position_bin[0]), int(position_bin[1])]\n", + " \n", + " xs = [str(i) for i in range(1, 11)]\n", + " ys = perform_search_query(query)\n", + " \n", + " axes[position[0], position[1]].scatter(xs, ys)\n", + " axes[position[0], position[1]].plot(xs, ys)\n", + " axes[position[0], position[1]].set_ylim(0.60, 0.85)\n", + " axes[position[0], position[1]].set_title(query, wrap=True)\n", + " axes[position[0], position[1]].set_xlabel(\"Position of retrieved document\")\n", + " axes[position[0], position[1]].set_ylabel(\"Elasticsearch score\")\n", + " \n", + " fig.tight_layout(h_pad=3)\n", + " pyplot.savefig(\"out.pdf\")" + ], + "id": "e9133e0058f786e5", + "outputs": [], + "execution_count": 268 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:49:45.571351Z", + "start_time": "2024-05-24T09:49:45.067495Z" + } + }, + "cell_type": "code", + "source": "display_graph(queries)", + "id": "be8a9f121cf278b3", + "outputs": [ + { + "data": { + "text/plain": [ + "
    " + ], + "image/png": "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hBC4cOECXF1d9Rc8ERGRDpjriIjIEAx6uea4cePw008/YdWqVbh69SrGjh2LiIgIDB06FEDWpSUvPpOod+/esLe3x8CBAxEWFoYjR45g4sSJGDRoEBQKBQBg5syZ2LdvH+7cuYMLFy5g8ODBuHDhgmaeRERExYm5joiIiptBH6HQs2dPxMbGYtasWYiMjES1atWwe/dueHp6AgAiIyMRERGh6W9lZYWQkBB88sknqFu3Luzt7dGjRw/MmTNH0yc+Ph4ff/wxoqKioFQqUatWLRw5cgT169cv9vUjIiJiriMiouImEUIIQwdR0iQmJkKpVCIhIQE2NjaGDoeIiHTE/bfuuK2IiEofXffdBh9dk4iIiIiIiPSHRR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGREWeUREREREREaERR4REREREZERYZFHRERERERkRFjkERERERERGRGDF3lLly6Ft7c35HI56tSpg6NHj+bbPy0tDVOmTIGnpydkMhl8fHywatUqrT5btmyBn58fZDIZ/Pz8sG3btqJcBSIionwx1xERUXEyaJG3adMmjBkzBlOmTMH58+fRuHFjtG/fHhEREXlO06NHDxw8eBArV67E9evXsWHDBlSpUkXz+YkTJ9CzZ0/07dsXFy9eRN++fdGjRw+cPHmyOFaJiIhIC3MdEREVN4kQQhhq4Q0aNEDt2rURFBSkafP19UW3bt0wb968HP337t2L999/H3fu3IGdnV2u8+zZsycSExOxZ88eTVu7du1ga2uLDRs26BRXYmIilEolEhISYGNjU8i1IiIiQymJ+2/mOiIi0hdd991mxRiT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GBkcHIzPP/8cdevWRd26dfH555/D2toaQ4YMKZP4SD/lLdcxz+mnPOY65jnDMNfph7kOnELB2A4dOiQAFHqMGDHCZDHpigeAWL16tcliGj16tPD09BQymUxUr15ddOnSRezdu9dk8ehi6mGlhRBi8ODBws3NTVhaWgp3d3cxcOBAceXKFZPGJIQQO3bsEP7+/kIul4v69euLFStWmDoksWfPHgFAXL9+3dShCCGESE1NFVOmTBG1atUSCoVCeHt7i1mzZons7GyTxrVp0ybh7e0tZDKZcHV1FZMmTRLJyclltv2SjpFqtVrMmTNHuLq6CrlcLjp06CAuXbpUZvGRfspbrmOee36mznXMc4ZhrtMPc50QEiGEMF7JSERERERERKbEPnlERERERERmhEUeERERERGRGWGRR0REREREZEZY5BEREREREZkRFnlERERERERmhEUeERERERGRGWGRR0REREREZEZY5BEREREREZkRFnlUYdy5cwcSiQRhYWHFLvfSSy8hODi4TGIqjhACb7/9NhwcHPSKu7To+7mVBYlEgq1btxr0nvLy+yQiKm3Mc8+HeY6oMBZ5ZFQjR46ERCKBRCKBpaUlvL298f777yMjI+OF112zZk3ExsbC398fAHD48GFIJBIkJydrLbd582Z88sknL7y9F7V7926sWbMGO3fu1IrbGNasWYOqVavqteyznxuVX0X9TRNR+cE89y/mOTIU81zZqWLqAMj89OzZE6tXr0Zubi6OHTuGsWPHIiMjAyEhIS+0XqlUCldX1xKXc3BweKHtGEtkZCTc3NwQGBio93tUKhUkEgksLIxz/iUnJwcymUyvz42IiPTDPJePeY6o/OKVPDI6uVwOV1dX1KxZE0OGDMFbb72luXUhOzsbkydPhrOzMxQKBdq1a4fQ0FDNex8/foy33noL1atXh5WVFerWrYvVq1cD0L4d486dO+jUqRMAoFq1apBIJBg5ciSAwrc9PH78GMOHD0e1atVgbW2NXr164ebNm5rXC84W7tmzBw0aNICtrS169uyJ2NjYYvfzyJEjaN26NeRyOdzc3DBjxgzk5eUByD/T++677yI6OhoSiQS1a9fWuY6Cbe/cuRN+fn6Qy+W4e/cucnJy8N///hc1atSAjY0N2rRpg8OHDwPIPws2atQopKSkaM4mz507FwBQu3ZtfPrppxg5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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 269 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/evaluation/scripts/evaluation.ipynb b/scripts/evaluation/scripts/evaluation.ipynb new file mode 100644 index 00000000..6656f96f --- /dev/null +++ b/scripts/evaluation/scripts/evaluation.ipynb @@ -0,0 +1,675 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2024-05-17T09:31:01.873881Z", + "start_time": "2024-05-17T09:31:01.869265Z" + } + }, + "source": [ + "import requests\n", + "\n", + "from typing import Any\n", + "from yaml import safe_load\n", + "from typing import Callable\n", + "from matplotlib import pyplot" + ], + "outputs": [], + "execution_count": 97 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Configuration", + "id": "81da2954bb585e01" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:01.953278Z", + "start_time": "2024-05-17T09:31:01.948494Z" + } + }, + "cell_type": "code", + "source": [ + "# Retrieve configuration file for elastic\n", + "with open(\"../../../config/debug.config.yml\") as config:\n", + " config_file = safe_load(config)[\"backend\"]" + ], + "id": "aad4278d1befeb33", + "outputs": [], + "execution_count": 98 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Evaluation", + "id": "279dad6910e08fca" + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Setup", + "id": "44bb28ffe4d41d7d" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:01.956677Z", + "start_time": "2024-05-17T09:31:01.954807Z" + } + }, + "cell_type": "code", + "source": "ORACLES = 1", + "id": "71bc2d4a3da214c8", + "outputs": [], + "execution_count": 99 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:01.965291Z", + "start_time": "2024-05-17T09:31:01.957389Z" + } + }, + "cell_type": "code", + "source": [ + "colors_map: dict[str, str] = {\n", + " \"precision\": \"orange\",\n", + " \"recall\": \"royalblue\",\n", + " \"f1\": \"forestgreen\",\n", + " \"ap\": \"firebrick\"\n", + "}\n", + "\n", + "ground_truth_needles: dict[str, list[str]] = {\n", + " \"weather forecast service\": [\n", + " \"663a01d3b43474523aefbef6\",\n", + " \"663a01d3b43474523aefbef5\",\n", + " \"663a0b2eb43474523aefbef8\",\n", + " \"663a0575b43474523aefbef7\"\n", + " ],\n", + " \"american sports news articles\": [\n", + " \"663caaaf76481aa0487cfe1c\",\n", + " \"663cac6776481aa0487cfe1d\",\n", + " \"663cb06576481aa0487cfe1e\",\n", + " \"663deba076481aa0487cfe1f\"\n", + " ],\n", + " \"digital book library\": [\n", + " \"663f822b76481aa0487cfe23\",\n", + " \"663f83df76481aa0487cfe24\",\n", + " \"663f86c776481aa0487cfe25\",\n", + " \"663f892876481aa0487cfe26\" \n", + " ],\n", + " \"restaurant booking platform\": [\n", + " \"66409b1376481aa0487cfe2a\",\n", + " \"66409e3876481aa0487cfe2c\",\n", + " \"6640a12176481aa0487cfe2d\",\n", + " \"6640a2ce76481aa0487cfe2e\"\n", + " ]\n", + "}\n", + "\n", + "ground_truth_total_ranks: dict[str, list[str]] = {\n", + " \"weather forecast service\": [\n", + " \"655a2b49863e4a08a6f2256f\",\n", + " \"655a168820959f0f93b42546\",\n", + " \"655a2b68863e4a08a6f22770\",\n", + " \"655aa97e863e4a08a6f3690f\",\n", + " \"655a6a0d863e4a08a6f2ee30\",\n", + " \"655a1fbc863e4a08a6eed4d2\",\n", + " \"655a284c863e4a08a6f1dad4\",\n", + " \"655a2bb9863e4a08a6f22ddd\",\n", + " \"655a2c70863e4a08a6f238c0\",\n", + " \"655a2be2863e4a08a6f230bc\" \n", + " ],\n", + " \"maps service\": [\n", + " \"655a2be2863e4a08a6f230bc\",\n", + " \"655a2be3863e4a08a6f230c8\",\n", + " \"655a2be3863e4a08a6f230d3\",\n", + " \"655a2be5863e4a08a6f230f2\",\n", + " \"655b9761863e4a08a6f516a8\",\n", + " \"655a184c20959f0f93b46c74\",\n", + " \"655b986b863e4a08a6f5206d\",\n", + " \"655a2b2d863e4a08a6f2238c\",\n", + " \"655a2b1f863e4a08a6f222a3\",\n", + " \"655a6a0d863e4a08a6f2ee30\"\n", + " ],\n", + " \"digital book library\": [\n", + " \"655aadfe863e4a08a6f395b6\",\n", + " \"655b0fa2863e4a08a6f40f81\",\n", + " \"655aa969863e4a08a6f36807\",\n", + " \"655a803d863e4a08a6f33a40\",\n", + " \"655ab14d863e4a08a6f3bc95\",\n", + " \"655aaa73863e4a08a6f37380\",\n", + " \"655aaa67863e4a08a6f372ea\",\n", + " \"655a107d20959f0f93b309c5\",\n", + " \"655a2ad4863e4a08a6f21ed7\",\n", + " \"655a2ad4863e4a08a6f21ee2\"\n", + " ],\n", + " \"tournaments and leaderboards\": [\n", + " \"655aab32863e4a08a6f37cac\",\n", + " \"655a171f20959f0f93b43d9a\",\n", + " \"655a2b67863e4a08a6f2275b\",\n", + " \"655a6d04863e4a08a6f30ba8\",\n", + " \"655a2bd3863e4a08a6f22f67\",\n", + " \"655a6f7b863e4a08a6f31941\",\n", + " \"655a2b35863e4a08a6f22411\",\n", + " \"655a184c20959f0f93b46c74\",\n", + " \"655a164e20959f0f93b41b17\",\n", + " \"655a2ea5863e4a08a6f24d54\" \n", + " ]\n", + "}\n", + "\n", + "ground_truth_total_rel: dict[str, dict[str, int]] = {\n", + " \"weather forecast service\": {\n", + " \"655a2b49863e4a08a6f2256f\": 1,\n", + " \"655a168820959f0f93b42546\": 0,\n", + " \"655a2b68863e4a08a6f22770\": 1,\n", + " \"655aa97e863e4a08a6f3690f\": 1,\n", + " \"655a6a0d863e4a08a6f2ee30\": 0,\n", + " \"655a1fbc863e4a08a6eed4d2\": 0,\n", + " \"655a284c863e4a08a6f1dad4\": 0,\n", + " \"655a2bb9863e4a08a6f22ddd\": 0,\n", + " \"655a2c70863e4a08a6f238c0\": 0,\n", + " \"655a2be2863e4a08a6f230bc\": 0\n", + " },\n", + " \"maps service\": {\n", + " \"655a2be2863e4a08a6f230bc\": 1,\n", + " \"655a2be3863e4a08a6f230c8\": 1,\n", + " \"655a2be3863e4a08a6f230d3\": 1,\n", + " \"655a2be5863e4a08a6f230f2\": 1,\n", + " \"655b9761863e4a08a6f516a8\": 0,\n", + " \"655a184c20959f0f93b46c74\": 1,\n", + " \"655b986b863e4a08a6f5206d\": 1,\n", + " \"655a2b2d863e4a08a6f2238c\": 0,\n", + " \"655a2b1f863e4a08a6f222a3\": 0,\n", + " \"655a6a0d863e4a08a6f2ee30\": 0\n", + " },\n", + " \"digital book library\": {\n", + " \"655aadfe863e4a08a6f395b6\": 1,\n", + " \"655b0fa2863e4a08a6f40f81\": 1,\n", + " \"655aa969863e4a08a6f36807\": 1,\n", + " \"655a803d863e4a08a6f33a40\": 1,\n", + " \"655ab14d863e4a08a6f3bc95\": 1,\n", + " \"655aaa73863e4a08a6f37380\": 0,\n", + " \"655aaa67863e4a08a6f372ea\": 1,\n", + " \"655a107d20959f0f93b309c5\": 1,\n", + " \"655a2ad4863e4a08a6f21ed7\": 0,\n", + " \"655a2ad4863e4a08a6f21ee2\": 0\n", + " },\n", + " \"tournaments and leaderboards\": {\n", + " \"655aab32863e4a08a6f37cac\": 1,\n", + " \"655a171f20959f0f93b43d9a\": 1,\n", + " \"655a2b67863e4a08a6f2275b\": 1,\n", + " \"655a6d04863e4a08a6f30ba8\": 0,\n", + " \"655a2bd3863e4a08a6f22f67\": 0,\n", + " \"655a6f7b863e4a08a6f31941\": 0,\n", + " \"655a2b35863e4a08a6f22411\": 0,\n", + " \"655a184c20959f0f93b46c74\": 0,\n", + " \"655a164e20959f0f93b41b17\": 0,\n", + " \"655a2ea5863e4a08a6f24d54\": 1\n", + " }\n", + "}\n", + "\n", + "evaluations: dict[str, dict[str, float]] = {}" + ], + "id": "9e41235f1da01340", + "outputs": [], + "execution_count": 100 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:01.978165Z", + "start_time": "2024-05-17T09:31:01.969496Z" + } + }, + "cell_type": "code", + "source": [ + "ground_truth_words_weather: dict[str, list[str]] = {\n", + " \"weather\": [\n", + " \"663a01d3b43474523aefbef6\",\n", + " \"663a01d3b43474523aefbef5\",\n", + " \"663a0b2eb43474523aefbef8\",\n", + " \"663a0575b43474523aefbef7\"\n", + " ],\n", + " \"weather forecast\": [\n", + " \"663a01d3b43474523aefbef6\",\n", + " \"663a01d3b43474523aefbef5\",\n", + " \"663a0b2eb43474523aefbef8\",\n", + " \"663a0575b43474523aefbef7\"\n", + " ],\n", + " \"weather forecast service\": [\n", + " \"663a01d3b43474523aefbef6\",\n", + " \"663a01d3b43474523aefbef5\",\n", + " \"663a0b2eb43474523aefbef8\",\n", + " \"663a0575b43474523aefbef7\"\n", + " ],\n", + " \"the api returns the next 4 days\\nof weather forecasts\": [\n", + " \"663a01d3b43474523aefbef6\",\n", + " \"663a01d3b43474523aefbef5\",\n", + " \"663a0b2eb43474523aefbef8\",\n", + " \"663a0575b43474523aefbef7\"\n", + " ],\n", + "}\n", + "\n", + "ground_truth_words_library: dict[str, list[str]] = {\n", + " \"digital\": [\n", + " \"663f822b76481aa0487cfe23\",\n", + " \"663f83df76481aa0487cfe24\",\n", + " \"663f86c776481aa0487cfe25\",\n", + " \"663f892876481aa0487cfe26\" \n", + " ],\n", + " \"digital book\": [\n", + " \"663f822b76481aa0487cfe23\",\n", + " \"663f83df76481aa0487cfe24\",\n", + " \"663f86c776481aa0487cfe25\",\n", + " \"663f892876481aa0487cfe26\" \n", + " ],\n", + " \"digital book library\": [\n", + " \"663f822b76481aa0487cfe23\",\n", + " \"663f83df76481aa0487cfe24\",\n", + " \"663f86c776481aa0487cfe25\",\n", + " \"663f892876481aa0487cfe26\" \n", + " ],\n", + " \"API for managing digital library resources\\nretrieving books authors and publishers\": [\n", + " \"663f822b76481aa0487cfe23\",\n", + " \"663f83df76481aa0487cfe24\",\n", + " \"663f86c776481aa0487cfe25\",\n", + " \"663f892876481aa0487cfe26\" \n", + " ],\n", + "}\n", + "\n", + "ground_truth_words_restaurant: dict[str, list[str]] = {\n", + " \"restaurant\": [\n", + " \"66409b1376481aa0487cfe2a\",\n", + " \"66409e3876481aa0487cfe2c\",\n", + " \"6640a12176481aa0487cfe2d\",\n", + " \"6640a2ce76481aa0487cfe2e\"\n", + " ],\n", + " \"restaurant booking\": [\n", + " \"66409b1376481aa0487cfe2a\",\n", + " \"66409e3876481aa0487cfe2c\",\n", + " \"6640a12176481aa0487cfe2d\",\n", + " \"6640a2ce76481aa0487cfe2e\"\n", + " ],\n", + " \"restaurant booking platform\": [\n", + " \"66409b1376481aa0487cfe2a\",\n", + " \"66409e3876481aa0487cfe2c\",\n", + " \"6640a12176481aa0487cfe2d\",\n", + " \"6640a2ce76481aa0487cfe2e\"\n", + " ],\n", + " \"Allows users to create a booking for a\\nspecific restaurant ID of the restaurant\": [\n", + " \"66409b1376481aa0487cfe2a\",\n", + " \"66409e3876481aa0487cfe2c\",\n", + " \"6640a12176481aa0487cfe2d\",\n", + " \"6640a2ce76481aa0487cfe2e\"\n", + " ]\n", + "}\n", + "\n", + "ground_truth_words_sports: dict[str, list[str]] = {\n", + " \"american sports\": [\n", + " \"663caaaf76481aa0487cfe1c\",\n", + " \"663cac6776481aa0487cfe1d\",\n", + " \"663cb06576481aa0487cfe1e\",\n", + " \"663deba076481aa0487cfe1f\"\n", + " ],\n", + " \"american sports news\": [\n", + " \"663caaaf76481aa0487cfe1c\",\n", + " \"663cac6776481aa0487cfe1d\",\n", + " \"663cb06576481aa0487cfe1e\",\n", + " \"663deba076481aa0487cfe1f\"\n", + " ],\n", + " \"american sports news articles\": [\n", + " \"663caaaf76481aa0487cfe1c\",\n", + " \"663cac6776481aa0487cfe1d\",\n", + " \"663cb06576481aa0487cfe1e\",\n", + " \"663deba076481aa0487cfe1f\"\n", + " ],\n", + " \"An API for accessing news articles across\\nvarious sports leagues including NFL\": [\n", + " \"663caaaf76481aa0487cfe1c\",\n", + " \"663cac6776481aa0487cfe1d\",\n", + " \"663cb06576481aa0487cfe1e\",\n", + " \"663deba076481aa0487cfe1f\"\n", + " ],\n", + "}" + ], + "id": "95b2972e498492cd", + "outputs": [], + "execution_count": 101 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:01.982237Z", + "start_time": "2024-05-17T09:31:01.979660Z" + } + }, + "cell_type": "code", + "source": [ + "def perform_search_query(query: str, k: int=10) -> list[str]:\n", + " url: str = f\"http://localhost:{config_file['port']}/api/v1/search?size={k}\"\n", + " body: dict[str, str | list[str]] = {\n", + " \"fragment\": query.replace(\"\\n\", \" \"),\n", + " \"fields\": [\"metadata\"]\n", + " }\n", + " \n", + " res: list[dict[str, Any]] = requests.post(url, json=body).json()\n", + " \n", + " return [doc[\"metadata\"][\"mongo-id\"] for doc in res]" + ], + "id": "338d80ac5bc1889d", + "outputs": [], + "execution_count": 102 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:01.999990Z", + "start_time": "2024-05-17T09:31:01.997934Z" + } + }, + "cell_type": "code", + "source": [ + "def is_relevant(query:str, document: str):\n", + " return ground_truth_total_rel[query][document] > (ORACLES / 2)" + ], + "id": "b327ba69256d130d", + "outputs": [], + "execution_count": 103 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Metrics", + "id": "20a6f552f33337b0" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.011412Z", + "start_time": "2024-05-17T09:31:02.009044Z" + } + }, + "cell_type": "code", + "source": [ + "def precision(found_docs: list[str], docs: list[str]=None, k: int=10) -> float:\n", + " return len(found_docs) / k" + ], + "id": "aa1877e33ae7956", + "outputs": [], + "execution_count": 104 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.015255Z", + "start_time": "2024-05-17T09:31:02.013108Z" + } + }, + "cell_type": "code", + "source": [ + "def recall(found_docs: list[str], docs: list[str], k: int=10) -> float:\n", + " return len(found_docs) / len(docs)" + ], + "id": "31916f70a911358f", + "outputs": [], + "execution_count": 105 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.019095Z", + "start_time": "2024-05-17T09:31:02.016559Z" + } + }, + "cell_type": "code", + "source": [ + "def f1(found_docs: list[str], docs: list[str], k: int=10) -> float:\n", + " precision_score = len(found_docs) / k\n", + " recall_score = len(found_docs) / len(docs)\n", + " \n", + " if precision_score + recall_score == 0:\n", + " return 0\n", + " \n", + " return 2 * ((precision_score * recall_score) / (precision_score + recall_score))" + ], + "id": "8305ef14b7b7ff", + "outputs": [], + "execution_count": 106 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.023027Z", + "start_time": "2024-05-17T09:31:02.020190Z" + } + }, + "cell_type": "code", + "source": [ + "def ap(query: str, k: int=10) -> float:\n", + " global ground_truth_total_ranks, ground_truth_total_rel\n", + " \n", + " num = 0\n", + " docs = ground_truth_total_ranks[query]\n", + " \n", + " for i in range(k):\n", + " relevant_docs = list(filter(lambda x: is_relevant(query, x), docs[:k]))\n", + " \n", + " precision_score = precision(relevant_docs, k=k)\n", + " rel_score = 1 if is_relevant(query, docs[i]) else 0\n", + " \n", + " num += precision_score * rel_score\n", + " \n", + " return num / k" + ], + "id": "4032123a87f5a2eb", + "outputs": [], + "execution_count": 107 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.051329Z", + "start_time": "2024-05-17T09:31:02.047491Z" + } + }, + "cell_type": "code", + "source": [ + "def evaluate_metric(metric: Callable[[list[str], list[str], int], float], to_evaluate: dict[str, list[str]], k: int=10, total: bool=False):\n", + " global evaluations\n", + " \n", + " for query, docs in to_evaluate.items():\n", + " if not total:\n", + " found_docs = perform_search_query(query, k)[:k]\n", + " found_docs = list(filter(lambda x: x in docs, found_docs))\n", + " \n", + " metric_value = metric(found_docs, docs, k)\n", + " else:\n", + " if metric.__name__ != \"ap\":\n", + " relevant_docs = list(filter(lambda x: is_relevant(query, x), docs[:k]))\n", + " metric_value = metric(relevant_docs, docs, k)\n", + " else:\n", + " metric_value = ap(query, k)\n", + " \n", + " if query not in evaluations:\n", + " evaluations[query] = {}\n", + " \n", + " evaluations[query][f\"{metric.__name__}@{k}\"] = metric_value" + ], + "id": "c4680c0ff0324be9", + "outputs": [], + "execution_count": 108 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Results", + "id": "d01d0aa827364f36" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.056480Z", + "start_time": "2024-05-17T09:31:02.052652Z" + } + }, + "cell_type": "code", + "source": [ + "def display_graph(query: str, metrics: list[str], axes: Any, position: list[int]):\n", + " global evaluations\n", + " \n", + " for metric in metrics:\n", + " y = [val for key, val in evaluations[query].items() if metric in key]\n", + " x = [k+1 for k in range(len(y))]\n", + " \n", + " axes[position[0], position[1]].scatter([str(x_i) for x_i in x], y, label=metric, color=colors_map[metric])\n", + " axes[position[0], position[1]].plot([str(x_i) for x_i in x], y, color=colors_map[metric])\n", + " \n", + " axes[position[0], position[1]].set_title(query, wrap=True)\n", + " axes[position[0], position[1]].set_ylim(-0.05, 1.05)\n", + " axes[position[0], position[1]].set_xlabel(\"Number of documents (K)\")\n", + " axes[position[0], position[1]].set_ylabel(\"Metric score\")\n", + " axes[position[0], position[1]].legend()" + ], + "id": "667270dca3fd2f60", + "outputs": [], + "execution_count": 109 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.061100Z", + "start_time": "2024-05-17T09:31:02.057877Z" + } + }, + "cell_type": "code", + "source": [ + "def perform_evaluation(metrics: list[str], ground_truth: dict, k: int=10, rows: int=2, cols: int=2, total: bool=False):\n", + " global evaluations\n", + " \n", + " for metric in metrics:\n", + " for i in range(1, k+1):\n", + " evaluate_metric(globals()[metric], ground_truth, i, total)\n", + " \n", + " fig, axs = pyplot.subplots(rows, cols, figsize=(8, 6))\n", + " fig.suptitle(\"Metrics @K for some test queries\\n\")\n", + " fig.tight_layout(h_pad=3)\n", + " \n", + " for ind, query in enumerate(evaluations.keys()):\n", + " position = bin(ind).replace(\"0b\", \"\")\n", + " position = f\"0{position}\" if len(position) == 1 else position\n", + " \n", + " display_graph(query, metrics, axs, [int(position[0]), int(position[1])])\n", + "\n", + " fig.tight_layout(h_pad=1)\n", + " pyplot.savefig(\"out.pdf\")" + ], + "id": "3bb2170c5b14b45f", + "outputs": [], + "execution_count": 110 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.064314Z", + "start_time": "2024-05-17T09:31:02.061943Z" + } + }, + "cell_type": "code", + "source": [ + "def get_averages(metrics: list[str], k:int = 10):\n", + " for metric in metrics:\n", + " for i in range(1, k+1):\n", + " all_values_k = []\n", + " \n", + " for values in evaluations.values():\n", + " all_values_k.append(values[f\"{metric}@{i}\"])\n", + " \n", + " print(f\"{metric}@{i}: \", sum(all_values_k) / len(all_values_k))" + ], + "id": "85250a9940376766", + "outputs": [], + "execution_count": 111 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T09:31:02.485402Z", + "start_time": "2024-05-17T09:31:02.083265Z" + } + }, + "cell_type": "code", + "source": [ + "# perform_evaluation([\"precision\", \"recall\", \"f1\"], ground_truth_needles)\n", + "# perform_evaluation([\"precision\", \"recall\", \"f1\"], ground_truth_words_sports)\n", + "perform_evaluation([\"precision\", \"ap\"], ground_truth_total_ranks, 10, total=True)\n", + "get_averages([\"precision\", \"ap\"])" + ], + "id": "5e4ba8367ddb69dd", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "precision@1: 1.0\n", + "precision@2: 0.875\n", + "precision@3: 0.9166666666666666\n", + "precision@4: 0.875\n", + "precision@5: 0.75\n", + "precision@6: 0.6666666666666667\n", + "precision@7: 0.6428571428571428\n", + "precision@8: 0.59375\n", + "precision@9: 0.5277777777777778\n", + "precision@10: 0.5\n", + "ap@1: 1.0\n", + "ap@2: 0.8125\n", + "ap@3: 0.8611111111111112\n", + "ap@4: 0.78125\n", + "ap@5: 0.59\n", + "ap@6: 0.47222222222222227\n", + "ap@7: 0.4591836734693877\n", + "ap@8: 0.40234375\n", + "ap@9: 0.31790123456790126\n", + "ap@10: 0.27499999999999997\n" + ] + }, + { + "data": { + "text/plain": [ + "
    " + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxYAAAJQCAYAAAATyPJiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3hURRfA4d+mN9I7hCQECL13BKR3xAaKUm1Ik6IiKiDgByooRWkqTUEBAREUgdBBAoTee+ghkEAoIT3z/XHJwpJCQsqmnPd59oG9O3vv2U2yc8/emTM6pZRCCCGEEEIIIbLBxNgBCCGEEEIIIQo+SSyEEEIIIYQQ2SaJhRBCCCGEECLbJLEQQgghhBBCZJskFkIIIYQQQohsk8RCCCGEEEIIkW2SWAghhBBCCCGyTRILIYQQQgghRLZJYiGEEEIIIYTINkkshBDMnz8fnU6HTqdjy5YtqR5XSlG6dGl0Oh3PP//8Mx1jxowZzJ8/P0vP2bJlS7ox5VfPP/88lSpVSrV9zZo12NjYUL9+fW7fvp3hPj7//HNKliyJmZkZjo6OuRRp4TN+/HhWrlyZq8c4fvw4X3zxBRcuXMjV4+S2gvi3JYTI/ySxEELoFStWjDlz5qTavnXrVs6dO0exYsWeed/PkljUqFGD4OBgatSo8czHfVaJiYn88ssvtG/fHnd3d8zMzPDw8KBVq1b88ssvJCUlZXpfv//+O507d6Zhw4Zs2LABJyendNv+9ddf/O9//6NHjx5s3bqVDRs25MTLKRLyKrEYM2ZMgU8sjPm3JYQovCSxEELode3aleXLl3P37l2D7XPmzKF+/fqULFkyT+JISEggMTERe3t76tWrh729fZ4cN8WRI0eoXLkygwcPply5ckydOpUtW7Ywd+5c6taty8iRI6lbty7nzp176r5mzpzJm2++SceOHfnnn3+wtbXNsP3Ro0cBGDRoEA0bNqRWrVrZfj0PHjzI9j5E4WDsvy0hRCGnhBBF3rx58xSgNm7cqKytrdWsWbP0j0VFRSlra2v1008/qYoVK6omTZoYPDcuLk6NGzdOBQYGKgsLC+Xq6qp69eqlbty4oW/j6+urAIObr6+vUkqpzZs3K0D98ssvaujQocrb21vpdDp14sQJ/WObN282OOauXbtUhw4dlLOzs7K0tFSlSpVSH3zwgf7xGzduqHfeeUeVKFFCH1ODBg1UUFDQU9+Lo0ePKnt7e9W3b191//79NNs8ePBAvffee6pkyZLqypUrBo81adJEVaxYUSml1P/+9z8FqD59+qjExMSnHjut92n06NFKKaWSkpLU119/rX+f3dzcVPfu3dXly5fTPP7WrVtV/fr1lbW1teratWu6xzx37pzq2rWr8vLyUhYWFsrd3V01a9ZMHThwQN8mq8feuXOnql+/vrKyslK+vr5q7ty5Siml/v77b1W9enVlbW2tKlWqpP79999U8Zw+fVq9/vrrys3NTVlYWKhy5cqpH3744anv3ZPvG2DwuxoWFqbeffddVbx4cWVubq78/PzUF198oRISEgz2M2PGDFWlShVla2ur7OzsVGBgoBoxYoRS6tHfyZO3efPmZRjb33//rapWraosLCyUn5+fmjhxoho9erR6vAsODQ1Nd1+P/x5k5X16lr+tkJAQ1bFjR+Xk5KQsLS1VtWrV1JIlSwzaREdHq2HDhik/Pz9laWmpnJycVM2aNdVvv/2W4fsghCj8zPIofxFCFAD29va88sorzJ07l/feew/QhvGYmJjQtWtXpkyZYtA+OTmZF154ge3bt/Pxxx/ToEEDLl68yOjRo3n++efZu3cv1tbW/Pnnn7zyyis4ODgwY8YMACwtLQ32NWLECOrXr8+sWbMwMTHB3d2d69evp4px3bp1dOzYkfLly/Pdd99RsmRJLly4wPr16/Vtunfvzv79+/nf//5H2bJliYqKYv/+/URGRmb4+pOSkujSpQtvvfUW3333XZptlFJYWFgwa9YsevToQd++fVm9enWqdh999BGTJk1i2LBhTJo0KcPjpvjzzz+ZPn06c+bMYe3atTg4OFCiRAkA3n//fX788UcGDBhAhw4duHDhAiNHjmTLli3s378fV1dX/X7CwsJ48803+fjjjxk/fjwmJulfnG7Xrh1JSUl88803lCxZkoiICHbu3ElUVJS+TVaOff36dXr37s3HH39MiRIl+P777+nTpw+XL19m2bJlfPrppzg4ODB27Fg6d+7M+fPn8fb2BrRhRg0aNKBkyZJ8++23eHp6sm7dOgYNGkRERASjR49O93UEBwfTrFkzmjZtysiRIwH038Zfv36dOnXqYGJiwqhRowgICCA4OJgvv/ySCxcuMG/ePAAWL15Mv379GDhwIJMmTcLExISzZ89y/Phx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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 112 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/evaluation/scripts/performance.ipynb b/scripts/evaluation/scripts/performance.ipynb new file mode 100644 index 00000000..e07ed53f --- /dev/null +++ b/scripts/evaluation/scripts/performance.ipynb @@ -0,0 +1,373 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.673826Z", + "start_time": "2024-05-17T13:42:12.668144Z" + } + }, + "source": [ + "import math\n", + "import requests\n", + "\n", + "from yaml import safe_load\n", + "from typing import Callable\n", + "from scipy.stats import sem\n", + "from matplotlib import pyplot" + ], + "outputs": [], + "execution_count": 22 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Configuration", + "id": "d53ef80c22544fcf" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.678984Z", + "start_time": "2024-05-17T13:42:12.675200Z" + } + }, + "cell_type": "code", + "source": [ + "# Retrieve configuration file for elastic\n", + "with open(\"../../../config/debug.config.yml\") as config:\n", + " config_file = safe_load(config)[\"backend\"]" + ], + "id": "e56f00cf542c077a", + "outputs": [], + "execution_count": 23 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Evaluation", + "id": "e04cf92c45f43baa" + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Setup", + "id": "dc1d5b55c27be592" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.688846Z", + "start_time": "2024-05-17T13:42:12.685908Z" + } + }, + "cell_type": "code", + "source": [ + "class Options:\n", + " def __init__(self, r_min: int=10, r_max: int=100, step: int=10, reps: int=5):\n", + " self.r_min = r_min\n", + " self.r_max = r_max\n", + " self.step = step\n", + " self.reps = reps\n", + " \n", + "evaluations: dict[str, dict[str, dict[str, list[int] | list[float]]]] = {}\n", + "\n", + "colors_map: dict[str, str] = {\n", + " \"metadata\": \"orange\",\n", + " \"specification\": \"royalblue\",\n", + " \"both\": \"forestgreen\"\n", + "}" + ], + "id": "b91c213bcdf7ad11", + "outputs": [], + "execution_count": 24 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.699729Z", + "start_time": "2024-05-17T13:42:12.696760Z" + } + }, + "cell_type": "code", + "source": [ + "def perform_search_query(query: str|None, fields: list[str], size:int = 10, k: int|None=100) -> int:\n", + " k_param: str = f\"&k={k}\" if k else \"\"\n", + " url: str = f\"http://localhost:{config_file['port']}/api/v1/search?size={size}{k_param}\"\n", + " body: dict[str, str|list[str]] = {\n", + " \"fields\": fields\n", + " }\n", + " \n", + " if query: body[\"fragment\"] = query\n", + " response = requests.post(url, json=body)\n", + " \n", + " return round(response.elapsed.microseconds / 1000)" + ], + "id": "83e13c88cde5b1dc", + "outputs": [], + "execution_count": 25 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.702782Z", + "start_time": "2024-05-17T13:42:12.700928Z" + } + }, + "cell_type": "code", + "source": [ + "def get_random_query() -> str:\n", + " return \" \".join(requests.get(\"https://random-word-api.herokuapp.com/word?lang=en&number=3\").json())" + ], + "id": "c89f3937fbdaf88d", + "outputs": [], + "execution_count": 26 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Tests", + "id": "d5238adc13743581" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.705445Z", + "start_time": "2024-05-17T13:42:12.703560Z" + } + }, + "cell_type": "code", + "source": [ + "def no_queries_test(fields: list[str]=None, size: int=None, k: int=None) -> int:\n", + " return perform_search_query(None, fields, size, None)" + ], + "id": "31c22cef9767eeff", + "outputs": [], + "execution_count": 27 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.709066Z", + "start_time": "2024-05-17T13:42:12.706760Z" + } + }, + "cell_type": "code", + "source": [ + "def random_queries_test(fields: list[str]=None, size: int=None, k: int=None) -> int:\n", + " return perform_search_query(get_random_query(), fields, size, k)" + ], + "id": "d46b4b83c59c6cfb", + "outputs": [], + "execution_count": 28 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:42:12.714069Z", + "start_time": "2024-05-17T13:42:12.709809Z" + } + }, + "cell_type": "code", + "source": [ + "def run_test(test: Callable[[list[str], int, int], int], options: Options, debug=False):\n", + " global evaluations\n", + " \n", + " cases: list[list[str]] = [[\"metadata\"], [\"specification\"], [\"metadata\", \"specification\"]]\n", + " \n", + " if debug: print(f\"{test.__name__}:\")\n", + " \n", + " for idx, case in enumerate(cases):\n", + " times: list[int] = []\n", + " errs: list[float] = []\n", + " \n", + " if debug: print(f\"\\t{case}\", end=\": [\")\n", + " \n", + " for i in range(options.r_min, options.r_max+1, options.step):\n", + " tmp_times: list[int] = []\n", + " \n", + " for _ in range(options.reps):\n", + " tmp_times.append(test(case, i, i))\n", + " \n", + " times.append(round(sum(tmp_times)/options.reps))\n", + " errs.append(sem(tmp_times))\n", + " \n", + " if debug: print(f\"{round(sum(tmp_times)/options.reps)}\", end=\", \" if i != options.r_max else \"\")\n", + " \n", + " if debug: print(\"]\")\n", + " \n", + " if test.__name__ not in evaluations:\n", + " evaluations[test.__name__] = {}\n", + " \n", + " case_name = case[0] if idx != 2 else \"both\"\n", + " \n", + " if case_name not in evaluations[test.__name__]:\n", + " evaluations[test.__name__][case_name] = {}\n", + " \n", + " evaluations[test.__name__][case_name][\"ys\"] = times\n", + " evaluations[test.__name__][case_name][\"errs\"] = errs\n", + " \n", + " if debug: print()" + ], + "id": "91e21ac28668671", + "outputs": [], + "execution_count": 29 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Results\n", + "id": "8381d2f4b8205f0c" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:49:26.803364Z", + "start_time": "2024-05-17T13:49:26.798217Z" + } + }, + "cell_type": "code", + "source": [ + "def display_graph(options: Options):\n", + " global evaluations\n", + " \n", + " x = [i for i in range(options.r_min, options.r_max+1, options.step)]\n", + " \n", + " fig, axes = pyplot.subplots(math.ceil(len(evaluations.keys())/2), 2, figsize=(10, 4))\n", + " fig.suptitle(\"Performance test @K\")\n", + " \n", + " for idx, (test, results) in enumerate(evaluations.items()):\n", + " position_bin: str = bin(idx).replace(\"0b\", \"\")\n", + " position: list[int] = [0, int(position_bin)] if len(position_bin) == 1 else [int(position_bin[0]), int(position_bin[1])]\n", + " \n", + " for case, item in results.items():\n", + " axes[position[1]].errorbar([str(x_i) for x_i in x], item[\"ys\"], item[\"errs\"], fmt='o', linewidth=2, capsize=6, label=case, color=colors_map[case])\n", + " # axes[position[1]].plot(x, item[\"ys\"])\n", + " \n", + " all_ys = [y for case in evaluations.values() for el in case.values() for y in el[\"ys\"]]\n", + " max_val = max(all_ys)\n", + " \n", + " axes[position[1]].set_title(test.replace(\"_\", \" \").replace(\"test\", \"\"))\n", + " axes[position[1]].legend(loc=\"upper left\")\n", + " axes[position[1]].set_xlabel(\"Number of documents (K)\")\n", + " axes[position[1]].set_ylabel(\"Retrieval time (in ms)\")\n", + " axes[position[1]].set_ylim(0, max_val+50)\n", + " \n", + " fig.tight_layout(h_pad=1)\n", + " pyplot.savefig(\"out.pdf\")" + ], + "id": "dbe89f17c3344b82", + "outputs": [], + "execution_count": 33 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:47:46.724179Z", + "start_time": "2024-05-17T13:42:12.725360Z" + } + }, + "cell_type": "code", + "source": [ + "opts: Options = Options(reps=10, r_max=120)\n", + "\n", + "run_test(no_queries_test, opts, debug=True)\n", + "run_test(random_queries_test, opts, debug=True)" + ], + "id": "a29ee6ddeb151d91", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "no_queries_test:\n", + "\t['metadata']: [56, 80, 140, 163, 199, 276, 245, 318, 356, 356, 403, 390]\n", + "\t['specification']: [62, 79, 137, 212, 242, 296, 325, 354, 407, 382, 432, 419]\n", + "\t['metadata', 'specification']: [60, 85, 134, 235, 255, 260, 314, 358, 381, 410, 400, 441]\n", + "\n", + "random_queries_test:\n", + "\t['metadata']: [49, 70, 99, 127, 146, 177, 212, 254, 224, 289, 289, 343]\n", + "\t['specification']: [54, 89, 124, 135, 162, 192, 245, 255, 290, 283, 341, 394]\n", + "\t['metadata', 'specification']: [51, 87, 111, 151, 158, 220, 228, 228, 283, 326, 356, 346]\n", + "\n" + ] + } + ], + "execution_count": 31 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-17T13:49:29.441637Z", + "start_time": "2024-05-17T13:49:29.132679Z" + } + }, + "cell_type": "code", + "source": "display_graph(opts)", + "id": "f5d5756aa5c425df", + "outputs": [ + { + "data": { + "text/plain": [ + "
    " + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAGMCAYAAAAstHr+AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC1rElEQVR4nOzdZ3hU1fr38e/MpJMQ0gOEKkWQKqIChyYoihRF9IgFEA+KBgEVRT0CATv+VUSxoEcQFCyIPuhRUAEBBTV0seFRkJaQQgqQPrOfF0OGTDIJSZhJ/X2uK4bZs/bslWLuufda614mwzAMRERERERERMQjzNXdAREREREREZG6TIm3iIiIiIiIiAcp8RYRERERERHxICXeIiIiIiIiIh6kxFtERERERETEg5R4i4iIiIiIiHiQEm8RERERERERD1LiLSIiIiIiIuJBSrxFREREREREPEiJt4iIeMSSJUswmUyODy8vL2JiYrjttts4cuSIW6+Vl5fHpEmTaNy4MRaLhW7durn19eubzz//nLi4OI9f58knn+STTz7x+HUKFf5Obtu2zel4SkoKF110EYGBgXz11VdV1h8REak/lHiLiIhHLV68mK1bt/LVV18xceJEVqxYQd++fTl16pTbrvHqq6/y+uuv8+9//5tvv/2WZcuWue2166PPP/+cOXPmePw655J479ixgzvvvJM2bdrg5+dHYGAgnTt3ZtasWRw9erTcr3P48GH69u3LX3/9xddff83ll19eqf6IiIiUxau6OyAiInVbp06duOiiiwAYOHAgVquVxx57jE8++YSbb775nF47KyuLgIAA9u7di7+/P5MnT3ZHlwHIzs7G39/fba8n7pGTk8PkyZNZvHgxV111FTNmzKBVq1aYzWb++OMPPvzwQ1588UVeeuklxo4dW+Zr/fHHHwwePJj8/Hw2btxI586dq+irEBGR+kYj3iIiUqUuvfRSAP7++28ADMPglVdeoVu3bvj7+xMSEsLo0aP566+/nM4bMGAAnTp1YtOmTfTu3ZuAgAAmTJiAyWTizTffJDs72zGtfcmSJYA9SXv44Ydp1aoVPj4+NG3alNjYWNLT051eu2XLlgwbNoxVq1bRvXt3/Pz8mDNnDt988w0mk4nly5czY8YMGjduTGBgIMOHD+fYsWOcOHGCO+64g/DwcMLDw7nttts4efKk02svXLiQfv36ERkZSYMGDejcuTPz5s0jPz/f5dcXHx9P3759CQgIoHXr1jz99NPYbDantunp6dx///20bt0aX19fIiMjGTp0KL/99pujTV5eHo8//jjnn38+vr6+REREcNttt5GcnFzmz2f8+PEsXLgQwGmpwIEDByr089q5cyfDhg0jMjISX19fmjRpwtVXX83hw4cdr33q1CnefvttxzUGDBhQZt8KCgoYOXIkW7ZsYc+ePXz22WdMnDiRwYMHc9lll3HnnXfy9ddfs2zZMiZPnszSpUtLfa1du3bxj3/8Ay8vL7799lsl3SIi4lEa8RYRkSr1v//9D4CIiAgA7rzzTpYsWcKUKVN45plnOH78OHPnzqV3797s3r2bqKgox7kJCQnccsstPPjggzz55JOYzWamTZvGY489xoYNG1i/fj0A5513HoZhcM0117Bu3Toefvhh+vbty549e5g9ezZbt25l69at+Pr6Ol57x44d/Prrrzz66KO0atWKBg0aOKbDP/LIIwwcOJAlS5Zw4MABpk+fzpgxY/Dy8qJr166sWLGCnTt38sgjjxAUFMSCBQscr/vnn39y0003OZL/3bt388QTT/Dbb7/x1ltvOX1vEhMTufnmm7n//vuZPXs2H3/8MQ8//DBNmjRxjN6eOHGCf/zjHxw4cIAZM2ZwySWXcPLkSTZt2kRCQgLnn38+NpuNkSNHsnnzZh588EF69+7N33//zezZsxkwYADbtm0rdTR/5syZnDp1ipUrV7J161bH8caNG5f753Xq1Ckuv/xyWrVqxcKFC4mKiiIxMZENGzZw4sQJALZu3cpll13GwIEDmTlzJgANGzYs83dn3rx5/PHHH+zYsYNGjRq5bFNQUMCIESNYtWoV1157LQMHDqRZs2ZObb799lvi4uJo1qwZX375peNrExER8RhDRETEAxYvXmwAxvfff2/k5+cbJ06cMD777DMjIiLCCAoKMhITE42tW7cagPHcc885nXvo0CHD39/fePDBBx3H+vfvbwDGunXrSlxr3LhxRoMGDZyOrVmzxgCMefPmOR1///33DcBYtGiR41iLFi0Mi8Vi/P77705tN2zYYADG8OHDnY5PmzbNAIwpU6Y4Hb/mmmuM0NDQUr8nVqvVyM/PN5YuXWpYLBbj+PHjJb6+H374wemcjh07GkOGDHE8njt3rgEYX331VanX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adWqFQEBAdXdXREREQet8RYREZFaISIignXr1rF37146duzIggULOHDgAC1atMBms/Hjjz+ybNkyLrjgArdsYSYiIuIuGvEWERGRWiU3N5fXXnuNN954g59//tlx3M/Pj8GDB/PAAw/Qr1+/auyhiIiIMyXeIiIiUmulpqaSkJCAt7c3LVu2dLnNmYiISHVT4i0iIiIiIiLiQVrjLSIiIiIiIuJBSrxFREREREREPEiJt4iIiIiIiIgHKfEWERERERER8SAl3iIiIiIiIiIepMRbRERERERExIOUeIuIiIiIiIh4kBJvEREREREREQ9S4i0iIiIiIiLiQUq8RURERERERDxIibeIiIiIiIiIBynxFhEREREREfEgJd4iIiIiIiIiHqTEW0RERERERMSDlHiLiIiIiIiIeJASbxEREREREREPUuItIiIiIiIi4kFKvEVEREREREQ8SIm3iIiIiIiIiAcp8RYRERERERHxICXeIlIjLVmyBJPJxIEDB6q7KyIiIm7VsmVLxo8fX93dqPHi4uIwmUzV3Q0Rt1DiLSI10tVXX83WrVtp3LhxdXdFREREqsG//vUvtm7dWt3dEHELr+rugIhIUdnZ2fj5+REREUFERER1d0dEROq4rKwsAgICqrsbUkThzyQmJoaYmJjq7o6IW2jEW6SGKpxe9fPPPzNmzBiCg4OJiopiwoQJZGRkOLXNycnh4YcfplWrVvj4+NC0aVNiY2NJT08v17WWLFlC+/bt8fX1pUOHDixdupTx48fTsmVLR5tvvvkGk8nEN99843TugQMHMJlMLFmyxOn4tm3bGDFiBKGhofj5+dG9e3c++OCDEtc1mUx8+eWXTJgwgYiICAICAsjNzS11qvnXX3/NoEGDaNiwIQEBAfTp04d169Y5tUlOTuaOO+6gWbNm+Pr6EhERQZ8+ffj666/L9f0QEZG6qTC27tixg9GjRxMSEsJ5550H2OPWjTfeSMuWLfH396dly5aMGTOGv//+2+k1CuPThg0buOuuuwgPDycsLIxRo0Zx9OhRp7b5+fk8+OCDREdHExAQwD/+8Q9+/PFHl33bu3cvI0eOJCQkBD8/P7p168bbb7/t1KYwFi9fvpwZM2bQuHFjAgMDGT58OMeOHePEiRPccccdhIeHEx4ezm233cbJkyfP+n0xDIN58+bRokUL/Pz8uPDCC/ni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D4iLiyMvL4/XXnvtrNcScTcl3iK13KBBg5g3bx7vvPMO9957r+P4Rx99xKlTp0oULymqffv2NG7cmBUrVnDfffc5gt7ff//Nli1bHHfaAUfg3bNnD0OGDHEcX716dYnXbNu2Lbt37+bJJ590x5cIQJ8+fWjUqBG//PILkydPLvd5zZs3Z/Lkyaxbt47vvvvObf0REZG6w2QyYRhGiWKkb775JlartVKvWVgN/N1336VHjx6O4x988AEFBQVObQcNGsTHH3/M0aNHnWLv0qVLCQgIKDV5dYdLL70UPz8/3n33Xafp3lu2bOHvv/92SryLvhcoehO9+HuBysbssxkyZAheXl78+eef5ZqaXqhdu3Y8+uijfPTRR+zYscNt/RGpCCXeIrXc5ZdfzpAhQ5gxYwaZmZn06dPHUdW8e/fu3HrrraWeazabeeyxx/jXv/7Ftddey8SJE0lPTycuLq7E9LLo6GgGDx7MU089RUhICC1atGDdunWsWrWqxOu+/vrrXHXVVQwZMoTx48fTtGlTjh8/zq+//sqOHTv48MMPK/x1BgYG8tJLLzFu3DiOHz/O6NGjiYyMJDk5md27d5OcnMyrr75KRkYGAwcO5KabbuL8888nKCiI+Ph41qxZU+oIvIiI1G8NGzakX79+PPvss4SHh9OyZUs2btzIf/7zHxo1alSp1+zQoQO33HIL8+fPx9vbm8GDB7N3717+7//+j4YNGzq1nT17Np999hkDBw5k1qxZhIaG8u677/Lf//6XefPmERwc7Iav0rWQkBCmT5/O448/zr/+9S+uv/56Dh065PK9QM+ePWnfvj3Tp0+noKCAkJAQPv74Y7799lunduWN2RXVsmVL5s6dy7///W/++usvrrzySkJCQjh27Bg//vgjDRo0YM6cOezZs4fJkydz/fXX07ZtW3x8fFi/fj179uxxmtEgUpWUeIvUciaTiU8++YS4uDgWL17ME088QXh4OLfeeitPPvlkmVuJAdx+++0APPPMM4waNYqWLVvyyCOPsHHjxhL7cS5btox77rmHGTNmYLVaGT58OCtWrOCiiy5yajdw4EB+/PFHnnjiCaZNm0ZaWhphYWF07NiRG264odJf6y233ELz5s2ZN28ed955JydOnCAyMpJu3bo5isv5+flxySWXsGzZMg4cOEB+fj7NmzdnxowZJbZjERERKbR8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67mJ87IQEZFWcOBNRERETUvLMZC1GIW+nJeFiIh0hANvIiIiano4LwsREemQkb4DEBERERERETVmHHgTERERERERaREH3kRERERERERaxIE3ERERERERkRZx4E1ERERERESkRRx4ExEREREREWkRB95EREREREREWsSBNxEREREREZEWceBNREREREREpEUceBMRERERERFpEQfeRERERERERFrEgTcRERERERGRFnHgTURERERERKRFHHgTERERERERaREH3kRERERERERaxIE3ERERERERkRZx4E1ERERERESkRY1m4L127Vq0adMG5ubm8PHxwfHjx/UdiYiIiKrBmk1ERE1Noxh4b9u2DaGhofjwww8RFxeHgQMHYtiwYbh9+7a+oxEREVElrNlERNQUSUIIoe8Qf1afPn3Qo0cPrFu3TtXWqVMnjB49GkuXLn3u9rm5ubC1tUVOTg5sbGy0GZWIiOi5GnNd+jM1uzEfFyIianjqUpeMdZRJa4qLixEbG4v58+ertQ8dOhSnTp2qdpuioiIUFRWplnNycgCUHzgiIiJ9q6hHjeCzcTV1rdms10REZMjqUq8b/MD7wYMHUCqVkMvlau1yuRxpaWnVbrN06VJERERUaW/ZsqVWMhIREdVHXl4ebG1t9R1DY+pas1mviYioIahNvW7wA+8KkiSpLQshqrRVWLBgAWbPnq1aLisrw8OHD+Hg4FDjNrWVm5uLli1b4s6dO3o/DY5ZDDuLoeRgFmZpqFkMJYc2sgghkJeXB4VCoYF0hqe2NVub9RownN8hQ8nBLMzSULMYSg5maXpZ6lKvG/zA29HRETKZrMon5enp6VU+Ua9gZmYGMzMztbbmzZtrNJeNjY3ef6kqMEv1DCWLoeQAmKUmzFI9Q8liKDkAzWZpTN90V6hrzdZFvQYM53fIUHIAzFITZqmeoWQxlBwAs9SkMWapbb1u8LOam5qawsfHB1FRUWrtUVFR6Nevn55SERER0bNYs4mIqKlq8N94A8Ds2bPx2muvoWfPnvD19cXXX3+N27dvY8aMGfqORkRERJWwZhMRUVPUKAber776KjIzM7Fo0SKkpqbC09MT+/btg7u7u86zmJmZISwsrMqpcfrALIadxVByMAuzNNQshpLD0LIYOtZsw83BLMzSULMYSg5mYZY/0iju401ERERERERkqBr8Nd5E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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 34 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-18T15:20:23.288515Z", + "start_time": "2024-05-18T15:20:23.284653Z" + } + }, + "cell_type": "code", + "source": [ + "no_meta = [56, 80, 140, 163, 199, 276, 245, 318, 356, 356, 403, 390]\n", + "no_spec = [62, 79, 137, 212, 242, 296, 325, 354, 407, 382, 432, 419]\n", + "no_meta_spec = [60, 85, 134, 235, 255, 260, 314, 358, 381, 410, 400, 441]\n", + "# [59.3, 81.3, 137, 203.3, 232, 277.3, 294.6, 343.3, 381.3, 382.6, 411.6, 416.6]\n", + "\n", + "rand_meta = [49, 70, 99, 127, 146, 177, 212, 254, 224, 289, 289, 343]\n", + "rand_spec = [54, 89, 124, 135, 162, 192, 245, 255, 290, 283, 341, 394]\n", + "rand_meta_spec = [51, 87, 111, 151, 158, 220, 228, 228, 283, 326, 356, 346]\n", + "# [51.3, 82.0, 111.3, 137.6, 155.3, 196.3, 228.3, 245.6, 265.6, 299.3, 328.6, 361]\n", + "\n", + "no_means = [(x+y+z)/3 for (x, y, z) in zip(no_meta, no_spec, no_meta_spec)]\n", + "rand_means = [(x+y+z)/3 for (x, y, z) in zip(rand_meta, rand_spec, rand_meta_spec)]" + ], + "id": "c4606a9965de63ee", + "outputs": [], + "execution_count": 39 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/evaluation/scripts/tsne.ipynb b/scripts/evaluation/scripts/tsne.ipynb new file mode 100644 index 00000000..ec181155 --- /dev/null +++ b/scripts/evaluation/scripts/tsne.ipynb @@ -0,0 +1,242 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "efb84e9fd5eddd81", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2024-05-24T09:46:08.574954Z", + "start_time": "2024-05-24T09:46:08.569938Z" + } + }, + "source": [ + "import requests\n", + "import numpy\n", + "\n", + "from yaml import safe_load\n", + "from matplotlib import pyplot\n", + "from sklearn.manifold import TSNE\n", + "from elasticsearch import Elasticsearch" + ], + "outputs": [], + "execution_count": 25 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Configuration", + "id": "8529ac5ef8d8536a" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:46:08.593465Z", + "start_time": "2024-05-24T09:46:08.589737Z" + } + }, + "cell_type": "code", + "source": [ + "# colors = ['b', 'g', 'r', 'c', 'm', 'y', 'k', 'gray', 'orange', 'brown', 'darkorange', 'aqua', 'pink', 'violet', 'magenta', 'indigo']\n", + "colors = ['lightgray', 'lightgray', 'lightgray', 'lightgray', 'lightgray', 'lightgray', 'lightgray', 'lightgray', 'b', 'g', 'r', 'c', 'lightgray', 'lightgray', 'lightgray', 'lightgray']\n", + "queries_weather = ['weather', 'weather forecast', 'weather forecast service', 'the api returns the next 4 days of weather forecasts']\n", + "queries_sports = ['american sports', 'american sports news', 'american sports news articles', 'An API for accessing news articles across various sports leagues including NFL']\n", + "queries_books = ['digital', 'digital book', 'digital book library', 'API for managing digital library resources retrieving books authors and publishers']\n", + "queries_restaurant = ['restaurant', 'restaurant booking', 'restaurant booking platform', 'Allows users to create a booking for a specific restaurant ID of the restaurant']" + ], + "id": "6b7db41c4d0e577c", + "outputs": [], + "execution_count": 26 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:46:08.603865Z", + "start_time": "2024-05-24T09:46:08.598795Z" + } + }, + "cell_type": "code", + "source": [ + "# Retrieve configuration file for elastic\n", + "with open(\"../../../config/debug.config.yml\") as config:\n", + " config_file = safe_load(config)" + ], + "id": "initial_id", + "outputs": [], + "execution_count": 27 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:46:08.612294Z", + "start_time": "2024-05-24T09:46:08.605612Z" + } + }, + "cell_type": "code", + "source": [ + "es = Elasticsearch(\n", + " f\"{config_file['elastic']['protocol']}://{config_file['elastic']['host']}:{config_file['elastic']['port']}\",\n", + " basic_auth= (config_file['elastic']['user'], config_file['elastic']['password']),\n", + ")" + ], + "id": "ee7210026a60bec8", + "outputs": [], + "execution_count": 28 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:46:08.631420Z", + "start_time": "2024-05-24T09:46:08.613999Z" + } + }, + "cell_type": "code", + "source": "es.info()", + "id": "9b08037c187dff8e", + "outputs": [ + { + "data": { + "text/plain": [ + "ObjectApiResponse({'name': 'es', 'cluster_name': 'docker-cluster', 'cluster_uuid': 'Lx5-0idaRLeK62lK4UJUog', 'version': {'number': '8.12.2', 'build_flavor': 'default', 'build_type': 'docker', 'build_hash': '48a287ab9497e852de30327444b0809e55d46466', 'build_date': '2024-02-19T10:04:32.774273190Z', 'build_snapshot': False, 'lucene_version': '9.9.2', 'minimum_wire_compatibility_version': '7.17.0', 'minimum_index_compatibility_version': '7.0.0'}, 'tagline': 'You Know, for Search'})" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "execution_count": 29 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:46:08.636660Z", + "start_time": "2024-05-24T09:46:08.633054Z" + } + }, + "cell_type": "code", + "source": [ + "def perform_search_query(query: str) -> list[list[int]]:\n", + " embeddings = []\n", + " \n", + " url: str = f\"http://localhost:{config_file['backend']['port']}/api/v1/search?k=10\"\n", + " body: dict[str, str|list[str]] = {\n", + " \"fields\": [\"metadata\"]\n", + " }\n", + " \n", + " if query: body[\"fragment\"] = query\n", + " response = requests.post(url, json=body).json()\n", + " ids = [doc['metadata']['mongo-id'] for doc in response]\n", + " \n", + " for idx in ids:\n", + " response = es.search(index=\"apis\", body={\n", + " \"query\": {\n", + " \"nested\": {\n", + " \"path\": \"metadata\",\n", + " \"query\": {\n", + " \"term\": {\n", + " \"metadata.mongo-id\": idx\n", + " }\n", + " }\n", + " }\n", + " }\n", + " })[\"hits\"][\"hits\"][0][\"_source\"]\n", + " \n", + " embeddings.append(response[\"embedding\"])\n", + " \n", + " response = requests.post(\"http://127.0.0.1:8501/v1/models/universal-encoder:predict\", json={\"instances\": [query]}).json()\n", + " embeddings.append(response[\"predictions\"][0])\n", + " \n", + " return embeddings" + ], + "id": "cdd8ad606dc2f559", + "outputs": [], + "execution_count": 30 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:46:08.641349Z", + "start_time": "2024-05-24T09:46:08.637713Z" + } + }, + "cell_type": "code", + "source": [ + "def compute_tsne(vectors_array: list[list[int]], queries_list: list[str]):\n", + " pyplot.figure(figsize=(13, 8))\n", + " \n", + " data = TSNE(perplexity=40, n_iter=3000, random_state=2).fit_transform(numpy.array(vectors_array))\n", + " \n", + " for i in range(0, len(data), 11):\n", + " alpha = 1 if colors[int(i / 11)] == \"lightgray\" else 0.5\n", + " \n", + " pyplot.scatter(data[i + 10, 0], data[i + 10, 1], marker='1', color=colors[int(i / 11)], label=queries_list[int(i / 11)][:10]+\"...\", alpha=alpha)\n", + " pyplot.scatter(data[i:i + 10, 0], data[i:i + 10, 1], marker='o', color=colors[int(i / 11)], alpha=alpha)\n", + " \n", + " pyplot.ylim((-1500, 1500))\n", + " pyplot.xlim((-1500, 1500))\n", + " pyplot.legend()\n", + " pyplot.savefig(\"out.pdf\")" + ], + "id": "319f690828420438", + "outputs": [], + "execution_count": 31 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2024-05-24T09:46:15.316400Z", + "start_time": "2024-05-24T09:46:08.642023Z" + } + }, + "cell_type": "code", + "source": [ + "queries = []\n", + "vectors = []\n", + "\n", + "queries.extend(queries_weather + queries_sports + queries_books + queries_restaurant)\n", + "vectors.extend([vec for query in queries_weather for vec in perform_search_query(query)])\n", + "vectors.extend([vec for query in queries_sports for vec in perform_search_query(query)])\n", + "vectors.extend([vec for query in queries_books for vec in perform_search_query(query)])\n", + "vectors.extend([vec for query in queries_restaurant for vec in perform_search_query(query)])\n", + "\n", + "compute_tsne(vectors, queries)" + ], + "id": "ca98ceb378684c68", + "outputs": [ + { + "data": { + "text/plain": [ + "
    " + ], + "image/png": "iVBORw0KGgoAAAANSUhEUgAABE4AAAKZCAYAAACm124hAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAADcLUlEQVR4nOzde3xTVbo//s9O0iZtmqSX9AZNaaHSiwW1FuTecpHiiArqiKIiI+NlHKmMKMpRFESFkWFU5OhRD1ocZr5chlMOo4wiaDkCAqVQQHqD2v7SMuWSUtI0JW0u+/dHbIbQFlpokl4+79errzRrr732k1hM8mStZwmiKIogIiIiIiIiIqJWJL4OgIiIiIiIiIiou2LihIiIiIiIiIioHUycEBERERERERG1g4kTIiIiIiIiIqJ2MHFCRERERERERNQOJk6IiIiIiIiIiNrBxAkRERERERERUTuYOCEiIiIiIiIiagcTJ0RERERERERE7WDihIiIiIiIiIioHR5NnPzf//0f7rrrLvTr1w+CIGDLli1ux2fPng1BENx+RowY4danqakJc+fOhVarhVKpxN13343q6mq3PnV1dXj00Ueh0Wig0Wjw6KOP4sKFC558aERERERERETUB3g0cWI2m3HTTTdh9erV7faZMmUKampqXD/btm1zOz5v3jzk5uZi/fr12L17NxoaGjB16lTY7XZXn5kzZ6KwsBBff/01vv76axQWFuLRRx/12OMiIiIiIiIior5BEEVR9MqFBAG5ubmYNm2aq2327Nm4cOFCq5koLYxGI8LDw/GXv/wFM2bMAAD861//gk6nw7Zt25CVlYXi4mKkpKRg3759uO222wAA+/btw8iRI1FSUoLExERPPzQiIiIiIiIi6qVkvg4gLy8PERERCA4ORkZGBt566y1EREQAAAoKCmC1WjF58mRX/379+iE1NRV79+5FVlYWfvzxR2g0GlfSBABGjBgBjUaDvXv3tps4aWpqQlNTk+u+w+HA+fPnERYWBkEQPPRoiYiIiIiIiOhaiaIIk8mEfv36QSLxTtlWnyZO7rjjDvz617/GgAEDUFFRgUWLFmHChAkoKCiAXC7H6dOn4e/vj5CQELfzIiMjcfr0aQDA6dOnXYmWS0VERLj6tGXZsmVYsmRJ1z4gIiIiIiIiIvK4qqoqxMTEeOVaPk2ctCy/AYDU1FSkp6djwIAB+Oqrr3Dvvfe2e54oim6zQtqaIXJ5n8stXLgQzz//vOu+0WhEbGwsqqqqoFarO/tQiIiIiIiIiMjD6uvrodPpoFKpvHZNny/VuVR0dDQGDBiAEydOAACioqLQ3NyMuro6t1knZ8+exahRo1x9zpw502qsc+fOITIyst1ryeVyyOXyVu1qtZqJEyIiIiIiIqJuzJslNryzIKiDamtrUVVVhejoaADArbfeCj8/P3z77beuPjU1Nfjpp59ciZORI0fCaDTiwIEDrj779++H0Wh09SEiIiIiIiIiuhYenXHS0NCAkydPuu5XVFSgsLAQoaGhCA0NxeLFi3HfffchOjoalZWV+I//+A9otVpMnz4dAKDRaDBnzhzMnz8fYWFhCA0NxQsvvIAhQ4Zg0qRJAIDk5GRMmTIFTzzxBD7++GMAwJNPPompU6dyRx0iIiIiIiIiui4eTZwcPHgQ48ePd91vqSny2GOP4aOPPsKxY8fwxRdf4MKFC4iOjsb48eOxYcMGt7VK7777LmQyGR544AFcvHgREydORE5ODqRSqavPX//6V2RnZ7t237n77ruxevVqTz40IiIiIiIiIuoDBFEURV8H0R3U19dDo9HAaDRescaJ3W6H1Wr1YmTUk/j5+bkl9YiIiIiIiKjrdPSze1fqVsVhuzNRFHH69GlcuHDB16FQNxccHIyoqCivFisiIiIiIiIiz2DipINakiYREREIDAzkh2JqRRRFNDY24uzZswDgKnJMREREREREPRcTJx1gt9tdSZOwsDBfh0PdWEBAAADnltkRERFctkNERERERNTDdavtiLurlpomgYGBPo6EeoKWvxPWwiEiIiIiIur5mDjpBC7PoY7g3wkREREREVHvwcQJEREREREREVE7mDghj8vMzMS8efN8HQYRERERERFRpzFxQl0mLy8PgiBwy2YiIiIiIiLqNZg4oR6JhVeJiIiIiIjIG5g46cX+8Y9/IDg4GA6HAwBQWFgIQRDw4osvuvo89dRTeOihhwAAe/fuxbhx4xAQEACdTofs7GyYzWZX33Xr1iE9PR0qlQpRUVGYOXMmzp49CwCorKzE+PHjAQAhISEQBAGzZ892netwOLBgwQKEhoYiKioKixcvdovVaDTiySefREREBNRqNSZMmIAjR464ji9evBg333wzPvvsMwwcOBByuRyiKHbp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4MGDIZVKu/x6RERERERE1HtwqY6XtSQXWm49SaPR4Oabb8a6detcy2rGjRuHQ4cOoayszNUGAC+99BJ+/PFH/P73v0dhYSFOnDiBrVu3Yu7cuQCcs078/f3xwQcf4Oeff8bWrVuxdOlSt+sNGDAAgiDgyy+/xLlz59DQ0NChOCdNmoSRI0di2rRp+Oabb1BZWYm9e/fi1VdfbZWcuRbz58/Hzp07sXTpUpSVlWHt2rVYvXo1XnjhBVefiRMnYvXq1a77q1evxsSJE133T506haSkJBw4cOC64yEiIiIiIqKeg4mTXm78+PGw2+2uJElISAhSUlIQHh6O5ORkV7+hQ4di165dOHHiBMaOHYtbbrkFixYtQnR0NAAgPDwcOTk52LRpE1JSUrB8+XL86U9/crtW//79sWTJErz88suIjIx025HnSgRBwLZt2zBu3Dg8/vjjGDx4MB588EFUVlYiMjLyup+DtLQ0bNy4EevXr0dqaipee+01vPHGG27bJZeXl8NgMLjuGwwGt/oqVqsVpaWlaGxsvO54iIiIiIiIqOfgrjq/8OSuOpeqq6uDyWSCSqVCSEjIdY1F3RN31SEiIiIiIvIMX+yqwxonXhYSEsKECREREREREVEPwaU6RERERERERETtYOKEiIiIiIiIiKgdTJwQEREREREREbWDiRMiIiIiIiIionYwcUJERERERERE1A4mToiIiIguceYMcPGie9vFi852IiIi6nu4HTERERHRL6xWYMMGoKkJmDIFSE0Fjh0DvvkGkMuBZ54BZHz3RERE1KfwpZ+IiIjoFyYTIJUCZjOwebPzp0VgIFBfD4SG+i4+IiIi8j4u1SGPy8zMxLx587x2vS1btiAhIQFSqdSr1yUiop4vNBR4+mlgwgT39okTne1MmhAREfU9TJxQl8nLy4MgCLhw4YJP43jqqadw//33o6qqCkuXLvVpLERE1PNIpcC4cUBysvN+cjIwdqyznYiIiPoeLtWhHslqtcLPz69Ve0NDA86ePYusrCz069fPB5EREVFvERLifktERER9E2ec9FL/+Mc/EBwcDIfDAQAoLCyEIAh48cUXXX2eeuopPPTQQ677e/fuxbhx4xAQEACdTofs7GyYzWbX8XXr1iE9PR0qlQpRUVGYOXMmzp49CwCorKzE+PHjAQAhISEQBAGzZ892netwOLBgwQKE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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 32 + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scripts/hooks/generate-go-docs.sh b/scripts/hooks/generate-go-docs.sh new file mode 100755 index 00000000..039fa82f --- /dev/null +++ b/scripts/hooks/generate-go-docs.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +cd ./app/internal || exit +doc2go -config ./doc2go.rc ./... diff --git a/scripts/hooks/generate-swagger-docs.sh b/scripts/hooks/generate-swagger-docs.sh new file mode 100755 index 00000000..3834ceae --- /dev/null +++ b/scripts/hooks/generate-swagger-docs.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +cd ./app || exit +swag init \ No newline at end of file