Skip to content

Commit

Permalink
update maguire
Browse files Browse the repository at this point in the history
  • Loading branch information
gsvr committed Dec 4, 2024
1 parent bb15b0d commit dadb31b
Show file tree
Hide file tree
Showing 14 changed files with 642 additions and 180 deletions.
64 changes: 29 additions & 35 deletions .github/workflows/gcp_publish.yml
Original file line number Diff line number Diff line change
@@ -1,48 +1,42 @@
name: Build and Deploy to GKE (Google Kubernetes Engine)
# This workflow will build a docker container and publish it to Google Container Registry

name: Production build and publish

on:
push:
branches:
- master

env:
REGISTRY_HOSTNAME: eu.gcr.io
GKE_PROJECT: ${{ secrets.GKE_PROJECT }}
GKE_EMAIL: ${{ secrets.GKE_EMAIL }}
GITHUB_SHA: ${{ github.sha }}
IMAGE: maguire-backend
REGISTRY_HOSTNAME: eu.gcr.io
DEPLOYMENT_NAME: gke-test

jobs:
setup-build-publish:
name: Setup, Build, Publish
backend:
name: build and publish backend
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v2

# Setup gcloud CLI
- uses: google-github-actions/setup-gcloud@v0
with:
version: '270.0.0'
service_account_email: ${{ secrets.GKE_EMAIL }}
service_account_key: ${{ secrets.GKE_KEY }}

# Configure docker to use the gcloud command-line tool as a credential helper
- run: |
# Set up docker to authenticate
# via gcloud command-line tool.
gcloud auth configure-docker
# Build the Docker image
- name: Build Maguire Backend
run: |
docker build -t "$REGISTRY_HOSTNAME"/"$GKE_PROJECT"/"$IMAGE":"$GITHUB_SHA" \
--build-arg GITHUB_SHA="$GITHUB_SHA" \
--build-arg GITHUB_REF="$GITHUB_REF" backend
# Push the Docker image to Google Container Registry
- name: Publish Maguire Backend
run: |
docker push $REGISTRY_HOSTNAME/$GKE_PROJECT/$IMAGE:$GITHUB_SHA
- name: Check out repository
uses: actions/checkout@v4

- id: auth
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GKE_KEY }}

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v2

- name: Setup docker gcloud auth
run: |
# configure docker to authenticate via gcloud command-line tool
gcloud auth configure-docker
- name: Build backend docker image
run: |
docker build -t "$REGISTRY_HOSTNAME"/"$GKE_PROJECT"/maguire-backend:"$GITHUB_SHA" backend
- name: Publish backend docker image
run: |
docker push $REGISTRY_HOSTNAME/$GKE_PROJECT/maguire-backend:$GITHUB_SHA
5 changes: 3 additions & 2 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
FROM praekeltfoundation/django-bootstrap:py3.8-buster
FROM ghcr.io/praekeltfoundation/docker-django-bootstrap-nw:py3.10-bullseye

COPY . /app
RUN pip install -r requirements.txt
RUN pip install -e .

ENV SENTRY_RELEASE=${VCS_HASH}
ENV DJANGO_SETTINGS_MODULE maguire.settings
ENV CELERY_APP maguire

RUN django-admin collectstatic --noinput
CMD ["maguire.wsgi:application", "--threads", "5", "--timeout", "300"]
CMD ["maguire.wsgi:application", "--workers", "2", "--threads", "3", "--timeout", "420", "--max-requests", "1000", "--max-requests-jitter", "100", "--graceful-timeout", "300"]
2 changes: 1 addition & 1 deletion backend/debits/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.dispatch import receiver
from django.db.models.signals import post_save
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _

from maguire.models import AppModel

Expand Down
86 changes: 40 additions & 46 deletions backend/debits/schema.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,57 @@
from django.contrib.contenttypes.models import ContentType
import django_filters
from graphene import relay, AbstractType, String, Field
from graphene import relay, String, Field
from graphene.types.datetime import DateTime
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from django_filters import OrderingFilter

from .models import Debit
from maguire.utils import (uuid_from_b64, schema_get_mutation_data, schema_update_model,
schema_define_user, schema_create_updated_event, TotalCountMixin)
from maguire.utils import (
TotalCountMixin,
get_node_with_permission,
schema_create_updated_event,
schema_define_user,
schema_get_mutation_data,
schema_update_model,
uuid_from_b64,
)


# Graphene will automatically map the Debit model's fields onto the DebitNode.
# This is configured in the DebitNode's Meta class (as you can see below)

DEBIT_FILTERS = {
'branch_code': ['exact', 'icontains', 'istartswith'],
'account_number': ['exact', 'icontains', 'istartswith'],
'account_name': ['exact', 'icontains', 'istartswith'],
'provider': ['exact'],
'client': ['exact'],
'downstream_reference': ['exact', 'icontains', 'istartswith'],
'reference': ['exact', 'icontains', 'istartswith'],
'provider_reference': ['exact', 'icontains', 'istartswith'],
'amount': ['lt', 'gt', 'lte', 'gte', 'exact'],
}

class DebitFilter(django_filters.FilterSet):
# Make CharField with choices filtering case-insensitive str lookup
account_type = django_filters.CharFilter(
field_name='account_type', lookup_expr='iexact')
status = django_filters.CharFilter(
field_name='status', lookup_expr='iexact')

class DebitNode(DjangoObjectType, TotalCountMixin):
class Meta:
model = Debit
# Allow for some more advanced filtering here
filter_fields = DEBIT_FILTERS
interfaces = (relay.Node, )
fields = {
'branch_code': ['exact', 'icontains', 'istartswith'],
'account_number': ['exact', 'icontains', 'istartswith'],
'account_name': ['exact', 'icontains', 'istartswith'],
'provider': ['exact'],
'client': ['exact'],
'downstream_reference': ['exact', 'icontains', 'istartswith'],
'reference': ['exact', 'icontains', 'istartswith'],
'provider_reference': ['exact', 'icontains', 'istartswith'],
'amount': ['lt', 'gt', 'lte', 'gte', 'exact'],
}

@classmethod
def get_node(cls, info, id):
# HTTP request
try:
node = cls._meta.model.objects.get(id=id)
except cls._meta.model.DoesNotExist:
return cls._meta.model.objects.none()
if info.context is not None:
if info.context.user.is_authenticated:
return node
else:
return cls._meta.model.objects.none()
else: # Not a HTTP request - no permissions testing currently
return node
order_by = OrderingFilter(fields=['created_at', 'scheduled_at', 'loaded_at'])


class DebitFilter(django_filters.FilterSet):
class DebitNode(DjangoObjectType, TotalCountMixin):

class Meta:
model = Debit
fields = DEBIT_FILTERS
filterset_class = DebitFilter
interfaces = (relay.Node, )

order_by = OrderingFilter(fields=['created_at', 'scheduled_at', 'loaded_at'])
@classmethod
def get_node(cls, info, id):
return get_node_with_permission(cls, id, info.context)


class DebitMutation(relay.ClientIDMutation):
Expand Down Expand Up @@ -113,20 +108,19 @@ def mutate_and_get_payload(cls, root, info, **input):
return DebitMutation(debit=debit)


class Query(AbstractType):
class Query(object):
debit = relay.Node.Field(DebitNode)
debits = DjangoFilterConnectionField(DebitNode, filterset_class=DebitFilter)
debits = DjangoFilterConnectionField(DebitNode)

def resolve_debits(self, args, context, info):
# context will reference to the Django request
if context is not None:
if context.user.is_authenticated:
def resolve_debits(self, info, **args):
if info.context is not None:
if info.context.user.is_authenticated:
return DebitFilter(args, queryset=Debit.objects.all()).qs
else:
return Debit.objects.none()
else: # Not a HTTP request - no permissions testing currently
return DebitFilter(args, queryset=Debit.objects.all()).qs


class Mutation(AbstractType):
class Mutation(object):
debit_mutate = DebitMutation.Field()
2 changes: 1 addition & 1 deletion backend/events/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.utils.timezone import now
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
Expand Down
71 changes: 25 additions & 46 deletions backend/events/schema.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,40 @@
import django_filters
from graphql import GraphQLError
from graphene import relay, AbstractType, String, Field, Int
from graphene import relay, String, Field, Int
from graphene.types.datetime import DateTime
from graphene.types.json import JSONString
from graphene_django import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField
from django_filters import OrderingFilter
from rolepermissions.verifications import has_object_permission, has_permission
from rolepermissions.verifications import has_permission

from .models import Event

from maguire.utils import (schema_get_mutation_data)
from maguire.utils import get_node_with_permission, schema_get_mutation_data


# Graphene will automatically map the Event model's fields onto the EventNode.
# This is configured in the EventNode's Meta class (as you can see below)

EVENT_FILTERS = {
'source_model': ['exact'],
'source_id': ['exact'],
'event_type': ['exact'],
}

class EventFilter(django_filters.FilterSet):

class EventNode(DjangoObjectType):
class Meta:
model = Event
# Allow for some more advanced filtering here
filter_fields = EVENT_FILTERS
interfaces = (relay.Node, )

@classmethod
def get_node(cls, id, context, info):
# HTTP request
try:
node = cls._meta.model.objects.get(id=id)
except cls._meta.model.DoesNotExist:
return cls._meta.model.objects.none()
if context is not None:
if context.user.is_authenticated and (
has_object_permission('access_event', context.user, node)):
return node
else:
return cls._meta.model.objects.none()
else: # Not a HTTP request - no permissions testing currently
return node
fields = {
'source_model': ['exact'],
'source_id': ['exact'],
'event_type': ['exact'],
}

order_by = OrderingFilter(fields=['created_at'])

class EventFilter(django_filters.FilterSet):

class EventNode(DjangoObjectType):
class Meta:
model = Event
fields = EVENT_FILTERS
filterset_class = EventFilter
interfaces = (relay.Node, )

order_by = OrderingFilter(fields=['created_at'])
@classmethod
def get_node(cls, info, id):
return get_node_with_permission(cls, id, info.context, 'access_event')


class EventMutation(relay.ClientIDMutation):
Expand Down Expand Up @@ -84,23 +65,21 @@ def mutate_and_get_payload(cls, input, context, info):
return EventMutation(event=event)


class Query(AbstractType):
class Query(object):
event = relay.Node.Field(EventNode)
events = DjangoFilterConnectionField(EventNode,
filterset_class=EventFilter)

def resolve_events(self, args, context, info):
# context will reference to the Django request
if context is not None:
if context.user.is_authenticated and (
has_permission(context.user, 'list_all') or
has_permission(context.user, 'list_events')):
events = DjangoFilterConnectionField(EventNode)

def resolve_events(self, info, **args):
if info.context is not None:
if info.context.user.is_authenticated and (
has_permission(info.context.user, 'list_all') or
has_permission(info.context.user, 'list_events')):
return EventFilter(args, queryset=Event.objects.all()).qs
else:
return Event.objects.none()
else: # Not a HTTP request - no permissions testing currently
return EventFilter(args, queryset=Event.objects.all()).qs


class Mutation(AbstractType):
class Mutation(object):
event_mutate = EventMutation.Field()
4 changes: 1 addition & 3 deletions backend/maguire/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
'raven.contrib.django.raven_compat',
'rest_framework',
'rest_framework.authtoken',
'rest_auth',
'dj_rest_auth',
'django_filters',
'corsheaders',
'reversion',
Expand Down Expand Up @@ -133,8 +133,6 @@

USE_I18N = True

USE_L10N = True

USE_TZ = True


Expand Down
23 changes: 14 additions & 9 deletions backend/maguire/urls.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import os
from django.conf.urls import include, url

from django.conf import settings
from django.conf.urls import include
from django.conf.urls.static import static
from django.contrib import admin
from django.views.decorators.csrf import csrf_exempt
from django.contrib.admin.views.decorators import staff_member_required
from django.urls import re_path
from django.views.decorators.csrf import csrf_exempt

from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import authentication_classes, permission_classes, api_view

from graphene_django.views import GraphQLView

from maguire.schema import schema
from django.conf import settings
from django.conf.urls.static import static

admin.site.site_header = os.environ.get('MAGUIRE_TITLE', 'Maguire Admin')

Expand All @@ -23,10 +28,10 @@ def graphql_token_view():


urlpatterns = [
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', admin.site.urls),
url(r'^graphql', graphql_token_view()),
url(r'^graphiql', staff_member_required(csrf_exempt(
re_path(r'^admin/doc/', include('django.contrib.admindocs.urls')),
re_path(r'^admin/', admin.site.urls),
re_path(r'^graphql', graphql_token_view()),
re_path(r'^graphiql', staff_member_required(csrf_exempt(
GraphQLView.as_view(schema=schema, graphiql=True)))),
url(r'^api/rest-auth/', include('rest_auth.urls')),
re_path(r'^api/rest-auth/', include('dj_rest_auth.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Loading

0 comments on commit dadb31b

Please sign in to comment.