class Client(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required(str) grant_type: str = ff.required(str, validators=[ ff.IsOneOf( (authorization_code, implicit, resource_owner_password_credentials, client_credentials)) ]) response_type: str = ff.optional( str, validators=[ff.IsOneOf(response_type_choices)]) scopes: str = ff.required(str) default_redirect_uri: str = ff.required(str) redirect_uris: List[str] = ff.list_() allowed_response_types: List[str] = ff.list_( validators=[ff.IsOneOf(('code', 'token'))]) def validate_redirect_uri(self, redirect_uri: str): return redirect_uri in self.redirect_uris def validate_response_type(self, response_type: str): return response_type in self.allowed_response_types def validate_scopes(self, scopes: List[str]): for scope in scopes: if scope not in self.scopes: return False return True
class Project(ff.AggregateRoot): id: str = ff.id_(is_uuid=False) services: List[Service] = ff.list_() def plan_deployment(self, deployment: ff.Deployment, config: ff.Configuration): project = config.all.get('project') aws = config.contexts.get('firefly_aws') try: api_gateway_resource = aws.get('api_gateways').get('default') except AttributeError: api_gateway_resource = None for s in deployment.services: id_ = f'{project}-{s.name}' service = self.get_service(id_) or Service(id=id_) function = service.get_lambda() for gateway in s.api_gateways: for endpoint in gateway.endpoints: print(endpoint.route) for topic in s.network_topology.topics: print(f'{topic.name}:') for sub in topic.subscribers: print(f' {sub.name}') def get_service(self, id_: str): for s in self.services: if s.id == id_: return s
class TodoList(ff.AggregateRoot, create_on='iam.UserCreated', delete_on='iam.UserDeleted'): id: str = ff.id_() user: User = ff.required() name: str = ff.optional() tasks: List[Task] = ff.list_() def __post_init__(self): if self.name is None: self.name = f"{self.user.name}'s TODO List" @ff.rest('/task', method='POST') def add_task(self, task: Task) -> ff.EventList: self.tasks.append(task) return 'TaskAdded', task def remove_task(self, task: Task): self.tasks.remove(task) def complete_task(self, task_id: str) -> ff.EventList: for task in self.tasks: if task_id == task.id: task.complete_task() return 'TaskCompleted', task raise Exception(f'Task {task_id} not found in TodoList {self}')
class Contact(ff.AggregateRoot): id: str = ff.id_() sub: str = ff.optional(index=True) email: str = ff.optional(index=True) given_name: str = ff.optional() family_name: str = ff.optional() birthdate: date = ff.optional() deleted_on: datetime = ff.optional()
class Task(ff.Entity): id: str = ff.id_() name: str = ff.required() due_date: datetime = ff.required() complete: bool = ff.optional(default=False) def complete_task(self): self.complete = True def is_overdue(self): return datetime.now() >= self.due_date
class Grant(ff.AggregateRoot): id: str = ff.id_() client_id: str = ff.required(str) user_id: str = ff.required(str) code: str = ff.required(str) redirect_uri: str = ff.required(str) scopes: List[str] = ff.list_() expires: datetime = ff.required(datetime) def validate_redirect_uri(self, redirect_uri: str): return self.redirect_uri == redirect_uri
class Audience(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required() tenant: domain.Tenant = ff.required() campaigns: List[domain.Campaign] = ff.list_() services: list = ff.list_(validators=ff.IsOneOf((MAILCHIMP, ))) meta: dict = ff.dict_() def get_campaign(self, id_: str) -> Optional[domain.Campaign]: for campaign in self.campaigns: if campaign.id == id_: return campaign
class Calendar(ff.AggregateRoot): id: str = ff.id_() events: List[cal.Event] = ff.list_() def add_event(self, event: cal.Event): self.events.append(event) self.dispatch('EventAdded', asdict(event)) def add_reminder(self, event_id: str, reminder: cal.Reminder): for event in self.events: if event.id == event_id: event.reminders.append(reminder) self.dispatch('ReminderAdded', asdict(reminder))
class Role(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required(str) users: List[str] = ff.list_() def assign_role_to_user(self, user_id: str): if user_id not in self.users: self.users.append(user_id) return 'iam.RoleAssigned', {'user_id': user_id, 'role_id': self.id} def remove_role_from_user(self, user_id: str): if user_id in self.users: self.users.remove(user_id) return 'iam.RoleRemoved', {'user_id': user_id, 'role_id': self.id}
class User(ff.AggregateRoot): # OpenID standard fields sub: str = ff.id_(validators=[ff.HasLength(36)]) name: str = ff.optional(str) given_name: str = ff.optional(str) family_name: str = ff.optional(str) middle_name: str = ff.optional(str) nickname: str = ff.optional(str) preferred_username: str = ff.optional(str) profile: str = ff.optional(str) picture: str = ff.optional(str) website: str = ff.optional(str) email: str = ff.optional(str, validators=[ff.IsValidEmail()]) email_verified: bool = ff.optional(bool, default=False) gender: str = ff.optional(str, validators=[ff.IsOneOf(('Male', 'Female'))]) birthdate: date = ff.optional(date) zoneinfo: str = ff.optional(str) locale: str = ff.optional(str) phone_number: str = ff.optional(str) phone_number_verified: bool = ff.optional(bool, default=False) address: Address = ff.optional(Address) updated_at: datetime = ff.now() # Custom fields created_at: datetime = ff.now() deleted_at: datetime = ff.optional(datetime) password_hash: str = ff.optional(str, length=32) salt: str = ff.hidden() # __pragma__('skip') @classmethod def create(cls, **kwargs): if 'email' in kwargs: kwargs['email'] = str(kwargs['email']).lower() try: kwargs['salt'] = bcrypt.gensalt() kwargs['password_hash'] = User._hash_password(kwargs['password'], kwargs['salt']) except KeyError: raise ff.MissingArgument('password is a required field for User::create()') return cls(**ff.build_argument_list(kwargs, cls)) @classmethod def _hash_password(cls, password: str, salt: str): return bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8') def correct_password(self, password: str): return self.password_hash == User._hash_password(password, self.salt)
class Group(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required(str) users: List[str] = ff.list_() roles: List[str] = ff.list_() def assign_user_to_group(self, user_id: str): if user_id not in self.users: self.users.append(user_id) return 'iam.GroupAssigned', {'user_id': user_id, 'group_id': self.id} def remove_user_from_group(self, user_id: str): if user_id in self.users: self.users.remove(user_id) return 'iam.GroupRemoved', {'user_id': user_id, 'group_id': self.id}
class Campaign(ff.Entity): id: str = ff.id_() name: str = ff.required() members: List[domain.AudienceMember] = ff.list_() def get_member_by_contact_id(self, contact_id: str): for member in self.members: if member.contact.id == contact_id: return member def add_contact(self, contact: domain.Contact, **kwargs): if self.get_member_by_contact_id(contact.id) is None: kwargs.update({'contact': contact}) self.members.append( domain.AudienceMember( **ff.build_argument_list(kwargs, domain.AudienceMember)))
class Stack(ff.Entity): id: str = ff.id_() resources: List[AWSObject] = ff.list_() parameters: List[BaseAWSObject] = ff.list_() def __post_init__(self): for t in ['resources', 'parameters']: converted = [] for resource in getattr(self, t): if isinstance(resource, dict) and 'module' in resource: module = importlib.import_module(resource['module']) type_ = module.get(resource['type']) converted.append(type_.from_dict(resource['data'])) setattr(self, t, converted) def to_dict(self): ret = { 'id': self.id, } for t in ['resources', 'parameters']: ret[t] = [{ 'module': r.__class__.__module__, 'type': r.__class__.__name__, 'data': r.to_dict(), } for r in getattr(self, t)] return ret def num_resources(self): return len(self.resources) + len(self.parameters) def has_resource(self, title: str): for resource in self.resources: if resource.title == title: return True return False def get_resource(self, title: str): for resource in self.resources: if resource.title == title: return resource
class User(ff.Entity): id: str = ff.id_() name: str = ff.required()
class AudienceMember(ff.AggregateRoot): id: str = ff.id_() audience: str = ff.required() contact: str = ff.required() tags: List[str] = ff.list_() meta: dict = ff.dict_()
class Tenant(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required()
class Event(ff.Entity): id: str = ff.id_() name: str = ff.required() reminders: List[cal.Reminder] = ff.list_()
class ParentWidget(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required() child: ChildWidget = ff.optional()
class User(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required() email: str = ff.required(index=True) roles: List[Role] = ff.list_() special_role: Role = ff.optional()
class Reminder(ff.Entity): id: str = ff.id_() event: cal.Event = ff.required()
class Scope(ff.AggregateRoot): id: str = ff.id_(is_uuid=False) def __str__(self): return self.id
class Widget(ff.AggregateRoot): id: str = ff.id_() name: str = ff.optional() value: int = ff.optional()
class Command(ff.ValueObject): id: str = ff.id_() name: str = ff.required(index=True) params: dict = ff.dict_()
class Role(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required(length=255, index=True) scopes: List[Scope] = ff.list_()
class RuleSet(ff.AggregateRoot): id: str = ff.id_() name: str = ff.optional(index=True) rules: List[Rule] = ff.required()
class User(ff.AggregateRoot): id: str = ff.id_() name: str = ff.required() email: str = ff.required()