Skip to content

Commit

Permalink
add some more stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnnsrs committed Mar 8, 2024
1 parent 5f91c53 commit bad1fd5
Show file tree
Hide file tree
Showing 31 changed files with 777 additions and 131 deletions.
5 changes: 5 additions & 0 deletions config_backend/compositions/default.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
name: generic
services:
lok: lok_next
rekuest: rekuest_next
fluss: fluss_next
4 changes: 4 additions & 0 deletions config_backend/instances/fluss_next.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
endpoint_url: "{{"https" if request.is_secure else "http" }}://{{"fluss" if request.host == "lok" else request.host + ":11090"}}/graphql"
healthz: "{{"https" if request.is_secure else "http" }}://{{"fluss" if request.host == "lok" else request.host + ":11090"}}/ht"
ws_endpoint_url: "{{"wss" if request.is_secure else "ws" }}://{{"fluss" if request.host == "lok" else request.host + ":11090"}}/graphql"
__service: "live.arkitekt.fluss"
15 changes: 15 additions & 0 deletions config_backend/instances/lok_next.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
base_url: "{{"https" if request.is_secure else "http" }}://{{"lok" if request.host == "lok" else request.host + ":12000"}}/o"
userinfo_url: "{{"https" if request.is_secure else "http" }}://{{"lok" if request.host == "lok" else request.host + ":12000"}}/o/userinfo"
endpoint_url: "{{"https" if request.is_secure else "http" }}://{{"lok" if request.host == "lok" else request.host + ":12000"}}/graphql"
healthz: "{{"https" if request.is_secure else "http" }}://{{"lok" if request.host == "lok" else request.host + ":12000"}}/ht"
secure: false
ws_endpoint_url: "{{"wss" if request.is_secure else "ws" }}://{{"lok" if request.host == "lok" else request.host + ":12000"}}/graphql"
client_id: "{{client.client_id}}"
client_secret: "{{client.client_secret}}"
grant_type: "{{client.authorization_grant_type}}"
name: "{{client.name}}"
scopes:
{% for item in manifest.scopes %}
- {{item}}
{% endfor %}
__service: "live.arkitekt.lok"
6 changes: 6 additions & 0 deletions config_backend/instances/rekuest_next.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
agent:
endpoint_url: "{{"wss" if request.is_secure else "ws" }}://{{"rekuest" if request.host == "lok" else request.host + ":11020"}}/agi"
endpoint_url: "{{"https" if request.is_secure else "http" }}://{{"rekuest" if request.host == "lok" else request.host + ":11020"}}/graphql"
healthz: "{{"https" if request.is_secure else "http" }}://{{"rekuest" if request.host == "lok" else request.host + ":11020"}}/ht"
ws_endpoint_url: "{{"wss" if request.is_secure else "ws" }}://{{"rekuest" if request.host == "lok" else request.host + ":11020"}}/graphql"
__service: "live.arkitekt.rekuest-next"
2 changes: 2 additions & 0 deletions config_backend/services/live.arkitekt.fluss.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
key: fluss
description: "The Next generation fluss"
2 changes: 2 additions & 0 deletions config_backend/services/live.arkitekt.lok.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
key: lok
description: "The Next generation Lok"
2 changes: 2 additions & 0 deletions config_backend/services/live.arkitekt.rekuest-next.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
key: rekuest_next
description: "The best service to have ever lived"
3 changes: 3 additions & 0 deletions contrib/config_backend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .backend import ConfigBackend

__all__ = ["ConfigBackend"]
164 changes: 164 additions & 0 deletions contrib/config_backend/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
from fakts.backends.backend_registry import BackendBase, InstanceDescriptor, CompositionDescriptor, InstanceMap, ServiceDescriptor
from fakts.base_models import LinkingContext, LinkingRequest, Manifest, LinkingClient
from typing import Dict, Any
from pathlib import Path
import yaml
from jinja2 import Template, TemplateSyntaxError, TemplateError


class ConfigBackend(BackendBase):
"""
This class is used to store the configuration of the server. It is
responsible for reading and writing the configuration to the file system.
"""

def __init__(self, config: dict) -> None:
self.config = config
self.service_dir = Path(self.config.get("SERVICE_DIR", "/workspace/config_backend/services"))
self.instance_dr = Path(self.config.get("INSTANCE_DIR", "/workspace/config_backend/instances"))
self.composition_dir = Path(self.config.get("COMPOSITION_DIR", "/workspace/config_backend/compositions"))

assert self.service_dir.exists(), f"Service directory {self.service_dir} does not exist"
assert self.composition_dir.exists(), f"Composition directory {self.composition_dir} does not exist"


self.loaded_services = self._load_services()
print(self.loaded_services)

self.loaded_instances = self._load_instances()
print(self.loaded_instances)

self.loaded_compositions = self._load_compositions()
print(self.loaded_compositions)


def _load_instances(self) -> dict[str, InstanceDescriptor]:
instances = {}

for instance_file in self.instance_dr.iterdir():
if instance_file.is_file():
instances[instance_file.stem] = self.retrieve_instance(instance_file)

return instances

def _load_services(self) -> dict[str, ServiceDescriptor]:
instances = {}

for service_file in self.service_dir.iterdir():
if service_file.is_file():
instances[service_file.stem] = self.retrieve_serve(service_file)

return instances

def _load_compositions(self) -> dict[str, CompositionDescriptor]:

compositions = {}

for composition_file in self.composition_dir.iterdir():
if composition_file.is_file():
compositions[composition_file.stem] = self.retrieve_composition(composition_file)

return compositions


def retrieve_composition(self, composition_file: Path) -> CompositionDescriptor:
with open(composition_file, "r") as file:
context = yaml.load(file.read(), Loader=yaml.SafeLoader)

assert context.get("name"), f"Composition file {composition_file} does not contain a name"
assert context.get("services"), f"Composition file {composition_file} does not contain any services"

service_dict = {}

for service_name, instance_name in context.get("services").items():
assert self.loaded_instances.get(instance_name), f"Composition file {composition_file} contains an unknown service instance {instance_name}"
service_dict[service_name] = InstanceMap(instance_identifier=instance_name, backend_identifier=self.get_name())

return CompositionDescriptor(
name=context.get("name"),
services=service_dict
)

def retrieve_serve(self, service_file: Path) -> ServiceDescriptor:
with open(service_file, "r") as file:
context = yaml.load(file.read(), Loader=yaml.SafeLoader)

assert context.get("key"), f"Service file {service_file} does not contain a key"


return ServiceDescriptor(
identifier=service_file.stem,
key=context.get("key"),
logo=context.get("logo"),
description=context.get("description"),
)



def retrieve_instance(self, service_file: Path) -> InstanceDescriptor:
with open(service_file, "r") as file:
template = file.read()


fake_context = LinkingContext(
request=LinkingRequest(
host="example.com",
port="443",
is_secure=True,
),
manifest=Manifest(
identifier="com.example.app",
version="1.0",
scopes=["scope1", "scope2"],
redirect_uris=["https://example.com"],
),
client=LinkingClient(
client_id="@client_id",
client_secret="@client_secret",
client_type="@client_type",
authorization_grant_type="authorization_grant_type",
name="@name",
),
)


result = yaml.load(Template(template).render(fake_context), Loader=yaml.SafeLoader)

assert result.get("__service"), f"Service file {service_file} does not contain an service identifier (\"__service\")"

return InstanceDescriptor(
backend_identifier=self.get_name(),
instance_identifier=service_file.stem,
service_identifier=result.get("__service"),
)




def render(self, instance_id: str, linking: LinkingContext) -> Dict[str, Any]:
with open(self.instance_dr / f"{instance_id}.yaml", "r") as file:
template = file.read()

answer = yaml.load(Template(template).render(linking.dict()), Loader=yaml.SafeLoader)
print(answer)
return answer




@classmethod
def get_name(cls) -> str:
return cls.__name__


def get_service_descriptors(self) -> list[ServiceDescriptor]:
return self.loaded_services.values()

def get_instance_descriptors(self) -> list[InstanceDescriptor]:
return self.loaded_instances.values()

def get_composition_descriptors(self) -> list[CompositionDescriptor]:
return self.loaded_compositions.values()



65 changes: 65 additions & 0 deletions contrib/config_backend/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from django.core.exceptions import ValidationError
from fakts.base_models import LinkingContext, LinkingRequest, Manifest, LinkingClient
from jinja2 import Template, TemplateSyntaxError, TemplateError, StrictUndefined
import yaml

def is_valid_jinja2_template(template_string, render_context=None):
""" Checks if a template string is a valid Jinja2 template. And if it is,
it checks if it is a valid YAML file, wher rendered with a fakt context If it is not, it raises a ValueError"""
try:
template = Template(template_string, undefined=StrictUndefined)
try:
rendered_template = template.render(render_context )
yaml.safe_load(rendered_template)
except (TemplateError, yaml.YAMLError) as e:
raise ValueError(f"Rendering error: {e}") from e
except TemplateSyntaxError as e:
raise ValueError(f"Template syntax error: {e}") from e



def jinja2_yaml_template_validator(value):
""" Validates that a string is a valid Jinja2 template. And if it is,
it checks if it is a valid YAML file, wher rendered with a fakt context If it is not, it raises a ValidationError
"""


fake_context = LinkingContext(
request=LinkingRequest(
host="example.com",
port="443",
is_secure=True,
),
manifest=Manifest(
identifier="com.example.app",
version="1.0",
scopes=["scope1", "scope2"],
redirect_uris=["https://example.com"],
),
client=LinkingClient(
client_id="@client_id",
client_secret="@client_secret",
client_type="@client_type",
authorization_grant_type="authorization_grant_type",
name="@name",
),
)


try:
is_valid_jinja2_template(value, fake_context.dict())
except ValidationError as e:
raise ValueError(e)

return value


def fake_load_yaml(value):
""" Loads a string as a YAML file. If it is not a valid YAML file, it raises a ValidationError"""



try:
return yaml.safe_load(value)
except yaml.YAMLError as e:
raise ValidationError(f"YAML error: {e}") from e
29 changes: 29 additions & 0 deletions contrib/docker_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from fakts.backends.backend_registry import BackendBase, InstanceDescriptor, CompositionDescriptor, ServiceDescriptor
from typing import Dict, Any
from fakts.base_models import LinkingContext


class DockerBackend(BackendBase):
"""
This class is used to store the configuration of the server. It is
responsible for reading and writing the configuration to the file system.
"""

def __init__(self, config_file):
self.config_file = config_file

@classmethod
def get_name(cls) -> str:
return cls.__name__

def render(cls, service_instance: str, context: LinkingContext) -> Dict[str, Any]:
pass

def get_instance_descriptors(cls) -> list[InstanceDescriptor]:
return []

def get_composition_descriptors(self) -> list[CompositionDescriptor]:
return []

def get_service_descriptors(self) -> list[ServiceDescriptor]:
return []
2 changes: 2 additions & 0 deletions fakts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
admin.site.register(App)
admin.site.register(Release)
admin.site.register(Composition)
admin.site.register(Service)
admin.site.register(ServiceInstance)
admin.site.register(Client)
admin.site.register(DeviceCode)
Empty file added fakts/backends/__init__.py
Empty file.
Loading

0 comments on commit bad1fd5

Please sign in to comment.