Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/chore.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Chore & Docs Workflow

on:
pull_request:
types:
- closed
branches:
- main

jobs:
merge-to-develop:
name: Merge to develop
if: >
(startsWith(github.event.pull_request.head.ref, 'chore/') ||
startsWith(github.event.pull_request.head.ref, 'docs/'))
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Merge Source Branch into Develop
run: |
git config --global user.name "bot"
git config --global user.email "bot@colorifix.com"
git fetch
git checkout develop
git merge --no-ff origin/${{ github.event.pull_request.head.ref }} -m "Merge ${{ github.event.pull_request.head.ref }} into develop"
git push origin develop

- name: Build docs
if: startsWith(github.event.pull_request.head.ref, 'docs/')
run: foo

publish-docs:
name: Publish Documentation
runs-on: ubuntu-latest
if: startsWith(github.event.pull_request.head.ref, 'docs/')
permissions:
id-token: write
pages: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install Poetry
uses: abatilo/actions-poetry@v2
with:
poetry-version: 'latest'

- name: Install dev dependencies
run: poetry install --only dev --no-root

- name: Build docs
run: poetry run mkdocs build

- name: Upload docs as artifacts
uses: actions/upload-pages-artifact@v3
with:
path: site

- name: Deploy pages
uses: actions/deploy-pages@v4
62 changes: 62 additions & 0 deletions .github/workflows/pull.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Pull workflow

on:
pull_request:
branches:
- 'develop'

jobs:
run-tests:
if: true
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}

- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install Poetry
uses: abatilo/actions-poetry@v2
with:
poetry-version: 'latest'

- name: Setup local venv
run: |
poetry config virtualenvs.create true --local
poetry config virtualenvs.in-project true --local
poetry run python --version

- name: Restore dependencies
id: restore-dependencies
uses: actions/cache/restore@v4
with:
path: ./.venv
key: venv-${{ hashFiles('poetry.lock') }}

- name: Install dependencies
if: steps.restore-dependencies.outputs.cache-hit != 'true'
run: poetry install -vvv

- name: Cache dependencies
uses: actions/cache/save@v4
with:
path: ./.venv
key: ${{ steps.restore-dependencies.outputs.cache-primary-key }}

- name: Install project
run: poetry install --only-root

- name: Run tests
run: poetry run pytest --junitxml=junit/test-results.xml

- name: Publish test report
uses: mikepenz/action-junit-report@v5
if: always()
with:
report_paths: 'junit/test-results.xml'
76 changes: 76 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Release workflow

on:
workflow_dispatch:
inputs:
version:
type: string
required: true
description: 'Version number to release in X.Y.Z format'
dry_run:
type: boolean
default: true
description: 'Dry run'
pull_request:
types:
- closed
branches:
- 'main'

jobs:
release_workflow:
runs-on: ubuntu-latest
if: >
(github.event_name == 'workflow_dispatch') ||
(github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.head.ref, 'release/'))
steps:
- name: Gitflow action
id: gitflow-action
uses: hoangvvo/gitflow-workflow-action@0.3.7
with:
develop_branch: "develop"
main_branch: "main"
version: ${{ inputs.version }}
version_increment: ${{ contains(github.head_ref, 'hotfix/') && 'patch' || '' }}
dry_run: ${{ inputs.dry_run }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ steps.gitflow-action.outputs.release_branch || 'main' }}

- name: Install Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install Poetry
uses: abatilo/actions-poetry@v2
with:
poetry-version: 'latest'

# Bumping version if we are in the 'create release PR mode'
- name: Bump version
if: ${{ steps.gitflow-action.outputs.release_branch }}
env:
VERSION: ${{ steps.gitflow-action.outputs.version }}
run: poetry version $VERSION

# Committing bumped version to the release branch
- name: Commit new version
if: ${{ steps.gitflow-action.outputs.release_branch }}
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "Bump version to ${{ steps.gitflow-action.outputs.version }}"

# Building and publishing if we are in 'created new release mode'
- name: Build and publish package
if: ${{ !steps.gitflow-action.outputs.release_branch }}
env:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: |
poetry config pypi-token.pypi $PYPI_TOKEN
poetry publish --build
11 changes: 1 addition & 10 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,3 @@
**/.DS_Store
**/*.pyc
local_config.json
examples/pickle_temp_store/store
.config
demo
colorifix_alpha/sa_cloud_storage.json
colorifix_alpha/.config
tds_template.pdf
*config.json
neo4j.yml
temp
public
123 changes: 8 additions & 115 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,120 +1,13 @@
# Welcome to Constelite

## Definitions

### StateModel

Model is just a class that is derived from `constelite.StateModel`, which is just a fancy `pydantic.BaseModel`.

### Store

Store is a wrapper around a third-party API or a database. All stores share a standard interface, allowing to `put`, `patch`, `get`, `query` and `delete` state models.

### StoreRecord

Represents a record of an entity in the store. It contains inofrmation about the store and has a unique identifier.

### Ref

Represents a referene to a store record of an entity. Think of it as a pointer to a state model stored in a remote store.

### Protocol

A function wrapped in `@protocol` that convers one or more models into another model.

Can alos be implemented as a `Protocol` class with a `run()` method.


## How to

### Define a new state model

Constelite uses `pydantic`. All models in constelite should be defined from `constelite.StateModel` base class.


```python
from typing import Optional
from constelite.models import Dynamic, StateModel


class Message(StateModel):
message: str
likes: Optional[Dynamic[int]]
```

### Add a protocol
[![docs](https://img.shields.io/badge/docs-blue)](https://colorifix.github.io/constelite/) ![PyPI - Version](https://img.shields.io/pypi/v/constelite)

Protocols can be defined as function wrapped in a `@protocol` or a class derived from `Protocol`.

**Functional protocols**

* Must use type hints.
* Must use references (not state models) as input arguments and state models as returns.
* Must have an `api` argument that will be linked to the instance of the API.

```python
from typing import List, Any

from constelite.models import Ref
from constelite.protocol import protocol

from model import Message


@protocol(name='Combine messages')
def combine_messages(messages: List[Ref[Message]], api: Any) -> Message:
return Message(
message=''.join([api.get_state(message).message for message in messages]),
)
```

**Class protocols**

* Must have a `run()` method defined.
* The `run()` method must use type hints.
* The `run()` must use references (not state models) as input arguments and state models as returns.
* The `api` instance can be accessed through `self.api`

```python

from typing import List
from constelite.models import Ref
from constelite.protocol import Protocol

from model import Message


class SumTotalLikes(Protocol):
_name = "Sum total likes"

messages: List[Ref[Message]]

def run(self) -> int:
total = 0
for r_message in self.messages:
message = self.api.get_state(r_message)
points = message.likes.points
if len(points) > 0:
total += points[-1].value
return total
```

### Start an API server

* Create an instance of the API.
* Discover protocols using `api.discover_protocols()`
* Launch using `api.run()` method
* See `example/api`


### Call remote functions
# Welcome to Constelite

Easy, just create an instance of `StarliteClient` and use it to do the reemote calls. Make sure you have a server running first.
<img src="./docs/img/logo.png" alt="Logo" width="100"/>

```python
from constelite.api import StarliteClient
Constelite is a framework for dealing with data exchange between external/internal data providers.

Problems Constelite attempts to solve:

if __name__ == '__main__':
client = StarliteClient(url='http://127.0.0.1:8083')
```
* Standardising data models across various data providers.
* Decoupling logic of data processing from making read/write operations to data providers.
* Creation of various APIs to manipulate and process data.
2 changes: 1 addition & 1 deletion constelite/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from constelite.utils import (
get_method_name, all_subclasses, resolve_forward_ref
)
from constelite.config import load_config
# from constelite.config import load_config

from constelite.models import StateModel, FlexibleModel, Ref

Expand Down
10 changes: 0 additions & 10 deletions constelite/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,2 @@
from constelite.api.api import ConsteliteAPI
from constelite.api.starlite import StarliteAPI, StarliteClient
from constelite.api.camunda import CamundaAPI
from constelite.api.redis import RedisAPI

__all__ = [
'ConsteliteAPI',
'CamundaAPI',
'StarliteClient',
'StarliteAPI',
'RedisAPI'
]
12 changes: 1 addition & 11 deletions constelite/api/starlite/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1 @@
from constelite.api.starlite.api import StarliteAPI
from constelite.api.starlite.client import StarliteClient

__all__ = [
'PutRequest',
'PatchRequest',
'GetRequest',
'DeleteRequest',
'StarliteAPI',
'StarliteClient',
]
from constelite.api.starlite.api import StarliteAPI
Loading