diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 509c502..cf446ad 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -23,6 +23,9 @@ "ghcr.io/devcontainers/features/node:1": { "version": "lts", "nvmVersion": "latest" + }, + "ghcr.io/devcontainers/features/go:1": { + "version": "latest" } }, "customizations": { @@ -35,7 +38,8 @@ "redhat.vscode-yaml", "ms-python.black-formatter", "tamasfe.even-better-toml", - "streetsidesoftware.code-spell-checker" + "streetsidesoftware.code-spell-checker", + "remcohaszing.schemastore" ] } }, @@ -47,7 +51,7 @@ // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "./scripts/post_create.sh", + "postCreateCommand": "./scripts/post-create.sh", // Configure tool-specific properties. // "customizations": {}, diff --git a/.github/workflows/check_spec.yaml b/.github/workflows/check_spec.yaml index ecf63dc..dd9d409 100644 --- a/.github/workflows/check_spec.yaml +++ b/.github/workflows/check_spec.yaml @@ -22,14 +22,14 @@ jobs: pip install -r requirements.txt - name: Check if specs have changed run: | - ./scripts/create_specs.sh - ./scripts/check_specs.sh + ./scripts/create-specs.sh + ./scripts/check-specs.sh exit_code=$? echo "Exit code: $exit_code" if [ $exit_code -ne 0 ]; then - echo "Specs have changed, please run 'create_specs' and commit the changes" + echo "Specs have changed, please run 'create-specs' and commit the changes" exit 1 fi diff --git a/.github/workflows/publish_sdk.yaml b/.github/workflows/publish_sdk.yaml index b1a62ff..7a9f0db 100644 --- a/.github/workflows/publish_sdk.yaml +++ b/.github/workflows/publish_sdk.yaml @@ -2,6 +2,8 @@ name: Publish SDK on: push: + branches: + - main paths: - 'spec.json' - 'liblab.config.json' diff --git a/.vscode/launch.json b/.vscode/launch.json index 3d3b2ff..2784961 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,8 +5,15 @@ "version": "0.2.0", "configurations": [ { - "name": "Python: FastAPI", - "type": "python", + "name": "Python - Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + }, + { + "name": "Debug llama store", + "type": "debugpy", "request": "launch", "module": "uvicorn", "env": { diff --git a/Dockerfile b/Dockerfile index a46ead7..cb5eada 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ WORKDIR /llama_store/ COPY ./llama_store /llama_store/ COPY ./requirements.txt /requirements.txt -COPY ./scripts/recreate_database.sh /scripts/recreate_database.sh +COPY ./scripts/recreate-database.sh /scripts/recreate-database.sh RUN pip install -r /requirements.txt -RUN chmod +x /scripts/recreate_database.sh && /scripts/recreate_database.sh +RUN chmod +x /scripts/recreate-database.sh && /scripts/recreate-database.sh EXPOSE 80 diff --git a/README.md b/README.md index 5b1bb5d..d58b9ae 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,11 @@ This repo also has a devContainer file, so you can also open it using the dev co ### Prime the database -Before you can run the API you need to configure the SQLite database that is used to store the llamas. This database needs to be primed with some llama data for 6 llamas, as well as creating some pictures. You can create the database using the `recreate_database.sh` script in the [`scripts`](./scripts/) folder: +Before you can run the API you need to configure the SQLite database that is used to store the llamas. This database needs to be primed with some llama data for 6 llamas, as well as creating some pictures. You can create the database using the `recreate-database.sh` script in the [`scripts`](./scripts/) folder: ```bash cd scripts -./recreate_database.sh +./recreate-database.sh ``` This will create a database called `sql_app.db` in the [`llama_store/.appdata`](/llama_store/.appdata) folder. It will add the following tables to this database: @@ -57,7 +57,7 @@ You can also run this from the command line using the `uvicorn` command: uvicorn main:app --reload ``` -Either way will launch the API on localhost on port 8000. You can then navigate to [http://localhost:8000/docs](http://localhost:8000/docs) to see the Swagger UI for the API. You can change the port number by passing the `--port` parameter to `uvicorn`: +Either way will launch the API on localhost on port 8080. You can then navigate to [http://localhost:8080/docs](http://localhost:8080/docs) to see the Swagger UI for the API. You can change the port number by passing the `--port` parameter to `uvicorn`: ```bash uvicorn main:app --reload --port 80 @@ -88,16 +88,16 @@ docker buildx build --platform=linux/arm64 -t llama-store . You can then run the container. On x86/x64 platforms run: ```bash -docker run -p 80:8000 llama-store +docker run -p 80:8080 llama-store ``` On ARM64 (such as macOS on Apple Silicon), run the following: ```bash -docker run --platform=linux/arm64 -p 8000:80 llama-store +docker run --platform=linux/arm64 -p 8080:80 llama-store ``` -This will run on port 8000. Change the port number if you want to run it on a different port. The Docker container exposes port 80, but this run command maps it to port 8000 on the host to be consistent with the default `uvicorn` command. +This will run on port 8080. Change the port number if you want to run it on a different port. The Docker container exposes port 80, but this run command maps it to port 8080 on the host to be consistent with the default `uvicorn` command. ## API end points @@ -171,48 +171,7 @@ If you don't have an account - [join our beta](https://liblab.com/join). You can learn more about how to use liblab from our [developer docs](https://developers.liblab.com). -The liblab CLI uses a [config file called `liblab.config.json`](https://developers.liblab.com/cli/config-file-overview) to configure the SDK. This repo has a config file called [`liblab.config.json`](./liblab.config.json) that you can use to generate the SDK. This config file has the following settings: - -```json -{ - "sdkName": "llama-store", - "specFilePath": "spec.json", - "languages": [ - "python", - "java", - "typescript" - ], - "auth": [ - "bearer" - ], - "createDocs": true, - "customizations": { - "devContainer": true, - "license": { - "type": "MIT" - } - }, - "languageOptions": { - "typescript": { - "githubRepoName": "llama-store-sdk-typescript", - "sdkVersion": "0.0.1" - }, - "python": { - "pypiPackageName": "LlamaStore", - "githubRepoName": "llama-store-sdk-python", - "sdkVersion": "0.0.1" - }, - "java": { - "groupId": "com.liblab", - "githubRepoName": "llama-store-sdk-java", - "sdkVersion": "0.0.1" - } - }, - "publishing": { - "githubOrg": "liblaber" - } -} -``` +The liblab CLI uses a [config file called `liblab.config.json`](https://developers.liblab.com/cli/config-file-overview) to configure the SDK. This repo has a config file called [`liblab.config.json`](./liblab.config.json) that you can use to generate the SDK. This config file reads the local `spec.json` file. If you want to generate an SDK from a running API, you can change this to the URL of that API. SDKs will be generated for Java, Python and TypeScript with a name of `llama-store` (adjust to be language specific, so `llamaStore` in Java and TypeScript). The SDKs will be configured to use bearer tokens for authentication, and will include documentation. The generated SDKs will also be set up with dev containers for VS Code, so you can open the created SDK folder and get going straight away. @@ -235,6 +194,8 @@ You can find pre-built SDKs in the following GitHub repos: | Python | [llama-store-sdk-python](https://github.com/liblaber/llama-store-sdk-python) | | Java | [llama-store-sdk-java](https://github.com/liblaber/llama-store-sdk-java) | | TypeScript | [llama-store-sdk-typescript](https://github.com/liblaber/llama-store-sdk-typescript) | +| C# | [llama-store-sdk-csharp](https://github.com/liblaber/llama-store-sdk-csharp) | +| Go | [llama-store-sdk-go](https://github.com/liblaber/llama-store-sdk-go) | These are generated by a GitHub action, and are updated whenever the spec changes. You can find the `publish_sdk.yaml` action in the [`.github/workflows`](./.github/workflows) folder. @@ -295,10 +256,10 @@ Next, you need to launch the Llama store: 1. From a terminal, run: ```bash - ./scripts/start_llama_store.sh + ./scripts/start-llama-store.sh ``` - This will reset the llama store database, then launch the API on port 8000. + This will reset the llama store database, then launch the API on port 8080. Once you have done this, you can run the examples. You will need to create a new terminal to do this. @@ -327,6 +288,14 @@ To run the TypeScript examples, navigate to the [`sdk-examples/typescript`](./sd This will create a user, generate an API token, and create a llama. +1. Run the get llamas demo again with the following command: + + ```bash + npm run get-llamas + ``` + + You will see the llama you created in the previous step in the list of llamas. + ### Python To run the Python examples, navigate to the [`sdk-examples/python`](./sdk-examples/python) folder. @@ -355,11 +324,47 @@ To run the Python examples, navigate to the [`sdk-examples/python`](./sdk-exampl This will create a user, generate an API token, and create a llama, uploading a picture. +1. Run the get llamas demo again with the following command: + + ```bash + python get_llamas.py + ``` + + You will see the llama you created in the previous step in the list of llamas. + +### Go + +To run the Go examples, you will need to copy the contents of the [`sdk-examples/go`](./sdk-examples/go) folder into the [`output/go/cmd/examples`](./output/go/cmd/examples) folder. + +1. Run the get llamas demo with the following command: + + ```bash + go run get-llamas.go + ``` + + This will create a user, generate an API token, and print out a list of llamas. This demo shows the ability to call services on the SDK, set an API token once, and use that for all subsequent calls. + +1. Run the create llamas demo with the following command: + + ```bash + go run create-llama.go + ``` + + This will create a user, generate an API token, and create a llama, uploading a picture. + +1. Run the get llamas demo again with the following command: + + ```bash + go run get-llamas.go + ``` + + You will see the llama you created in the previous step in the list of llamas. + ## OpenAPI spec The OpenAPI spec for this API is in the [`spec.json`](/spec.json) and [`spec.yaml`](/spec.yaml) files. These need to be generated whenever the spec changes. To do this, run the following command: ```bash cd scripts -./create_specs.sh +./create-specs.sh ``` diff --git a/hooks/csharp/CustomHook.cs b/hooks/csharp/CustomHook.cs new file mode 100644 index 0000000..d359faa --- /dev/null +++ b/hooks/csharp/CustomHook.cs @@ -0,0 +1,20 @@ + +public class CustomHook : IHook +{ + public async Task BeforeRequestAsync(HttpRequestMessage request) + { + Console.WriteLine($"Before request on URL {request.RequestUri.AbsoluteUri} with method {request.Method.ToUpper()}"); + return request; + } + + public async Task AfterResponseAsync(HttpResponseMessage response) + { + Console.WriteLine($"After response on URL {response.RequestMessage.RequestUri.AbsoluteUri} with method {response.RequestMessage.Method.ToUpper()}, returning status {response.StatusCode}") + return response; + } + + public async Task OnErrorAsync(HttpResponseMessage response) + { + Console.WriteLine($"On error - {response.StatusCode} - {response.ReasonPhrase}"); + } +} diff --git a/hooks/go/go.mod b/hooks/go/go.mod new file mode 100644 index 0000000..48d2dbe --- /dev/null +++ b/hooks/go/go.mod @@ -0,0 +1,3 @@ +module github.com/liblaber/sdkhook + +go 1.18 diff --git a/hooks/go/hooks/custom_hook.go b/hooks/go/hooks/custom_hook.go new file mode 100644 index 0000000..b24506c --- /dev/null +++ b/hooks/go/hooks/custom_hook.go @@ -0,0 +1,26 @@ +package hooks + +import ( + "fmt" +) + +type CustomHook struct{} + +func NewCustomHook() Hook { + return &CustomHook{} +} + +func (h *CustomHook) BeforeRequest(req Request) Request { + fmt.Printf("Before request on URL %#v with method %#v\n", req.GetBaseUrl(), req.GetMethod()) + return req +} + +func (h *CustomHook) AfterResponse(req Request, resp Response) Response { + fmt.Printf("After response on URL %#v with method %#v, returning status %d\n", req.GetBaseUrl(), req.GetMethod(), resp.GetStatusCode()) + return resp +} + +func (h *CustomHook) OnError(req Request, resp ErrorResponse) ErrorResponse { + fmt.Printf("On Error: %#v\n", resp.Error()) + return resp +} diff --git a/hooks/go/hooks/hook.go b/hooks/go/hooks/hook.go new file mode 100644 index 0000000..c9eb172 --- /dev/null +++ b/hooks/go/hooks/hook.go @@ -0,0 +1,44 @@ +package hooks + +type Hook interface { + BeforeRequest(req Request) Request + AfterResponse(req Request, resp Response) Response + OnError(req Request, resp ErrorResponse) ErrorResponse +} + +type Request interface { + GetMethod() string + SetMethod(method string) + GetBaseUrl() string + SetBaseUrl(baseUrl string) + GetPath() string + SetPath(path string) + GetHeader(header string) string + SetHeader(header string, value string) + GetPathParam(param string) string + SetPathParam(param string, value any) + GetQueryParam(param string) string + SetQueryParam(param string, value any) + GetBody() any + SetBody(body any) +} + +type Response interface { + GetStatusCode() int + SetStatusCode(statusCode int) + GetHeader(header string) string + SetHeader(header string, value string) + GetBody() []byte + SetBody(body []byte) +} + +type ErrorResponse interface { + Error() string + GetError() error + GetStatusCode() int + SetStatusCode(statusCode int) + GetHeader(header string) string + SetHeader(header string, value string) + GetBody() []byte + SetBody(body []byte) +} diff --git a/hooks/python/.gitignore b/hooks/python/.gitignore new file mode 100644 index 0000000..68bc17f --- /dev/null +++ b/hooks/python/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/hooks/python/requirements.txt b/hooks/python/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/hooks/python/src/hook.py b/hooks/python/src/hook.py new file mode 100644 index 0000000..796dc59 --- /dev/null +++ b/hooks/python/src/hook.py @@ -0,0 +1,32 @@ +class Request: + def __init__(self, method, url, headers, body=''): + self.method = method + self.url = url + self.headers = headers + self.body = body + + def __str__(self): + return f"method={self.method}, url={self.url}, headers={self.headers}, body={self.body})" + + +class Response: + def __init__(self, status, headers, body): + self.status = status + self.headers = headers + self.body = body + + def __str__(self): + return "Response(status={}, headers={}, body={})".format( + self.status, self.headers, self.body) + + +class CustomHook: + + def before_request(self, request: Request): + print(f"Before request on URL {request.url} with method {request.method.upper()}") + + def after_response(self, request: Request, response: Response): + print(f"After response on URL {request.url} with method {request.method.upper()}, returning status {response.status}") + + def on_error(self, error: Exception, request: Request, response: Response): + print(f"On error - {error}") diff --git a/hooks/python/test/__init__.py b/hooks/python/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hooks/python/test/test_hook.py b/hooks/python/test/test_hook.py new file mode 100644 index 0000000..7c57e91 --- /dev/null +++ b/hooks/python/test/test_hook.py @@ -0,0 +1,26 @@ +import unittest + +from src.hook import CustomHook, Request + +class TestCustomHook(unittest.TestCase): + + @classmethod + def setUpClass(cls): + pass + + def test_before_request(self): + hook = CustomHook() + + request = Request("GET", "https://api.example.com", { + "Content-Type": "application/json", + }) + + hook.before_request(request) + + # Assert the header is set + self.assertTrue(request.headers["Content-Type"] is not None) + self.assertEqual(request.headers["Content-Type"], "application/json") + + +if __name__ == '__main__': + unittest.main() diff --git a/hooks/typescript/.env.example b/hooks/typescript/.env.example new file mode 100644 index 0000000..7f80e75 --- /dev/null +++ b/hooks/typescript/.env.example @@ -0,0 +1,2 @@ +SECRET_KEY="" +ACCESS_KEY="" \ No newline at end of file diff --git a/hooks/typescript/.eslintignore b/hooks/typescript/.eslintignore new file mode 100644 index 0000000..27602d0 --- /dev/null +++ b/hooks/typescript/.eslintignore @@ -0,0 +1 @@ +templates/ \ No newline at end of file diff --git a/hooks/typescript/.eslintrc.js b/hooks/typescript/.eslintrc.js new file mode 100644 index 0000000..991ba55 --- /dev/null +++ b/hooks/typescript/.eslintrc.js @@ -0,0 +1,36 @@ +module.exports = { + env: { + browser: true, + commonjs: true, + es2021: true, + }, + extends: ['airbnb-base', 'airbnb-typescript/base', 'prettier'], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.eslint.json', + }, + plugins: ['@typescript-eslint', 'prettier'], + rules: { + 'no-console': 'off', // TODO: get a real logging solution and remove this line + 'max-len': [ + 'error', + { + code: 100, + ignoreComments: true, + ignoreRegExpLiterals: true, + ignoreStrings: true, + ignoreTemplateLiterals: true, + }, + ], + 'prettier/prettier': 'error', + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + }, + }, +}; diff --git a/hooks/typescript/.gitignore b/hooks/typescript/.gitignore new file mode 100644 index 0000000..c6bba59 --- /dev/null +++ b/hooks/typescript/.gitignore @@ -0,0 +1,130 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/hooks/typescript/.nvmrc b/hooks/typescript/.nvmrc new file mode 100644 index 0000000..7fd0237 --- /dev/null +++ b/hooks/typescript/.nvmrc @@ -0,0 +1 @@ +v16.15.0 diff --git a/hooks/typescript/.prettierignore b/hooks/typescript/.prettierignore new file mode 100644 index 0000000..d502512 --- /dev/null +++ b/hooks/typescript/.prettierignore @@ -0,0 +1,2 @@ +/node_modules +/package-lock.json diff --git a/hooks/typescript/.prettierrc.js b/hooks/typescript/.prettierrc.js new file mode 100644 index 0000000..90e4eb5 --- /dev/null +++ b/hooks/typescript/.prettierrc.js @@ -0,0 +1,22 @@ +module.exports = { + printWidth: 100, // https://github.com/airbnb/javascript#19.13 + tabWidth: 2, // https://github.com/airbnb/javascript#19.1 + useTabs: false, // https://github.com/airbnb/javascript#19.1 + semi: true, // https://github.com/airbnb/javascript#21.1 + singleQuote: true, // https://github.com/airbnb/javascript#6.1 + quoteProps: 'as-needed', // https://github.com/airbnb/javascript#3.6 + jsxSingleQuote: false, // https://github.com/airbnb/javascript/tree/master/react#quotes + trailingComma: 'all', // https://github.com/airbnb/javascript#20.2 + bracketSpacing: true, // https://github.com/airbnb/javascript#19.12 + arrowParens: 'always', // https://github.com/airbnb/javascript#8.4 + overrides: [ + { + files: '.editorconfig', + options: { parser: 'yaml' }, + }, + { + files: 'LICENSE', + options: { parser: 'markdown' }, + }, + ], +}; diff --git a/hooks/typescript/package.json b/hooks/typescript/package.json new file mode 100644 index 0000000..2a7694c --- /dev/null +++ b/hooks/typescript/package.json @@ -0,0 +1,32 @@ +{ + "name": "custom-liblab-plugin", + "version": "1.0.0", + "description": "Sample Plugin to add custom code to LibLab's generated sdk", + "scripts": { + "build": "npm test && tsc", + "test": "env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\" }' mocha -r ts-node/register 'tests/**/*.ts'", + "format": "npx prettier \"{src,tests}/**/*.ts\" --write", + "format:check": "npx prettier \"{src,tests}/**/*.ts\" --check", + "lint": "npx eslint \"{src,tests}/**/*.ts\" --cache", + "lint:init": "npx eslint --init", + "lint:ci": "npx eslint \"{src,tests}/**/*.ts\" --cache --quiet", + "lint:fix": "npx eslint \"{src,tests}/**/*.ts\" --cache --fix" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@types/chai": "^4.3.3", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.3", + "chai": "^4.3.6", + "dotenv": "^16.0.3", + "mocha": "^3.5.3", + "ts-node": "^10.9.1", + "typescript": "^4.8.4", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-prettier": "^4.2.1" + } +} diff --git a/hooks/typescript/prettier.config.js b/hooks/typescript/prettier.config.js new file mode 100644 index 0000000..b470a6a --- /dev/null +++ b/hooks/typescript/prettier.config.js @@ -0,0 +1,35 @@ +// Some settings automatically inherited from .editorconfig +// We still need this config for npm run test in CI/CD + +module.exports = { + printWidth: 100, // https://github.com/airbnb/javascript#19.13 + tabWidth: 2, // https://github.com/airbnb/javascript#19.1 + useTabs: false, // https://github.com/airbnb/javascript#19.1 + semi: true, // https://github.com/airbnb/javascript#21.1 + singleQuote: true, // https://github.com/airbnb/javascript#6.1 + quoteProps: 'as-needed', // https://github.com/airbnb/javascript#3.6 + jsxSingleQuote: false, // https://github.com/airbnb/javascript/tree/master/react#quotes + trailingComma: 'all', // https://github.com/airbnb/javascript#20.2 + bracketSpacing: true, // https://github.com/airbnb/javascript#19.12 + arrowParens: 'always', // https://github.com/airbnb/javascript#8.4 + overrides: [ + { + files: '.editorconfig', + options: { parser: 'yaml' }, + }, + { + files: 'LICENSE', + options: { parser: 'markdown' }, + }, + { + files: '*.java', + options: { + parser: 'java', + printWidth: 140, + tabWidth: 4, + useTabs: true, + trailingComma: false, + }, + }, + ], +}; diff --git a/hooks/typescript/src/index.ts b/hooks/typescript/src/index.ts new file mode 100644 index 0000000..5386379 --- /dev/null +++ b/hooks/typescript/src/index.ts @@ -0,0 +1,70 @@ +/** + * Request to the API + */ +export interface Request { + method: string; + url: string; + input?: object; + headers: object; +} + +/** + * Response from the API + */ +export interface Response { + data: object; + headers: object; + status: number; +} + +/** + * Exception thrown by the API + */ +export interface Exception extends Error { + title: string; + type?: string; + detail?: string; + instance?: string; + statusCode: number; +} + +/** + * Standard Hook interface + */ +export interface Hook { + /** + * Called before the request is sent to the API + * @param request + */ + beforeRequest(request: Request): Promise; + + /** + * Called after the response is received from the API + * @param request + * @param response + */ + afterResponse(request: Request, response: Response): Promise; + + /** + * Called when an error occurs + * @param error + */ + onError(error: Exception): Promise; +} + +/** + * Custom Hook + */ +export default class CustomHook implements Hook { + async beforeRequest(request: Request): Promise { + console.log('Before request on URL', request.url, 'with method', request.method.toUpperCase()); + } + + async afterResponse(request: Request, response: Response): Promise { + console.log('After response on URL', request.url, 'with method', request.method.toUpperCase(), ', returning status', response.status); + } + + async onError(error: Exception): Promise { + console.error('an error occurred!'); + } +} diff --git a/hooks/typescript/tests/hook.spec.ts b/hooks/typescript/tests/hook.spec.ts new file mode 100644 index 0000000..b1db9d6 --- /dev/null +++ b/hooks/typescript/tests/hook.spec.ts @@ -0,0 +1,33 @@ +import { beforeEach } from 'mocha'; +import { expect } from 'chai'; +import CustomHook, { Request } from "../src/index"; +import * as dotenv from 'dotenv'; +import { assert } from 'console'; + +dotenv.config(); + +describe('test custom hook', () => { + let hook = new CustomHook(); + + beforeEach(() => { + hook = new CustomHook(); + }); + + it('empty passing test', async () => { + + const request: Request = { + method: 'GET', + url: 'https://api.example.com', + input: {}, + headers: { + 'Content-Type': 'application/json', + }, + }; + + await hook.beforeRequest(request); + console.log(request.headers) + + // Assert the headers are correct + assert(request.headers['Content-Type'] === 'application/json'); + }); +}); diff --git a/hooks/typescript/tsconfig.eslint.json b/hooks/typescript/tsconfig.eslint.json new file mode 100644 index 0000000..78445d0 --- /dev/null +++ b/hooks/typescript/tsconfig.eslint.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "./src", "./tests" + ] +} diff --git a/hooks/typescript/tsconfig.json b/hooks/typescript/tsconfig.json new file mode 100644 index 0000000..59b9410 --- /dev/null +++ b/hooks/typescript/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "lib": [ + "es2015", + "dom" + ], + "module": "commonjs", + "target": "es6", + "moduleResolution": "node", + "sourceMap": false, + "allowSyntheticDefaultImports": true + }, + "outDir": "./dist", + "includes": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "lib", + "tests" + ] +} \ No newline at end of file diff --git a/liblab.config.json b/liblab.config.json index 6af231b..5205df3 100644 --- a/liblab.config.json +++ b/liblab.config.json @@ -1,19 +1,27 @@ { "sdkName": "llama-store", "specFilePath": "spec.json", + "baseUrl": "http://localhost:8080", "languages": [ - "typescript", + "csharp", + "go", + "java", "python", - "java" + "typescript" ], "auth": [ "bearer" ], - "createDocs": false, + "createDocs": true, "customizations": { "devContainer": true, "license": { "type": "MIT" + }, + "authentication": { + "access": { + "prefix": "Bearer" + } } }, "languageOptions": { @@ -21,17 +29,26 @@ "bundle": true, "exportClassDefault": true, "githubRepoName": "llama-store-sdk-typescript", - "sdkVersion": "0.0.1" + "sdkVersion": "0.0.3" }, "python": { "pypiPackageName": "LlamaStore", "githubRepoName": "llama-store-sdk-python", - "sdkVersion": "0.0.1" + "sdkVersion": "0.0.3" }, "java": { "groupId": "com.liblab", "githubRepoName": "llama-store-sdk-java", - "sdkVersion": "0.0.1" + "sdkVersion": "0.0.3" + }, + "go": { + "goModuleName": "github.com/liblaber/llama-store-sdk-go", + "githubRepoName": "llama-store-sdk-go", + "sdkVersion": "0.0.3" + }, + "csharp": { + "githubRepoName": "llama-store-sdk-csharp", + "sdkVersion": "0.0.3" } }, "publishing": { diff --git a/llama_store/data/database.py b/llama_store/data/database.py index e6eee68..aa14433 100644 --- a/llama_store/data/database.py +++ b/llama_store/data/database.py @@ -1,6 +1,7 @@ """ Code to interact with the SQLite database """ + # pylint: disable=invalid-name from sqlalchemy import create_engine diff --git a/llama_store/data/files.py b/llama_store/data/files.py index 4c9859a..3e092c3 100644 --- a/llama_store/data/files.py +++ b/llama_store/data/files.py @@ -1,6 +1,7 @@ """ Methods for interacting with files. Llama pictures are stored on the file system, not in the database. """ + import io import os diff --git a/llama_store/data/llama_crud.py b/llama_store/data/llama_crud.py index 3abb1d3..8c2d02b 100644 --- a/llama_store/data/llama_crud.py +++ b/llama_store/data/llama_crud.py @@ -1,6 +1,7 @@ """ This file contains the functions that will be used to interact with llamas in the database. """ + # pylint: disable=invalid-name from typing import List diff --git a/llama_store/data/llama_picture_crud.py b/llama_store/data/llama_picture_crud.py index 4bb65ec..ea3fc8c 100644 --- a/llama_store/data/llama_picture_crud.py +++ b/llama_store/data/llama_picture_crud.py @@ -1,6 +1,7 @@ """ This file contains the functions that will be used to interact with llamas in the database. """ + # pylint: disable=invalid-name from sqlalchemy.orm import Session diff --git a/llama_store/data/schema.py b/llama_store/data/schema.py index 2d4d2d4..66760c6 100644 --- a/llama_store/data/schema.py +++ b/llama_store/data/schema.py @@ -1,6 +1,7 @@ """ The schema models used by the ORM to store data in the database. """ + # pylint: disable=too-few-public-methods from sqlalchemy import Column, Integer, String diff --git a/llama_store/data/security.py b/llama_store/data/security.py index 19286ad..ee4d1c1 100644 --- a/llama_store/data/security.py +++ b/llama_store/data/security.py @@ -2,6 +2,7 @@ This file contains functions to help with API security. This includes password hashing, token creation and token validation. """ + # pylint: disable=invalid-name from datetime import datetime, timedelta diff --git a/llama_store/data/user_crud.py b/llama_store/data/user_crud.py index c1b1d57..2f2b36b 100644 --- a/llama_store/data/user_crud.py +++ b/llama_store/data/user_crud.py @@ -1,6 +1,7 @@ """ This file contains the functions that will be used to interact with users in the database. """ + # pylint: disable=invalid-name from typing import Annotated, List diff --git a/llama_store/db_migrations/env.py b/llama_store/db_migrations/env.py index d2679ca..f17d48f 100644 --- a/llama_store/db_migrations/env.py +++ b/llama_store/db_migrations/env.py @@ -1,6 +1,7 @@ """ Alembic environment configuration file. """ + # pylint: disable=invalid-name,no-member from logging.config import fileConfig diff --git a/llama_store/db_migrations/versions/first.py b/llama_store/db_migrations/versions/first.py index fb14f41..471ee1b 100644 --- a/llama_store/db_migrations/versions/first.py +++ b/llama_store/db_migrations/versions/first.py @@ -5,6 +5,7 @@ Create Date: 2023-09-29 01:15:55.669005 """ + # pylint: disable=invalid-name,no-member import secrets from typing import Sequence, Union diff --git a/llama_store/export_openapi.py b/llama_store/export_openapi.py index 5de2f28..47402d3 100644 --- a/llama_store/export_openapi.py +++ b/llama_store/export_openapi.py @@ -1,6 +1,7 @@ """ Helper script to extract the OpenAPI spec from a FastAPI app. """ + import argparse import json import sys diff --git a/llama_store/main.py b/llama_store/main.py index 167e55b..c84667f 100644 --- a/llama_store/main.py +++ b/llama_store/main.py @@ -21,6 +21,7 @@ This will create an SDK in the output folder. You can then use the SDK in your project, or see one of the examples. """ + import functools import io import os @@ -71,11 +72,11 @@ ] app = FastAPI( - servers=[{"url": "http://localhost:8000", "description": "Prod"}], + servers=[{"url": "http://localhost:8080", "description": "Prod"}], contact={"name": "liblab", "url": "https://liblab.com"}, description=OPENAPI_DESCRIPTION, openapi_tags=tags_metadata, - version="0.0.1", + version="0.1.7", redirect_slashes=True, title="Llama Store API", ) @@ -132,4 +133,4 @@ def read_openapi_yaml() -> Response: # Run the app if __name__ == "__main__": - uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) + uvicorn.run("main:app", host="0.0.0.0", port=8080, reload=True) diff --git a/llama_store/models/llama.py b/llama_store/models/llama.py index 9ddaadc..cc00226 100644 --- a/llama_store/models/llama.py +++ b/llama_store/models/llama.py @@ -1,6 +1,7 @@ """ Llama models. These are used by the llama endpoints. """ + from enum import Enum from pydantic import BaseModel, Field diff --git a/llama_store/models/llama_picture.py b/llama_store/models/llama_picture.py index 9130b03..4816200 100644 --- a/llama_store/models/llama_picture.py +++ b/llama_store/models/llama_picture.py @@ -1,6 +1,7 @@ """ Llama models. These are used by the llama endpoints. """ + from pydantic import BaseModel diff --git a/llama_store/models/token.py b/llama_store/models/token.py index 4d42ace..6b3297d 100644 --- a/llama_store/models/token.py +++ b/llama_store/models/token.py @@ -1,6 +1,7 @@ """ User models. These are used by the user endpoints. """ + # pylint: disable=duplicate-code from pydantic import BaseModel, Field diff --git a/llama_store/models/user.py b/llama_store/models/user.py index d998480..b70a40e 100644 --- a/llama_store/models/user.py +++ b/llama_store/models/user.py @@ -1,6 +1,7 @@ """ User models. These are used by the user endpoints. """ + # pylint: disable=duplicate-code import re diff --git a/llama_store/openapi.py b/llama_store/openapi.py index 2552e31..39dcd01 100644 --- a/llama_store/openapi.py +++ b/llama_store/openapi.py @@ -1,6 +1,7 @@ """ Helpers for the OpenAPI spec generated by FastAPI. """ + # pylint: disable=line-too-long from fastapi import FastAPI from data.user_crud import MAXIMUM_USERS diff --git a/llama_store/routers/llama_picture_read.py b/llama_store/routers/llama_picture_read.py index 5c3a2bd..a1857c4 100644 --- a/llama_store/routers/llama_picture_read.py +++ b/llama_store/routers/llama_picture_read.py @@ -1,6 +1,7 @@ """ The endpoints for reading llama picturess. """ + # pylint: disable=invalid-name from typing import Annotated diff --git a/llama_store/routers/llama_picture_write.py b/llama_store/routers/llama_picture_write.py index a8fdc0b..2eff8de 100644 --- a/llama_store/routers/llama_picture_write.py +++ b/llama_store/routers/llama_picture_write.py @@ -1,6 +1,7 @@ """ The endpoints for creating, updating and deleting llamas. """ + # pylint: disable=invalid-name from typing import Annotated diff --git a/llama_store/routers/llama_read.py b/llama_store/routers/llama_read.py index ecea430..332147c 100644 --- a/llama_store/routers/llama_read.py +++ b/llama_store/routers/llama_read.py @@ -1,6 +1,7 @@ """ The endpoints for reading llamas. """ + # pylint: disable=invalid-name from typing import Annotated, List diff --git a/llama_store/routers/llama_write.py b/llama_store/routers/llama_write.py index 04a165b..e577d4c 100644 --- a/llama_store/routers/llama_write.py +++ b/llama_store/routers/llama_write.py @@ -1,6 +1,7 @@ """ The endpoints for creating, updating and deleting llamas. """ + # pylint: disable=invalid-name from typing import Annotated diff --git a/llama_store/routers/token.py b/llama_store/routers/token.py index a182076..f04337a 100644 --- a/llama_store/routers/token.py +++ b/llama_store/routers/token.py @@ -1,6 +1,7 @@ """ The endpoints for accessing users. """ + # pylint: disable=invalid-name from fastapi import APIRouter, Depends, HTTPException, status diff --git a/llama_store/routers/user_debug.py b/llama_store/routers/user_debug.py index 864acd9..b0d0a49 100644 --- a/llama_store/routers/user_debug.py +++ b/llama_store/routers/user_debug.py @@ -1,6 +1,7 @@ """ The endpoints for accessing users for debug mode. """ + # pylint: disable=invalid-name from typing import List diff --git a/llama_store/routers/user_read.py b/llama_store/routers/user_read.py index ef6b8ce..14fa412 100644 --- a/llama_store/routers/user_read.py +++ b/llama_store/routers/user_read.py @@ -1,6 +1,7 @@ """ The endpoints for accessing users. """ + # pylint: disable=invalid-name from typing import Annotated diff --git a/llama_store/routers/user_write.py b/llama_store/routers/user_write.py index ac475a4..cfe2855 100644 --- a/llama_store/routers/user_write.py +++ b/llama_store/routers/user_write.py @@ -1,6 +1,7 @@ """ The endpoints for accessing users. """ + # pylint: disable=invalid-name from fastapi import APIRouter, Depends, HTTPException, status diff --git a/llama_store/tests/test_llama_endpoint.py b/llama_store/tests/test_llama_endpoint.py index 6a7704c..6307b08 100644 --- a/llama_store/tests/test_llama_endpoint.py +++ b/llama_store/tests/test_llama_endpoint.py @@ -2,10 +2,11 @@ Integration tests for the Llama store API. These tests test the /llama endpoint -These tests assume a clean database. Run recreate_database.sh to clean up the database. +These tests assume a clean database. Run recreate-database.sh to clean up the database. They also assume that the User integration tests have been run, so that there is a valid user and API token. """ + import pytest from db_migrations.versions.first import ALL_LLAMAS @@ -90,7 +91,7 @@ def test_create_a_new_llama_with_an_api_key_and_no_name_gives_an_error(self): "loc": ["body", "name"], "msg": "Field required", "input": {"age": 9, "color": "white", "rating": 4}, - "url": "https://errors.pydantic.dev/2.4/v/missing", + "url": "https://errors.pydantic.dev/2.6/v/missing", } ] } @@ -113,7 +114,7 @@ def test_create_a_new_llama_with_an_api_key_and_no_age_gives_an_error(self): "loc": ["body", "age"], "msg": "Field required", "input": {"name": "Llamageddon", "color": "white", "rating": 4}, - "url": "https://errors.pydantic.dev/2.4/v/missing", + "url": "https://errors.pydantic.dev/2.6/v/missing", } ] } @@ -136,7 +137,7 @@ def test_create_a_new_llama_with_an_api_key_and_no_color_gives_an_error(self): "loc": ["body", "color"], "msg": "Field required", "input": {"name": "Llamageddon", "age": 9, "rating": 4}, - "url": "https://errors.pydantic.dev/2.4/v/missing", + "url": "https://errors.pydantic.dev/2.6/v/missing", } ] } @@ -159,7 +160,7 @@ def test_create_a_new_llama_with_an_api_key_and_no_rating_gives_an_error(self): "loc": ["body", "rating"], "msg": "Field required", "input": {"name": "Llamageddon", "age": 9, "color": "white"}, - "url": "https://errors.pydantic.dev/2.4/v/missing", + "url": "https://errors.pydantic.dev/2.6/v/missing", } ] } diff --git a/llama_store/tests/test_llama_picture_endpoint.py b/llama_store/tests/test_llama_picture_endpoint.py index 8373629..77b257b 100644 --- a/llama_store/tests/test_llama_picture_endpoint.py +++ b/llama_store/tests/test_llama_picture_endpoint.py @@ -2,10 +2,11 @@ Integration tests for the Llama store API. These tests test the /llama/{llama_id}/picture endpoint -These tests assume a clean database. Run recreate_database.sh to clean up the database. +These tests assume a clean database. Run recreate-database.sh to clean up the database. They also assume that the User integration tests have been run, so that there is a valid user and API token. """ + from io import BytesIO import pytest diff --git a/llama_store/tests/test_readonly_llama_endpoint.py b/llama_store/tests/test_readonly_llama_endpoint.py index 642d7c4..4654dcb 100644 --- a/llama_store/tests/test_readonly_llama_endpoint.py +++ b/llama_store/tests/test_readonly_llama_endpoint.py @@ -2,8 +2,9 @@ Integration tests for the Llama store API. These tests test /llama endpoint is read only if the environment variable ALLOW_WRITE is not set -These tests assume a clean database. Run recreate_database.sh to clean up the database +These tests assume a clean database. Run recreate-database.sh to clean up the database """ + import pytest diff --git a/llama_store/tests/test_readonly_llama_picture_endpoint.py b/llama_store/tests/test_readonly_llama_picture_endpoint.py index 261a95b..29cf70c 100644 --- a/llama_store/tests/test_readonly_llama_picture_endpoint.py +++ b/llama_store/tests/test_readonly_llama_picture_endpoint.py @@ -2,8 +2,9 @@ Integration tests for the Llama store API. These tests test /llama/{llama_id}/picture endpoint is read only if the environment variable ALLOW_WRITE is not set -These tests assume a clean database. Run recreate_database.sh to clean up the database +These tests assume a clean database. Run recreate-database.sh to clean up the database """ + import pytest diff --git a/llama_store/tests/test_setup_write.py b/llama_store/tests/test_setup_write.py index f6f8d4f..1b57fe2 100644 --- a/llama_store/tests/test_setup_write.py +++ b/llama_store/tests/test_setup_write.py @@ -33,7 +33,7 @@ def setup_class(cls): Sets up the tests by recreating the database and creating a test client """ # Rebuild the initial database - os.system("bash ../scripts/recreate_database.sh") + os.system("bash ../scripts/recreate-database.sh") # Enable write mode os.environ["ALLOW_WRITE"] = "true" diff --git a/llama_store/tests/test_user_endpoint.py b/llama_store/tests/test_user_endpoint.py index ef8866d..d32c531 100644 --- a/llama_store/tests/test_user_endpoint.py +++ b/llama_store/tests/test_user_endpoint.py @@ -2,8 +2,9 @@ Integration tests for the Llama store API. These tests test the /user and /token endpoints -These tests assume a clean database. Run recreate_database.sh to clean up the database +These tests assume a clean database. Run recreate-database.sh to clean up the database """ + import pytest @@ -100,7 +101,7 @@ def test_create_user_with_an_invalid_email_address_gives_a_validaton_error(self) "msg": "String should match pattern " "'.+\\@.+\\..+'", "input": "test_userexample.com", "ctx": {"pattern": ".+\\@.+\\..+"}, - "url": "https://errors.pydantic.dev/2.4/v/string_pattern_mismatch", + "url": "https://errors.pydantic.dev/2.6/v/string_pattern_mismatch", } ] } @@ -122,7 +123,7 @@ def test_create_user_with_an_invalid_password_gives_a_validaton_error(self): "and contain at least one letter, one number, and one special character", "input": "Password123", "ctx": {"error": {}}, - "url": "https://errors.pydantic.dev/2.4/v/assertion_error", + "url": "https://errors.pydantic.dev/2.6/v/assertion_error", } ] } diff --git a/read-only-spec.json b/read-only-spec.json index 1eb3870..5a1ff5e 100644 --- a/read-only-spec.json +++ b/read-only-spec.json @@ -7,11 +7,11 @@ "name": "liblab", "url": "https://liblab.com/" }, - "version": "0.0.1" + "version": "0.1.7" }, "servers": [ { - "url": "http://localhost:8000", + "url": "http://localhost:8080", "description": "Prod" } ], diff --git a/read-only-spec.yaml b/read-only-spec.yaml index c6bd37d..8d08848 100644 --- a/read-only-spec.yaml +++ b/read-only-spec.yaml @@ -19,9 +19,9 @@ info: contact: name: liblab url: https://liblab.com/ - version: 0.0.1 + version: 0.1.7 servers: -- url: http://localhost:8000 +- url: http://localhost:8080 description: Prod paths: /llama/{llama_id}/picture: diff --git a/requirements.txt b/requirements.txt index a5d1caa..0663263 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ fastapi==0.103.1 httpx==0.25.0 passlib[bcrypt]==1.7.4 pillow==10.0.1 +pydantic==2.6.3 pylint==2.17.5 pytest==7.4.2 pytest-order==1.1.0 diff --git a/scripts/check_specs.sh b/scripts/check-specs.sh similarity index 100% rename from scripts/check_specs.sh rename to scripts/check-specs.sh diff --git a/scripts/create_specs.sh b/scripts/create-specs.sh similarity index 100% rename from scripts/create_specs.sh rename to scripts/create-specs.sh diff --git a/scripts/post_create.sh b/scripts/post-create.sh similarity index 60% rename from scripts/post_create.sh rename to scripts/post-create.sh index 1a5c709..8387445 100755 --- a/scripts/post_create.sh +++ b/scripts/post-create.sh @@ -1,4 +1,4 @@ pip install --upgrade pip pip3 install -r requirements.txt -/workspaces/llama-store/scripts/recreate_database.sh +/workspaces/llama-store/scripts/recreate-database.sh npm install -g liblab diff --git a/scripts/recreate_database.sh b/scripts/recreate-database.sh similarity index 100% rename from scripts/recreate_database.sh rename to scripts/recreate-database.sh diff --git a/scripts/start_llama_store.sh b/scripts/start-llama-store.sh similarity index 50% rename from scripts/start_llama_store.sh rename to scripts/start-llama-store.sh index 869bf5b..7ca4456 100755 --- a/scripts/start_llama_store.sh +++ b/scripts/start-llama-store.sh @@ -1,3 +1,3 @@ -/workspaces/llama-store/scripts/recreate_database.sh +/workspaces/llama-store/scripts/recreate-database.sh cd /workspaces/llama-store/llama_store python main.py \ No newline at end of file diff --git a/sdk-examples/python/llamapoleon-bonaparte.png b/sdk-examples/create-pics/llamapoleon-bonaparte.png similarity index 100% rename from sdk-examples/python/llamapoleon-bonaparte.png rename to sdk-examples/create-pics/llamapoleon-bonaparte.png diff --git a/sdk-examples/go/create-llama.go b/sdk-examples/go/create-llama.go new file mode 100644 index 0000000..8f71279 --- /dev/null +++ b/sdk-examples/go/create-llama.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/liblaber/llama-store-sdk-go/pkg/llama" + "github.com/liblaber/llama-store-sdk-go/pkg/llamastore" + "github.com/liblaber/llama-store-sdk-go/pkg/llamastoreconfig" + "github.com/liblaber/llama-store-sdk-go/pkg/token" + "github.com/liblaber/llama-store-sdk-go/pkg/user" +) + +func main() { + config := llamastoreconfig.NewConfig() + config.SetBaseUrl("http://localhost:8080") + + llamaStore := llamastore.NewLlamaStore(config) + + // Create a new user + userRegistration := user.UserRegistration{} + userRegistration.SetEmail("noone@example.com") + userRegistration.SetPassword("Password123!") + + // Register the user + _, err := llamaStore.User.RegisterUser(userRegistration) + + // Check if the user was created - a 400 status code means the user already exists, so do nothing + if err != nil { + // check for 400 in the error message + if strings.Contains(err.Error(), "400") { + fmt.Println("User already exists") + } else { + panic(err) + } + } + + // Create an access token request + tokenRequest := token.ApiTokenRequest{} + tokenRequest.SetEmail(*userRegistration.Email) + tokenRequest.SetPassword(*userRegistration.Password) + + // // Get the access token + tokenResponse, err := llamaStore.Token.CreateApiToken(tokenRequest) + + // Check if the access token was created + if err != nil { + fmt.Println("Failed to get access token") + panic(err) + } + + // Set the access token on the llama store + llamaStore.SetAccessToken(*tokenResponse.Data.AccessToken) + + // Create a new llama + LlamaCreate := llama.LlamaCreate{} + LlamaCreate.SetName("Llamapoleon Bonaparte") + LlamaCreate.SetAge(5) + LlamaCreate.SetColor(llama.LLAMA_COLOR_WHITE) + LlamaCreate.SetRating(4) + + // Create the llama + newLlama, err := llamaStore.Llama.CreateLlama(LlamaCreate) + + // Check if the llama was created + if err != nil { + fmt.Println("Failed to create llama") + panic(err) + } + + fmt.Println("Llama created: ", *newLlama.Data.Name, " with ID: ", *newLlama.Data.Id) +} diff --git a/sdk-examples/go/get-llamas.go b/sdk-examples/go/get-llamas.go new file mode 100644 index 0000000..ecfec0d --- /dev/null +++ b/sdk-examples/go/get-llamas.go @@ -0,0 +1,68 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/liblaber/llama-store-sdk-go/pkg/llamastore" + "github.com/liblaber/llama-store-sdk-go/pkg/llamastoreconfig" + "github.com/liblaber/llama-store-sdk-go/pkg/token" + "github.com/liblaber/llama-store-sdk-go/pkg/user" +) + +func main() { + config := llamastoreconfig.NewConfig() + config.SetBaseUrl("http://localhost:8080") + + llamaStore := llamastore.NewLlamaStore(config) + + // Create a new user + userRegistration := user.UserRegistration{} + userRegistration.SetEmail("noone@example.com") + userRegistration.SetPassword("Password123!") + + // Register the user + _, err := llamaStore.User.RegisterUser(userRegistration) + + // Check if the user was created - a 400 status code means the user already exists, so do nothing + if err != nil { + // check for 400 in the error message + if strings.Contains(err.Error(), "400") { + fmt.Println("User already exists") + } else { + panic(err) + } + } + + // Create an access token request + tokenRequest := token.ApiTokenRequest{} + tokenRequest.SetEmail(*userRegistration.Email) + tokenRequest.SetPassword(*userRegistration.Password) + + // // Get the access token + tokenResponse, err := llamaStore.Token.CreateApiToken(tokenRequest) + + // Check if the access token was created + if err != nil { + fmt.Println("Failed to get access token") + panic(err) + } + + // Set the access token on the llama store + llamaStore.SetAccessToken(*tokenResponse.Data.AccessToken) + + // Get the llamas + llamas, err := llamaStore.Llama.GetLlamas() + + // Check if the llamas were retrieved + if err != nil { + fmt.Println("Failed to get llamas") + panic(err) + } + + // Print the llama names + fmt.Println("Llama names:") + for _, llama := range llamas.Data { + fmt.Println(*llama.Name) + } +} diff --git a/sdk-examples/python/create_llama.py b/sdk-examples/python/create-llama.py similarity index 81% rename from sdk-examples/python/create_llama.py rename to sdk-examples/python/create-llama.py index 8548ecd..957c594 100644 --- a/sdk-examples/python/create_llama.py +++ b/sdk-examples/python/create-llama.py @@ -18,9 +18,9 @@ from http_exceptions.client_exceptions import BadRequestException from llamastore import Llamastore -from llamastore.services.user import User as UserService, UserRegistrationModel -from llamastore.services.token import Token as TokenService, ApiTokenRequestModel -from llamastore.services.llama import Llama as LlamaService, LlamaCreateModel +from llamastore.services.user import User as UserService, RegisterUserRequestModel +from llamastore.services.token import Token as TokenService, CreateApiTokenRequestModel +from llamastore.services.llama import Llama as LlamaService, CreateLlamaRequestModel from llamastore.services.llama_picture import LlamaPicture as LlamaPictureService # Create an instance of the llama store SDK @@ -31,7 +31,7 @@ user_service: UserService = llama_store.user # Create the registration object -user_registration = UserRegistrationModel(email="noone@example.com", password="Password123!") +user_registration = RegisterUserRequestModel(email="noone@example.com", password="Password123!") user = None # Try to register the user. If the user already exists, a 400 will be thrown @@ -49,7 +49,7 @@ token_service: TokenService = llama_store.token # Create the token request using the same credentials as the user registration -token_request = ApiTokenRequestModel(email=user_registration.email, password=user_registration.password) +token_request = CreateApiTokenRequestModel(email=user_registration.email, password=user_registration.password) # Create the token token = token_service.create_api_token(token_request) @@ -64,7 +64,7 @@ llama_picture_service: LlamaPictureService = llama_store.llama_picture # Define the create llama request -new_llama_request: LlamaCreateModel = LlamaCreateModel( +new_llama_request = CreateLlamaRequestModel( name="Llamapoleon Bonaparte", age=5, color="white", @@ -76,7 +76,7 @@ # Upload the llama picture # Open the llama picture -with open("llamapoleon-bonaparte.png", "rb") as f: +with open("../create-pics/llamapoleon-bonaparte.png", "rb") as f: llama_picture = f.read() # Upload the picture diff --git a/sdk-examples/python/get_llamas.py b/sdk-examples/python/get-llamas.py similarity index 87% rename from sdk-examples/python/get_llamas.py rename to sdk-examples/python/get-llamas.py index e8a19fe..0bd0802 100644 --- a/sdk-examples/python/get_llamas.py +++ b/sdk-examples/python/get-llamas.py @@ -15,8 +15,8 @@ from http_exceptions.client_exceptions import BadRequestException from llamastore import Llamastore -from llamastore.services.user import User as UserService, UserRegistrationModel -from llamastore.services.token import Token as TokenService, ApiTokenRequestModel +from llamastore.services.user import User as UserService, RegisterUserRequestModel +from llamastore.services.token import Token as TokenService, CreateApiTokenRequestModel from llamastore.services.llama import Llama as LlamaService, GetLlamasResponseModel from llamastore.services.llama_picture import LlamaPicture as LlamaPictureService @@ -28,7 +28,7 @@ user_service: UserService = llama_store.user # Create the registration object -user_registration = UserRegistrationModel(email="noone@example.com", password="Password123!") +user_registration = RegisterUserRequestModel(email="noone@example.com", password="Password123!") user = None # Try to register the user. If the user already exists, a 400 will be thrown @@ -46,7 +46,7 @@ token_service: TokenService = llama_store.token # Create the token request using the same credentials as the user registration -token_request = ApiTokenRequestModel(email=user_registration.email, password=user_registration.password) +token_request = CreateApiTokenRequestModel(email=user_registration.email, password=user_registration.password) # Create the token token = token_service.create_api_token(token_request) diff --git a/sdk-examples/python/setup-python.sh b/sdk-examples/python/setup-python.sh index 969a3c1..2d1fdc5 100755 --- a/sdk-examples/python/setup-python.sh +++ b/sdk-examples/python/setup-python.sh @@ -1,5 +1,6 @@ rm -rf /workspaces/llama-store/sdk-examples/python/pics cd /workspaces/llama-store/output/python +pip install -r requirements.txt pip install build python -m build --outdir dist . -pip install dist/llamastore-0.0.1-py3-none-any.whl +pip install dist/llamastore-0.0.3-py3-none-any.whl --upgrade --no-deps --force-reinstall diff --git a/sdk-examples/rest/create_llama_rest.py b/sdk-examples/rest/create_llama_rest.py index d5283b5..7b9f067 100644 --- a/sdk-examples/rest/create_llama_rest.py +++ b/sdk-examples/rest/create_llama_rest.py @@ -10,7 +10,7 @@ import requests # The URL of the llama store API -BASE_URL = "http://localhost:8000/" +BASE_URL = "http://localhost:8080/" # Build the new user as a JSON object new_user = { @@ -55,7 +55,7 @@ # Upload the llama picture # Open the llama picture -with open("llamapoleon-bonaparte.png", "rb") as f: +with open("../create-pics/llamapoleon-bonaparte.png", "rb") as f: llama_picture = f.read() # Upload the picture diff --git a/sdk-examples/rest/get_llamas_rest.py b/sdk-examples/rest/get_llamas_rest.py index 11409ab..5720779 100644 --- a/sdk-examples/rest/get_llamas_rest.py +++ b/sdk-examples/rest/get_llamas_rest.py @@ -10,7 +10,7 @@ import requests # The URL of the llama store API -BASE_URL = "http://localhost:8000/" +BASE_URL = "http://localhost:8080/" # Build the new user as a JSON object new_user = { @@ -62,7 +62,7 @@ llama_id = llama["id"] # Download the image - llama_picture_response = requests.get(BASE_URL + f"llama/{llama_id}/picture", timeout=5) + llama_picture_response = requests.get(BASE_URL + f"llama/{llama_id}/picture", timeout=5, headers=headers) # Save the image with open(f"./pics/{llama['name']}.png", "wb") as f: diff --git a/sdk-examples/typescript/README.md b/sdk-examples/typescript/README.md index f1ffacc..de38707 100644 --- a/sdk-examples/typescript/README.md +++ b/sdk-examples/typescript/README.md @@ -1,4 +1,5 @@ -# llamastore-example +# Llama Store TypeScript SDK Example + A basic example of how to use the llamastore package. ## Installation diff --git a/sdk-examples/typescript/package-lock.json b/sdk-examples/typescript/package-lock.json index 795b3c4..10c5319 100644 --- a/sdk-examples/typescript/package-lock.json +++ b/sdk-examples/typescript/package-lock.json @@ -43,13 +43,13 @@ }, "../../output/typescript": { "name": "llamastore", - "version": "0.0.1", + "version": "0.0.3", "license": "MIT", "dependencies": { - "axios": "^1.5.0" + "axios": "^1.0.0" }, "devDependencies": { - "@types/jest": "^27.4.1", + "@types/jest": "^29.5.6", "@types/node": "^17.0.23", "@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/parser": "^5.43.0", @@ -59,12 +59,12 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^4.2.1", - "jest": "^27.5.1", + "jest": "^29.7.0", "nock": "^13.2.4", "prettier": "^2.6.2", - "ts-jest": "^27.0.3", + "ts-jest": "^29.1.1", "tsup": "^6.7.0", - "typescript": "4.8.4" + "typescript": "^4.6.0" } }, "node_modules/@cspotcode/source-map-support": { diff --git a/sdk-examples/typescript/package.json b/sdk-examples/typescript/package.json index fb9e893..ce8e0bc 100644 --- a/sdk-examples/typescript/package.json +++ b/sdk-examples/typescript/package.json @@ -7,7 +7,6 @@ }, "scripts": { "setup": "npm --prefix ../../output/typescript install && npm --prefix ../../output/typescript run build && npm install", - "start": "tsc && node -r dotenv/config dist/index.js", "get-llamas": "tsc && node -r dotenv/config dist/get_llamas.js", "create-llama": "tsc && node -r dotenv/config dist/create_llama.js", "dev": "ts-node src/index.ts" diff --git a/sdk-examples/typescript/src/create_llama.ts b/sdk-examples/typescript/src/create_llama.ts index 8d85d54..0bd0973 100644 --- a/sdk-examples/typescript/src/create_llama.ts +++ b/sdk-examples/typescript/src/create_llama.ts @@ -13,7 +13,7 @@ // This script will upload a llama picture called llamapoleon-bonaparte.png. This file needs to // be in the same directory as this script. -import { Llamastore, LlamaCreate, UserRegistration, ApiTokenRequest } from 'llamastore'; +import { Llamastore, CreateLlamaRequest, RegisterUserRequest, CreateApiTokenRequest } from 'llamastore'; var fs = require('fs/promises'); (async () => { @@ -26,7 +26,7 @@ var fs = require('fs/promises'); const userService = llamaStore.user; // Create the registration object - const userRegistration: UserRegistration = { email: 'noone@example.com', password: 'Password123!' }; + const userRegistration: RegisterUserRequest = { email: 'noone@example.com', password: 'Password123!' }; let user: any = null; // Try to register the user. If the user already exists, a 400 will be thrown @@ -42,7 +42,7 @@ var fs = require('fs/promises'); const tokenService = llamaStore.token; // Create the token request using the same credentials as the user registration - const tokenRequest: ApiTokenRequest = { email: userRegistration.email, password: userRegistration.password }; + const tokenRequest: CreateApiTokenRequest = { email: userRegistration.email, password: userRegistration.password }; // Create the token const token = await tokenService.createApiToken(tokenRequest); @@ -51,7 +51,7 @@ var fs = require('fs/promises'); llamaStore.setAccessToken(token.access_token); // Define the create llama request - const newLlamaRequest: LlamaCreate = { + const newLlamaRequest: CreateLlamaRequest = { "name": "Llamapoleon Bonaparte", "age": 5, "color": "white", diff --git a/sdk-examples/typescript/src/get_llamas.ts b/sdk-examples/typescript/src/get_llamas.ts index 5a06f9b..70ad125 100644 --- a/sdk-examples/typescript/src/get_llamas.ts +++ b/sdk-examples/typescript/src/get_llamas.ts @@ -12,8 +12,7 @@ // - Run this script using npm run get-llamas */ -import { Llamastore, ApiTokenRequest, UserRegistration, GetLlamasResponse } from 'llamastore'; -var fs = require('fs/promises'); +import { Llamastore, CreateApiTokenRequest, RegisterUserRequest, GetLlamasResponse } from 'llamastore'; (async () => { @@ -25,7 +24,7 @@ var fs = require('fs/promises'); const userService = llamaStore.user; // Create the registration object - const userRegistration: UserRegistration = { email: 'noone@example.com', password: 'Password123!' }; + const userRegistration: RegisterUserRequest = { email: 'noone@example.com', password: 'Password123!' }; let user: any = null; // Try to register the user. If the user already exists, a 400 will be thrown @@ -41,7 +40,7 @@ var fs = require('fs/promises'); const tokenService = llamaStore.token; // Create the token request using the same credentials as the user registration - const tokenRequest: ApiTokenRequest = { email: userRegistration.email, password: userRegistration.password }; + const tokenRequest: CreateApiTokenRequest = { email: userRegistration.email, password: userRegistration.password }; // Create the token const token = await tokenService.createApiToken(tokenRequest); diff --git a/sdk-examples/typescript/src/index.ts b/sdk-examples/typescript/src/index.ts deleted file mode 100644 index 7d560ff..0000000 --- a/sdk-examples/typescript/src/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Llamastore } from 'llamastore'; - -const sdk = new Llamastore({ accessToken: process.env.LLAMASTORE_ACCESS_TOKEN }); - -(async () => { - const result = await sdk.llama.getLlamas(); - console.log(result); -})(); diff --git a/spec.json b/spec.json index 1eb3870..5a1ff5e 100644 --- a/spec.json +++ b/spec.json @@ -7,11 +7,11 @@ "name": "liblab", "url": "https://liblab.com/" }, - "version": "0.0.1" + "version": "0.1.7" }, "servers": [ { - "url": "http://localhost:8000", + "url": "http://localhost:8080", "description": "Prod" } ], diff --git a/spec.yaml b/spec.yaml index c6bd37d..8d08848 100644 --- a/spec.yaml +++ b/spec.yaml @@ -19,9 +19,9 @@ info: contact: name: liblab url: https://liblab.com/ - version: 0.0.1 + version: 0.1.7 servers: -- url: http://localhost:8000 +- url: http://localhost:8080 description: Prod paths: /llama/{llama_id}/picture: