Skip to content

Commit

Permalink
fixed dark-them, add userdefined services (configs)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnnsrs committed Dec 9, 2024
1 parent ac0f6f5 commit 4b75445
Show file tree
Hide file tree
Showing 21 changed files with 371 additions and 41 deletions.
9 changes: 9 additions & 0 deletions fakts/backends/backend_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ class BackendRegistry:
backends: dict[str, Backend] = {}

def __init__(self) -> None:
self.with_user_defined = True



if not hasattr(settings, "FAKTS_BACKENDS"):
self.backend_configs = []
else:
Expand All @@ -107,11 +111,16 @@ def __init__(self) -> None:

self.register(backend)



pass

def get_backend_identifiers(self):
names = []

if self.with_user_defined:
names.append(settings.USER_DEFINED_BACKEND_NAME)

for i in self.backends.values():
names.append(i.get_name())

Expand Down
11 changes: 11 additions & 0 deletions fakts/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ class ClientKindVanilla(str, Enum):
DESKTOP = "desktop"



@strawberry.enum
class FaktValueType(str, Enum):
STRING = "string"
NUMBER = "number"
BOOLEAN = "boolean"





@strawberry.enum
class ClientKind(str, Enum):
DEVELOPMENT = strawberry.enum_value("development", description="A development client. Development clients are clients that receive a client_id and client_secret, and are always linked to a user, that grants rights when creating the application. There is no active user authentication when the app gets started. They are used for development purposes.")
Expand Down
36 changes: 35 additions & 1 deletion fakts/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,38 @@ def filter_ids(self, queryset, info):
def filter_search(self, queryset, info):
if self.search is None:
return queryset
return queryset.filter(name__contains=self.search)
return queryset.filter(name__contains=self.search)


@strawberry_django.filter(fakts_models.Service)
class ServiceFilter:
search: str | None
ids: list[strawberry.ID] | None

def filter_ids(self, queryset, info):
if self.ids is None:
return queryset
return queryset.filter(id__in=self.ids)


def filter_search(self, queryset, info):
if self.search is None:
return queryset
return queryset.filter(name__contains=self.search)


@strawberry_django.filter(fakts_models.ServiceInstance)
class ServiceInstanceFilter:
search: str | None
ids: list[strawberry.ID] | None

def filter_ids(self, queryset, info):
if self.ids is None:
return queryset
return queryset.filter(id__in=self.ids)


def filter_search(self, queryset, info):
if self.search is None:
return queryset
return queryset.filter(backend__contains=self.search)
3 changes: 2 additions & 1 deletion fakts/graphql/mutations/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .client import *
from .scan import *
from .render import *
from .render import *
from .service import *
55 changes: 55 additions & 0 deletions fakts/graphql/mutations/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import hashlib
import json
import logging
import uuid

import namegenerator
import strawberry
import strawberry_django
from ekke.types import Info

from fakts import enums, inputs, models, scalars, types
from fakts.base_models import DevelopmentClientConfig, Manifest, Requirement
from fakts.builders import create_client
from fakts.models import Composition
from django.conf import settings

logger = logging.getLogger(__name__)




def create_user_defined_service_instance(info: Info, input: inputs.UserDefinedServiceInstanceInput) -> types.UserDefinedServiceInstance:

service, _ = models.Service.objects.get_or_create(
identifier=input.identifier,
defaults=dict(
name=input.identifier,
description=" No description",
),
)


instance, _ = models.ServiceInstance.objects.get_or_create(
service=service,
backend=settings.USER_DEFINED_BACKEND_NAME,
identifier=service.identifier,
defaults=dict(
template="None"
),
)

user_defined, _ = models.UserDefinedServiceInstance.objects.update_or_create(
instance=instance,
creator=info.context.request.user,
defaults=dict(
values=[strawberry.asdict(x) for x in input.values],
),
)



return user_defined



28 changes: 27 additions & 1 deletion fakts/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Optional
from pydantic import BaseModel, Field
import uuid
from fakts import enums



Expand Down Expand Up @@ -83,4 +84,29 @@ class RenderInput:
client: strawberry.ID
composition: strawberry.ID | None = None
request: LinkingRequestInput | None = None
manifest: ManifestInput | None = None
manifest: ManifestInput | None = None



class KeyValueInputModel(BaseModel):
key: str
value: str
as_type: enums.FaktValueType


class UserDefinedServiceInstanceInputModel(BaseModel):
identifier: str
values: list[KeyValueInputModel] = Field(default_factory=list)

@pydantic.input(KeyValueInputModel)
class KeyValueInput:
key: str
value: str
as_type: enums.FaktValueType



@pydantic.input(UserDefinedServiceInstanceInputModel)
class UserDefinedServiceInstanceInput:
identifier: str
values: list[KeyValueInput] = strawberry.field(default_factory=list)
64 changes: 52 additions & 12 deletions fakts/logic.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from fakts import base_models
from fakts import models
from fakts import models, inputs, enums
import re
from jinja2 import Template, TemplateSyntaxError, TemplateError
from string import Template
import yaml
from pydantic import BaseModel, Field
import re
Expand All @@ -12,6 +12,41 @@
from fakts.backends.instances import registry as backend_registry
from fakts.base_models import Manifest
from hashlib import sha256
from django.conf import settings


def render_user_defined(instance: models.ServiceInstanceMapping, context: base_models.LinkingContext) -> dict:

user_defined = models.UserDefinedServiceInstance.objects.filter(
instance=instance
).first()


if user_defined is None:
raise errors.ConfigurationError(f"No user defined instance found for {instance}")


values = {}

for value in user_defined.values:
x = inputs.KeyValueInput(**value)


value = Template(x.value).safe_substitute(context.dict())
if x.as_type == enums.FaktValueType.STRING:
values[x.key] = value
elif x.as_type == enums.FaktValueType.INT:
values[x.key] = int(value)
elif x.as_type == enums.FaktValueType.FLOAT:
values[x.key] = float(value)
elif x.as_type == enums.FaktValueType.BOOL:
values[x.key] = bool(value)



return values




def render_composition(composition: models.Composition, context: base_models.LinkingContext) -> dict:
Expand All @@ -27,20 +62,25 @@ def render_composition(composition: models.Composition, context: base_models.Lin
instance = mapping.instance


if instance.backend not in backend_registry.backends:
raise errors.BackendNotAvailable(f"The backend {instance.backend} for this instance is not available")
if instance.backend == settings.USER_DEFINED_BACKEND_NAME:
value = render_user_defined(instance, context)

backend = backend_registry.backends[instance.backend]
config_dict[mapping.key] = value
else:
if instance.backend not in backend_registry.backends:
raise errors.BackendNotAvailable(f"The backend {instance.backend} for this instance is not available")

try:
value = backend.render(instance.identifier, context)
except Exception as e:
raise errors.BackendError(f"An error occurred while rendering the backend instance {instance}: {str(e)}") from e
backend = backend_registry.backends[instance.backend]

try:
value = backend.render(instance.identifier, context)
except Exception as e:
raise errors.BackendError(f"An error occurred while rendering the backend instance {instance}: {str(e)}") from e

if not isinstance(value, dict):
raise errors.ConfigurationError(f"The backend {instance.backend} for this instance did not return a dictionary")
if not isinstance(value, dict):
raise errors.ConfigurationError(f"The backend {instance.backend} for this instance did not return a dictionary")

config_dict[mapping.key] = value
config_dict[mapping.key] = value

return config_dict

Expand Down
46 changes: 46 additions & 0 deletions fakts/migrations/0010_userdefinedserviceinstance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 4.2.5 on 2024-12-09 10:28

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("fakts", "0009_client_redirect_uris"),
]

operations = [
migrations.CreateModel(
name="UserDefinedServiceInstance",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("values", models.JSONField(default=dict)),
(
"creator",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="services",
to=settings.AUTH_USER_MODEL,
),
),
(
"instance",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="user_defined",
to="fakts.serviceinstance",
),
),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.5 on 2024-12-09 10:32

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("fakts", "0010_userdefinedserviceinstance"),
]

operations = [
migrations.AlterField(
model_name="userdefinedserviceinstance",
name="instance",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="defined",
to="fakts.serviceinstance",
),
),
migrations.AlterField(
model_name="userdefinedserviceinstance",
name="values",
field=models.JSONField(default=list),
),
]
22 changes: 22 additions & 0 deletions fakts/migrations/0012_alter_userdefinedserviceinstance_instance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.5 on 2024-12-09 13:28

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
("fakts", "0011_alter_userdefinedserviceinstance_instance_and_more"),
]

operations = [
migrations.AlterField(
model_name="userdefinedserviceinstance",
name="instance",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="user_definitions",
to="fakts.serviceinstance",
),
),
]
13 changes: 13 additions & 0 deletions fakts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ def __str__(self):




class UserDefinedServiceInstance(models.Model):
instance = models.ForeignKey(ServiceInstance, on_delete=models.CASCADE, related_name="user_definitions")
creator = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name="services")
values = models.JSONField(default=list)








class InstanceConfig(models.Model):
instance = models.ForeignKey(ServiceInstance, on_delete=models.CASCADE, related_name="configs")
key = models.CharField(max_length=1000)
Expand Down
Loading

0 comments on commit 4b75445

Please sign in to comment.