text:
            <form ng-submit="login()">

Log in

{{ msg }}

                    <input type="text" placeholder="Username" required ng-model="formData.username"/>
                    <input type="password" placeholder="Password" required ng-model="formData.password"/>

                        <input type="checkbox" id="remember-me" ng-model="formData.rememberMe"/>
                        Remember me

                        <input type="submit" class="btn btn-primary btn-large" value="Log in"/>

                    Create an account | Forgot your password?

            </form>

            <form ng-submit="signup()">

Sign Up

{{ msg }}

                    <input type="text" placeholder="Username" required ng-class="{'errorField' : errorFields.indexOf('username') > -1}" ng-model="formData.username"/>
                    <input type="email" placeholder="Email" required ng-class="{'errorField' : errorFields.indexOf('email') > -1}" ng-model="formData.email"/>
                    <input type="password" placeholder="Password" ng-class="{'errorField' : errorFields.indexOf('password') > -1}" required ng-model="formData.password"/>
                    <input type="password" placeholder="Password again" ng-class="{'errorField' : errorFields.indexOf('passwordAgain') > -1}" required ng-model="formData.passwordAgain"/>

                        <input type="submit" class="btn btn-primary btn-large" value="Sign Up"/>

                    Back to login

            </form>

            <form ng-submit="passwordRecovery()">

Lost password

{{ msg }}

                    <input type="email" placeholder="Email" required ng-class="{'errorField' : errorFields.indexOf('email') > -1}" ng-model="formData.email"/>

                        <input type="submit" class="btn btn-primary btn-large" value="Recover password"/>

                    Back to login

            </form>

            <form ng-submit="accountSettings()">

Settings

{{ msg }}

                    <input type="email" placeholder="Email" ng-class="{'errorField' : errorFields.indexOf('email') > -1}" ng-model="formData.email"/>
                    <input type="password" placeholder="Old password" ng-class="{'errorField' : errorFields.indexOf('oldPassword') > -1}" ng-model="formData.oldPassword"/>
                    <input type="password" placeholder="New password" ng-class="{'errorField' : errorFields.indexOf('password') > -1}" ng-model="formData.password"/>
                    <input type="password" placeholder="New password again" ng-class="{'errorField' : errorFields.indexOf('passwordAgain') > -1}" ng-model="formData.passwordAgain"/>

                        <input type="submit" class="btn btn-primary btn-large" value="Save"/>

            </form>


text:
Drop images anywhere to insert them

        <news ng-repeat="news in newsCollection track by $index" news-title="news.title" data-created="news.dataCreated" on-close="markNewsAsRead($index)">{{ news.content }}</news>

Share this note

        People who know this URL can see this note, but cannot edit it.

        EditorEditor + PreviewPreview Share

        Menu

            <input type="text" id="note-title" placeholder="Untitled note" tabindex="1" ng-model="notesService.notes.active[currentNoteIndex].title">

                        Create a note

 0">

                        {{notebook.title}}Untitled notebook

                        <input type="text" ng-model="notebook.title" placeholder="Untitled notebook">

                                Delete notebook
                                Notes in the notebook will be not be deleted

                            Drag notes here to add them to this notebook.

                        Trash

                         0">

                            No notes in trash

                                {{note.title}}Untitled note
                                <time class='subtitle' relative datetime="{{ note.date_updated }}"></time>

Account settings

Give feedback

Donate

Log out

                <form action="">
                    <textarea id="textarea" placeholder="Start typing here..." name="input" ui-codemirror="codemirrorOptions" ng-model="notesService.notes.active[currentNoteIndex].content"></textarea>
                </form>

repo_name: nicbou/markdown-notes

repo_language: JavaScript

mime_type: text/plain


text:
{{ title }}

    <time class="date">{{ dateCreated }}</time>


text:
{{message.message}}

            {{ note.title }}


text:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.query import QuerySet
from django.template.loader import render_to_string
from django.db.models.signals import post_save
import random
import base36

class SoftDeletionQuerySet(QuerySet):
    def delete(self):
        # Bulk delete bypasses individual objects' delete methods.
        return super(SoftDeletionQuerySet, self).update(deleted=True)

    def hard_delete(self):
        return super(SoftDeletionQuerySet, self).delete()

    def active(self):
        return self.filter(deleted=False)

    def deleted(self):
        return self.exclude(deleted=False)

class SoftDeletionManager(models.Manager):
    def __init__(self, *args, **kwargs):
        self.active_only = kwargs.pop('active_only', True)
        super(SoftDeletionManager, self).__init__(*args, **kwargs)

    def get_queryset(self):
        if self.active_only:
            return SoftDeletionQuerySet(self.model).filter(deleted=False)
        return SoftDeletionQuerySet(self.model)

    def hard_delete(self):
        return self.get_queryset().hard_delete()

class SoftDeletionModel(models.Model):
    """
    Abstract model for items that can only be
    """
    deleted = models.BooleanField(default=False)

    objects = SoftDeletionManager()
    all_objects = SoftDeletionManager(active_only=False)

    class Meta:
        abstract = True

    def delete(self):
        self.deleted = True
        self.save()

    def hard_delete(self):
        super(SoftDeletionModel, self).delete()

class Notebook(models.Model):
    """
    Notebooks let users group their notes together
    """
    user = models.ForeignKey(User)
    title = models.CharField(max_length=255)

    class Meta:
        ordering = ['title']

def generate_hash():
    return base36.dumps(random.getrandbits(32))

class Note(SoftDeletionModel):
    """
    Model for individual notes.
    """
    user = models.ForeignKey(User)
    title = models.CharField(max_length=255)
    content = models.TextField()
    date_created = models.DateTimeField(auto_now_add=True)
    date_updated = models.DateTimeField(auto_now=True)
    public_id = models.CharField(max_length=32, default=generate_hash)

    notebook = models.ForeignKey(Notebook, null=True, blank=True, on_delete=models.SET_NULL)

    objects = SoftDeletionManager()

    def __unicode__(self):
        return "%s by %s" % (self.title, self.user)

    class Meta:
        ordering = ['-date_updated', 'title']

class DummyNote(Note):
    """
    Public, read-only notes available to all users in the demo
    """
    objects = SoftDeletionManager()

    class Meta:
        ordering = ['-date_updated', 'title']

# Create a 'getting started' note when a user is created
def user_created(sender, **kwargs):
    if kwargs["created"]:
        note = Note(
            user=kwargs["instance"],
            title="Getting started",
            content=render_to_string('notes/getting_started.md'),
        )
        note.save()

post_save.connect(user_created, sender=User)


text:
from django.core.urlresolvers import reverse_lazy
from django.views.generic import TemplateView
from django.views.generic.base import RedirectView

class NotesView(TemplateView):
    template_name = "notes/app.html"

class SharedNoteRedirectView(RedirectView):
    permanent = False
    query_string = False
    pattern_name = 'index'

    def get_redirect_url(self, *args, **kwargs):
        return reverse_lazy('index') + '#/' + kwargs.get('public_id')


text:
from django.contrib import admin
from notes.models import Note, Notebook

class NoteAdmin(admin.ModelAdmin):
    list_display = (
        'user',
        'title',
        'date_created',
        'date_updated',
        'deleted',
    )

    def get_queryset(self, request):
        return Note.all_objects.all()

class NotebookAdmin(admin.ModelAdmin):
    list_display = (
        'user',
        'title',
    )

admin.site.register(Note, NoteAdmin)
admin.site.register(Notebook, NotebookAdmin)


text:
class AngularCSRFRename(object):
    """
    Lets CSRF play nicely with angular
    """
    ANGULAR_HEADER_NAME = 'HTTP_X_XSRF_TOKEN'
    def process_request(self, request):
        if self.ANGULAR_HEADER_NAME in request.META:
            request.META['HTTP_X_CSRFTOKEN'] = request.META[self.ANGULAR_HEADER_NAME]
            del request.META[self.ANGULAR_HEADER_NAME]
        return None


text:
from django.conf.urls import url
from django.core.exceptions import ObjectDoesNotExist
from notes.models import Note, DummyNote, Notebook
from tastypie import fields
from tastypie.authentication import ApiKeyAuthentication
from tastypie.authorization import Authorization
from tastypie.exceptions import Unauthorized, NotFound
from tastypie.resources import ModelResource
import datetime

class UserNotesAuthorization(Authorization):
    """
    Only allows a user to modify his own notes
    """
    def read_list(self, object_list, bundle):
        # This assumes a ``QuerySet`` from ``ModelResource``.
        return object_list.filter(user=bundle.request.user)

    def read_detail(self, object_list, bundle):
        # Is the requested object owned by the user?
        return bundle.obj.user == bundle.request.user

    def create_list(self, object_list, bundle):
        # Assuming they are auto-assigned to ``user``.
        return object_list

    def create_detail(self, object_list, bundle):
        return bundle.obj.user == bundle.request.user

    def update_list(self, object_list, bundle):
        allowed = []

        # Since they may not all be saved, iterate over them.
        for obj in object_list:
            if obj.user == bundle.request.user:
                allowed.append(obj)

        return allowed

    def update_detail(self, object_list, bundle):
        return bundle.obj.user == bundle.request.user

    def delete_list(self, object_list, bundle):
        allowed = []

        # Since they may not all be saved, iterate over them.
        for obj in object_list:
            if obj.user == bundle.request.user:
                allowed.append(obj)

        return allowed

    def delete_detail(self, object_list, bundle):
        return bundle.obj.user == bundle.request.user

class NotebookResource(ModelResource):
    class Meta:
        queryset = Notebook.objects.all()
        resource_name = 'notebook'

        list_allowed_methods = ['get', 'post', 'put', 'delete']
        authentication = ApiKeyAuthentication()
        authorization = UserNotesAuthorization()
        always_return_data = True

    def get_object_list(self, request):
        return super(NotebookResource, self).get_object_list(request).filter(user=request.user)

    def obj_create(self, bundle, **kwargs):
        """
        Assign created notebooks to the current user
        """
        return super(NotebookResource, self).obj_create(bundle, user=bundle.request.user, date_created=datetime.datetime.now())

    def apply_authorization_limits(self, request, object_list):
        """
        Return the user's notebooks
        """
        return object_list.filter(user=request.user)

class NoteResource(ModelResource):
    notebook_uri = fields.ForeignKey(NotebookResource, attribute='notebook', null=True, full=False)

    class Meta:
        queryset = Note.all_objects.all()  # Includes soft-deleted notes
        resource_name = 'note'

        list_allowed_methods = ['get', 'post', 'put', 'delete', 'patch']
        authentication = ApiKeyAuthentication()
        authorization = UserNotesAuthorization()  # Same as notes
        always_return_data = True
        filtering = {
            'deleted': 'exact'
        }

    def get_object_list(self, request):
        return super(NoteResource, self).get_object_list(request).filter(user=request.user)

    def obj_create(self, bundle, **kwargs):
        """
        Assign created notes to the current user
        """
        return super(NoteResource, self).obj_create(bundle, user=bundle.request.user, date_created=datetime.datetime.now())

    def obj_delete(self, bundle, **kwargs):
        """
        A rewrite of obj_delete with support for soft deletion. Pass it ?permanent=true
        to delete the object permanently.
        """
        if not hasattr(bundle.obj, 'delete'):
            try:
                bundle.obj = self.obj_get(bundle=bundle, **kwargs)
            except ObjectDoesNotExist:
                raise NotFound("A model instance matching the provided arguments could not be found.")

        self.authorized_delete_detail(self.get_object_list(bundle.request), bundle)

        hard_delete = bundle.request.GET.get('permanent', '').lower() in ['true', '1']

        if hard_delete:
            bundle.obj.hard_delete()
        else:
            bundle.obj.delete()

    def apply_authorization_limits(self, request, object_list):
        """
        Return the user's notes
        """
        return object_list.filter(user=request.user)

class SharedNotesAuthorization(Authorization):
    """
    Only allows a user to modify its own notes
    """
    def read_list(self, object_list, bundle):
        raise Unauthorized()

    def read_detail(self, object_list, bundle):
        return True

    def create_list(self, object_list, bundle):
        raise Unauthorized()

    def create_detail(self, object_list, bundle):
        raise Unauthorized()

    def update_list(self, object_list, bundle):
        raise Unauthorized()

    def update_detail(self, object_list, bundle):
        raise Unauthorized()

    def delete_list(self, object_list, bundle):
        raise Unauthorized()

    def delete_detail(self, object_list, bundle):
        raise Unauthorized()

class SharedNoteResource(ModelResource):
    """
    Shared notes are accessible by all, but use a public hash instead of an id
    """
    class Meta:
        queryset = Note.objects.all()
        resource_name = 'shared-note'
        list_allowed_methods = ['get', ]
        authorization = SharedNotesAuthorization()
        detail_uri_name = 'public_id'

    def prepend_urls(self):
        return [
            url(r"^(?P<resource_name>%s)/(?P<public_id>[a-zA-Z0-9]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
        ]

class DummyNoteResource(ModelResource):
    """
    Dummy notes API. Returns a preset list of notes, doesn't obey save requests
    """
    class Meta:
        queryset = DummyNote.objects.all()  # Excludes soft-deleted notes
        resource_name = 'note-dummy'

        list_allowed_methods = ['get', ]


text:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

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

class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Note',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('deleted', models.BooleanField(default=False)),
                ('title', models.CharField(max_length=255)),
                ('content', models.TextField()),
                ('date_created', models.DateTimeField(auto_now_add=True)),
                ('date_updated', models.DateTimeField(auto_now=True)),
            ],
            options={
                'ordering': ['-date_updated', 'title'],
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='DummyNote',
            fields=[
                ('note_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='notes.Note')),
            ],
            options={
                'ordering': ['-date_updated', 'title'],
            },
            bases=('notes.note',),
        ),
        migrations.AddField(
            model_name='note',
            name='user',
            field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
            preserve_default=True,
        ),
    ]


text:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

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

class Migration(migrations.Migration):

    dependencies = [
        ('notes', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='note',
            name='public_id',
            field=models.CharField(default=lambda:notes.models.generate_hash(), max_length=32),
        ),
    ]


text:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

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

class Migration(migrations.Migration):

    dependencies = [
        ('notes', '0002_note_public_id'),
    ]

    operations = [
        migrations.AlterField(
            model_name='note',
            name='public_id',
            field=models.CharField(default=notes.models.generate_hash, max_length=32),
        ),
    ]


text:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

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

class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('notes', '0004_auto_20151022_1517'),
    ]

    operations = [
        migrations.CreateModel(
            name='Notebook',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('title', models.CharField(max_length=255)),
                ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
            ],
            options={
                'ordering': ['title'],
            },
            bases=(models.Model,),
        ),
        migrations.AddField(
            model_name='note',
            name='notebook',
            field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to='notes.Notebook', null=True),
            preserve_default=True,
        ),
    ]


text:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models
from notes.models import generate_hash

def update_public_ids(apps, schema_editor):
    # Update from a 32 character integer string to a 8 character base 36 string
    Note = apps.get_model("notes", "Note")
    for note in Note.objects.all():
        note.public_id = generate_hash();
        note.save()

class Migration(migrations.Migration):

    dependencies = [
        ('notes', '0003_auto_20151019_1552'),
    ]

    operations = [
    	migrations.RunPython(update_public_ids),
    ]


text:
from cStringIO import StringIO
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import models
from notes.models import Note
from PIL import Image as PILImage
import os
import urllib2 as urllib

class Image(models.Model):
    note = models.ForeignKey(Note)
    image = models.ImageField(max_length=255, upload_to='/')

    def save(self, *args, **kwargs):
        max_size = 900  # Don't exceed 900x900

        if not self.id and not self.image:
            return

        super(Image, self).save(*args, **kwargs)

        # Open the original photo
        img_file = urllib.urlopen(self.image.url)
        im = StringIO(img_file.read())
        img = PILImage.open(im)

        # Resize the image if necessary
        width = img.size[0]
        height = img.size[1]
        ratio = 1.0
        if width > height and width > max_size:
            ratio = float(max_size) / float(width)
        elif height > width and height > max_size:
            ratio = float(max_size) / float(height)

        new_size = (int(width * ratio), int(height * ratio))
        if new_size != img.size:
            # Convert to RGB if needed
            if img.mode not in ('L', 'RGB'):
                img = img.convert('RGB')

            # Resize
            img.thumbnail(new_size, PILImage.ANTIALIAS)

            # Save the image
            temp_handle = StringIO()
            img.save(temp_handle, 'jpeg')
            temp_handle.seek(0)

            # Update the model's ImageField
            suf = SimpleUploadedFile(os.path.split(self.image.name)[-1].split('.')[0], temp_handle.read(), content_type='image/jpeg')
            self.image.save('%s.jpg' % suf.name, suf, save=True)


text:
from models import Image
from forms import ImageUploadForm
from django.http import HttpResponse, HttpResponseForbidden
from django.views.generic.base import RedirectView
from django.conf import settings

class S3RedirectView(RedirectView):
    """
    Legacy function. For a time, we piped markdownnotes.com/i/ to
    the Amazon S3 URL. We now use a custom subdomain (i.markdownnotes.com)
    for that. This is just to avoid breaking current image links.
    """
    permanent = True

    def get_redirect_url(self, *args, **kwargs):
        return settings.MEDIA_URL + kwargs['filename']

def upload_image(request):
    """
    Handles image uploads and assigns them to the correct user. Resizes the image before uploading.

    The uploaded image's URL is in the HTTP header (Location)
    """
    if request.method == 'POST':
        form = ImageUploadForm(request.POST, request.FILES)
        if form.is_valid():
            img = Image()
            img.user = request.user
            img.note_id = form.cleaned_data['note']
            img.image = form.cleaned_data['image']
            img.save()

            # Build a response
            response = HttpResponse(status=201)
            response['Location'] = img.image.url.replace(settings.AWS_MEDIA_URL,settings.MEDIA_URL,1)
            return response
        else:
            return HttpResponse(status=400)
    return HttpResponseForbidden()


text:
from django.conf.urls import patterns, url

urlpatterns = patterns('',
    url(r'^image/$', 'uploads.views.upload_image', name='upload_image'),
)


text:
# In forms.py...
from django import forms

class ImageUploadForm(forms.Form):
    image = forms.ImageField()
    note = forms.IntegerField()


text:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('notes', '0004_auto_20151022_1517'),
    ]

    operations = [
        migrations.CreateModel(
            name='Image',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('image', models.ImageField(upload_to=b'/')),
                ('note', models.ForeignKey(to='notes.Note')),
            ],
            options={
            },
            bases=(models.Model,),
        ),
    ]


text:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('uploads', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='image',
            name='image',
            field=models.ImageField(max_length=255, upload_to=b'/'),
            preserve_default=True,
        ),
    ]


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Feedback{% endblock %}
{% block description %}Submit feedback about Markdown Notes and help us build the best markdown editor.{% endblock %}
{% block page %}

Give feedback

Markdown Notes is not actively maintained anymore. It will remain online, but no new features will be developed. Nonetheless, Markdown Notes is an open source project, and pull requests are welcome.

If you have questions about Markdown Notes, send me an email at contact@nicolasbouliane.com.

Thank you!

{% endblock %}


text:
{% extends "base.html" %}
{% load staticfiles %}
{% block full_title %}Markdown Notes: fast markdown editor with LaTeX support{% endblock full_title %}
{% block extra_scripts %}

{% endblock extra_scripts %}
{% block home_button %}{% endblock %}
{% block page %}

Students, take note.

Markdown Notes lets you write rich notes with images, LaTeX equations and syntax-highlighted code from your browser.

            {% include "notes/editor.html" %}

Light as a feather

Use Markdown to create beautiful notes without lifting your hands from the keyboard. Your notes are instantly saved, easily printable and accessible from anywhere.

Made for math

Use LaTeX to create complex math equations in a jiffy. It’s finally easy to take math and chemistry notes on a computer. How cool is that?

Camera-ready

Drop images on your notes to upload them. This is the first markdown editor that lets you add images this easily.

Feature-packed

Markdown Notes also has code highlighting, folding sections, a distraction-free mode and many more neat features.

...and hassle-free!

        {% if not user.is_authenticated %}

No ads, no in-app purchases and no spam.
Why not give it a test drive?

            Create my account
        {% else %}
            Go to my notes
        {% endif %}

{% endblock %}


text:
<form class="form-horizontal" action="" method="POST" {% if form.is_multipart %}enctype="multipart/form-data"{% endif %}>
    {% csrf_token %}
    {{ form.media }}
    {% if form.non_field_errors %}

            {{ form.non_field_errors|join:"
" }}

    {% endif %}
    {% for field in form.visible_fields %}

            {{ field.label }}{% if field.field.required %} *{% endif %}

                {{ field }}

    {% endfor %}
    {% for field in form.hidden_fields %}
        {{ field }}
    {% endfor %}

    {% block formsets %}
        {% for formset in inlines %}
            {% include "inline_formset.html" with formset=formset %}
        {% endfor %}
    {% endblock formsets %}

        <input class="btn btn-primary btn-large" type="submit" value="{{ submit_text|default:"Submit" }}"/>
        {% block extra_buttons %}
        {% endblock extra_buttons %}

</form>


text:
{% load i18n %}
<fieldset>
	<legend>{{ formset.label }}</legend>

		{% for form in formset.forms %}

				{% if field.errors %}{{ field.errors.as_text }}{% endif %}
				{% for field in form.visible_fields %}
					{% if not field.name = form.DELETE.name %}
						{{ field.errors }}
						{{ field.label }}
						{{ field }}
					{% endif %}
				{% endfor %}
				{% if form.instance.pk and formset.can_delete %}
					{{ form.DELETE }}
				{% endif %}
				{% for field in form.hidden_fields %}
					{{ field }}
				{% endfor %}
				<input type="hidden" name="{{form.DELETE.html_name}}" id="{{form.DELETE.auto_id}}"/>

		{% endfor %}
		{{ formset.management_form }}

</fieldset>


text:
{% load compress %}
{% load staticfiles %}
<!DOCTYPE html>

    <title>{% block full_title %}{% block title %}Home{% endblock title %} | Markdown Notes: fast markdown editor with LaTeX support{% endblock full_title %}</title>
    <meta name="description" content="{% block description %}A markdown editor with LaTeX support, image uploads, code highlighting and print support.{% endblock %}"/>

    {% compress js %}

    {% endcompress %}
    {% block extra_scripts %}{% endblock extra_scripts %}

    {% compress css %}
        <link rel="stylesheet" type="text/x-scss" href="{% static "css/site.scss" %}">
        <link rel="stylesheet" type="text/x-scss" href="{% static "bower_components/codemirror/lib/codemirror.css" %}">
        <link rel="stylesheet" type="text/x-scss" href="{% static "bower_components/highlightjs/styles/github.css" %}">
        <link rel="stylesheet" type="text/x-scss" href="{% static "css/app.scss" %}">
    {% endcompress %}
    <link href='http://fonts.googleapis.com/css?family=Roboto:100,300,700,400|Roboto+Mono:300,300italic,700' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
    <link rel="icon" type="image/png" href="{% static "img/favicon.png" %}">
    <meta name="viewport" content="width=device-width, height=device-height, user-scalable=no, initial-scale=1.0">
    <meta charset="utf-8" />

    {% block account_buttons %}

            {% spaceless %}
                {% block home_button %}
                    Home

                {% endblock %}
                {% if not user.is_authenticated %}
                    Sign up

                    Log in
                {% else %}
                    My notes

                    Log out
                {% endif %}
            {% endspaceless %}

    {% endblock account_buttons %}
    {% block page %}

    {% endblock page %}


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Password reset{% endblock %}
{% block description %}Reset password done.{% endblock %}
{% block page %}

Password reset successful

We've e-mailed you instructions for setting your password to the e-mail address you submitted.

You should be receiving it shortly.

{% endblock %}


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Password reset{% endblock %}
{% block description %}Reset your password{% endblock %}
{% block page %}

Password reset

        {% if validlink %}

{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}

            {% trans "Reset password" as signup_text %}
            {% include "form.html" with form=form submit_text=signup_text %}
        {% else %}

{% trans "The password reset link was invalid, possibly because it has already been used.  Please request a new password reset." %}

        {% endif %}

{% endblock %}


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Password reset{% endblock %}
{% block description %}Reset your password{% endblock %}
{% block page %}
    {% if not user.is_authenticated %}

Password reset

Please specify your email address to receive instructions for resetting your password.

            {% trans "Reset password" as signup_text %}
            {% include "form.html" with form=form submit_text=signup_text %}

    {% else %}

You are logged in.

            Return to your notes

    {% endif %}
{% endblock %}


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Account settings{% endblock %}
{% block description %}Change your account settings{% endblock %}
{% block page %}
    {% if user.is_authenticated %}

{% trans "Account settings" %}

            {% include "form.html" with submit_text="Save changes" %}

    {% else %}

You must be logged in to see this page.

    {% endif %}
{% endblock %}


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Log in{% endblock %}
{% block description %}Log in to see your notes{% endblock %}
{% block page %}
    {% if not user.is_authenticated %}

Log in

            {% trans "Log in" as signup_text %}
            {% include "form.html" with form=form submit_text=signup_text %}

            Did you forget password?

    {% else %}

You are already logged in.

        Return to your notes
    {% endif %}
{% endblock %}


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Sign up{% endblock %}
{% block description %}Create an account on Markdown Notes to start writing neatly formatted engineering notes.{% endblock %}
{% block page %}
    {% if not user.is_authenticated %}

Sign up

            {% trans "Create my account" as signup_text %}
            {% include "form.html" with form=form submit_text=signup_text %}

    {% else %}

You are already logged in.

        Return to your notes
    {% endif %}
{% endblock %}


text:
{% extends "base.html" %}
{% load i18n %}
{% block title %}Password reset complete{% endblock %}
{% block description %}Password reset complete{% endblock %}
{% block page %}

Password reset complete

{% trans "Your password has been set.  You may go ahead and log in now." %}

{% endblock %}


text:
{% extends "base.html" %}
{% load staticfiles %}
{% block title %}Notes{% endblock %}
{% block extra_scripts %}
    <script src="{% static "js/app-config.js" %}"></script>
{% endblock extra_scripts %}
{% block account_buttons %}{% endblock %}
{% block page %}
    {% include "notes/editor.html" with class='app' %}
{% endblock page %}


text: {% csrf_token %}


text:
#Hello there!

Welcome to **Markdown Notes**! This guide will show you some of the tricks Markdown Notes has up its sleeves.

This note is editable, so feel free play with it.

##1. A quick markdown primer

Markdown is a simple formatting language used by Stack Overflow, Github and many other sites. Markdown Notes uses [Github-flavored markdown](https://help.github.com/articles/github-flavored-markdown).

Since markdown is a standard, the notes you create here will work just fine in other markdown editors.

###1.1. Basic formatting

* **Bold**, *italics* and ~~strikethrough~~ text is easy to create
* [Links](http://markdownnotes.com) and ![images](http://img4me.com/7vS7Tfx.png) are also easy to grasp.
* Titles are created by adding one or more `#`'s at the beginning of the line.
* A single line break creates a second line. Two line breaks create a paragraph.
* Use angled brackets to cite your favorite authors. The standard formatting options also work in citation blocks.
> Markdown Notes is awesome. I use it every day.
>
>*- Abraham Lincoln*

###1.2. Lists

You create lists by adding a `*` at the begining of the line. You can indent your lists by adding spaces before the `*`. You can have multi-line list items as long as you use single line breaks.

* List item 1
* List item 2
	* Sublist 1
    	* Sublist item 1
        * Sublist item 2
    * Sublist 2
* List item 3
List item 3, line 2

Numbered lists are pretty easy to create too:

1. Item one
	1. Subitem one
    2. Subitem two

You can also mix lists if you feel like it:

1. List item 1
	* Subitem 1
2. List item 2
	1. Subitem 1

You can also create nested to-do lists:

* [x] This one is done
* [ ] This one is not
    * [ ] This is a subtask
    * [ ] Another subtask
    	* [ ] Do this first
    	* [x] Do this next
* [ ] Neither is this one

###1.3. Code

You can create syntax-highlighted code blocks by using three backticks. The code will be highlighted automatically:

```
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World");
    }
}
```

For `inline code`, use a single backtick.

You can also use four spaces to create code blocks:

	def hello_world:
		print 'Hello, World!'

###1.4. LaTeX

Wrap your LaTeX equations with two `$`'s for inline equations and three `$`'s for equation blocks.

You can create an inline equation for $$3n+\sum_2^n{\pi}$$ or put it in a block:

$$$
3n+\sum_2^n{\pi}
$$$

###1.5. Images

Images can be dragged and dropped over the editor. Alternatively, you can copy&paste image data (not files) directly to editor (supported only by Chrome, Firefox and Internet Explorer 11). Lastly, you can use the standard markdown syntax to insert images.

![](http://i.markdownnotes.com/montreal_xMvtnHZ.jpg)

##2. Markdown Notes features

Markdown Notes provides convenient features that make note-taking easier.

###2.1. Preview and editor modes

Use the *Editor*, *Preview* and *Editor + Preview* modes at the top of the window to toggle the editor and the preview.

###2.2. Full screen mode

Click on the ![](http://i.markdownnotes.com/full-screen.png) icon to put the editor in full screen mode.

###2.2. Printing on paper

All your notes are printable. Print them like any other webpage, and the output will be formatted like a professional document. The markdown code will not show on the printed page.

###2.3. Automatic save

Your notes are saved automatically when you stop typing.

###2.4. Exporting notes

Click the download button next to a note in the notes menu to export that note to an `.md` file. These text files can be opened by any markdown editor, although unique features such as LaTeX equations might not work.

###2.5. Code folding

Click the small `▾` icons at the left of the editor to collapse the section under them. Click them again to reveal the hidden sections.

You can also use for section folding keyboard shortcut `Ctrl+M`. If you will use it in middle of text, it will fold the closest surrounding section.

###2.6. Keyboard shortcuts

The following standard shortcuts are supported:

* **Bold text:** `Ctrl+B`
* **Italics:** `Ctrl+I`
* **Link:** `Ctrl+K`
* **Image:** `Ctrl+Shift+K`
* **Fold section:** `Ctrl+M`
* **Print:** `Ctrl+P`

###2.7. Multiple cursors

You hold the `Ctrl` or `Cmd` key while selecting text to have multiple cursors. This allows you to edit multiple blocks of text simultaneously.

###2.8. Linking between notes

You can link to another note by using its ID. For instance, [Note #2880](2880) is the same as [Note #2880](http://markdownnotes.com/app/#/?note=2880).


text:
from django.conf import settings
from django.conf.urls import patterns, include, url
from django.conf.urls.static import static
from django.contrib import admin
from django.views.generic import TemplateView

from news.api import NewsResource
from notes.api import NoteResource, DummyNoteResource, SharedNoteResource, NotebookResource
from authentication.api import *
from notes.views import NotesView, SharedNoteRedirectView
from tastypie.api import Api
from uploads.views import S3RedirectView

# API urls
v1_api = Api(api_name='v1')
v1_api.register(NoteResource())
v1_api.register(NotebookResource())
v1_api.register(DummyNoteResource())
v1_api.register(SharedNoteResource())
v1_api.register(NewsResource())
v1_api.register(CreateUserResource())
v1_api.register(UserResource())
v1_api.register(PasswordRecoveryResource())

urlpatterns = patterns('',
    url(r'^api/', include(v1_api.urls)),
    url(r'^admin/', include(admin.site.urls)),
    url(r'^upload/', include('uploads.urls')),
    url(r'^i/(?P<filename>.+)', S3RedirectView.as_view(), name='image'),
    url(r'^$', TemplateView.as_view(template_name="home.html"), name='homepage'),
    url(r'^feedback/$', TemplateView.as_view(template_name="feedback.html"), name='feedback'),

    url(r'^app/$', NotesView.as_view(), name='index'),
    url(r'^\+(?P<public_id>[a-z0-9]+)/$', SharedNoteRedirectView.as_view(), name='shared_note'),

    url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
        'django.contrib.auth.views.password_reset_confirm', name='password_reset_confirm',
        kwargs={'template_name': 'auth/password_reset_confirm.html'}),
    url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete',
        name='password_reset_complete',
        kwargs={'template_name': 'auth/password_reset_complete.html'}),
) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


text:
# Local settings template

DEBUG = False
TEMPLATE_DEBUG = DEBUG

ALLOWED_HOSTS = ['*']

ADMINS = (
    ('Bob Bobson', 'b+bobson@gmail.com'),
)
MANAGERS = ADMINS

# Required for bug reports
EMAIL_HOST = 'smtp.server.org'
EMAIL_HOST_USER = 'postmaster@server.org'
EMAIL_HOST_PASSWORD = 'password'
EMAIL_PORT = 587
EMAIL_USE_TLS = True

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'dev.db',
    }
}

SECRET_KEY = 'secret'
AWS_ACCESS_KEY_ID = 'ABCDEFGHIJ1234567890'
AWS_SECRET_ACCESS_KEY = '1234567890abcdefghij1234567890abcdefghij'


text:
# Django settings for markdown_notes project.
from django.core.urlresolvers import reverse_lazy
from datetime import date, timedelta
import os

DEBUG = True
TEMPLATE_DEBUG = DEBUG

# Site
SITE_NAME = 'Markdown-Notes'
SITE_DOMAIN = 'MarkdownNotes.com'

# Emails
ADMINS = ()
MANAGERS = ADMINS

EMAIL_HOST = 'smtp.mailgun.org'
EMAIL_HOST_USER = 'postmaster@markdownnotes.com'
EMAIL_HOST_PASSWORD = 'password'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'noreply@markdownnotes.com'

# Databases
DATABASES = {}

# Locales
TIME_ZONE = 'UTC'
LANGUAGE_CODE = 'en-us'
SITE_ID = 1
USE_I18N = True
USE_L10N = True
USE_TZ = True

# URLs
MEDIA_ROOT = 'files'
MEDIA_URL = 'http://i.markdownnotes.com/'
STATIC_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'staticfiles')
STATIC_URL = '/static/'
ROOT_URLCONF = 'markdown_notes.urls'
LOGIN_URL = '/app/'
LOGIN_REDIRECT_URL = reverse_lazy('index')
ADMIN_MEDIA_PREFIX = STATIC_URL + 'admin/'

# Amazon S3
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = 'ABCDEFGHIJ1234567890'
AWS_SECRET_ACCESS_KEY = '1234567890abcdefghij1234567890abcdefghij'
AWS_STORAGE_BUCKET_NAME = 'i.markdownnotes.com'
AWS_QUERYSTRING_AUTH = False  # Removes the signature from the querystring
AWS_S3_FILE_OVERWRITE = False
AWS_MEDIA_URL = 'https://' + AWS_STORAGE_BUCKET_NAME + '.s3.amazonaws.com/'

far_future = date.today() + timedelta(days=365 * 10)
AWS_HEADERS = {
    'Cache-Control': 'max-age=172800',
    'Expires': far_future.strftime('%a, %d %b %Y 20:00:00 GMT'),  # Long term expires headers on images
}

# Tastypie
API_LIMIT_PER_PAGE = 200
TASTYPIE_DATETIME_FORMATTING = 'iso-8601-strict'  # Javascript-parsable date format

# General config
SECRET_KEY = 'secret'
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
ALLOWED_HOSTS = ['markdownnotes.com', '*.markdownnotes.com', 'www.markdownnotes.com']
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
CSRF_COOKIE_NAME = "XSRF-TOKEN"  # For AngularJS compatibility
WSGI_APPLICATION = 'markdown_notes.wsgi.application'
TEST_RUNNER = 'django.test.runner.DiscoverRunner'

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.admin',
    'storages',
    'tastypie',
    'notes',
    'uploads',
    'news',
    'compressor',
    'authentication',
    'django_libsass',
)

STATICFILES_DIRS = (
    os.path.join(os.path.dirname(__file__), "../frontend"),
)
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    'compressor.finders.CompressorFinder',
)
COMPRESS_PRECOMPILERS = (
    ('text/x-scss', 'django_libsass.SassCompiler'),
)

TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__), "../templates"),  # templates dir at the project root
)
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'notes.middleware.AngularCSRFRename',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
)

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {
        "console": {
            "level": "INFO",
            "class": "logging.StreamHandler",
        },
        "mail_admins": {
            "level": "ERROR",
            "class": "django.utils.log.AdminEmailHandler",
        }
    },
    "loggers": {
        "": {
            "handlers": ["console", "mail_admins"],
        }
    }
}

try:
    from local_settings import *
except ImportError:
    pass


text:
"""
WSGI config for markdown_notes project.

This module contains the WSGI application used by Django's development server
and any production WSGI deployments. It should expose a module-level variable
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
this application via the ``WSGI_APPLICATION`` setting.

Usually you will have the standard Django WSGI application here, but it also
might make sense to replace the whole Django WSGI application with a custom one
that later delegates to the Django one. For example, you could introduce WSGI
middleware here, or combine a Django application with an application of another
framework.

"""
import os

# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
# if running multiple sites in the same mod_wsgi process. To fix this, use
# mod_wsgi daemon mode with each site in its own daemon process, or use
# os.environ["DJANGO_SETTINGS_MODULE"] = "markdown_notes.settings"
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "markdown_notes.settings")

# This application object is used by any WSGI server configured to use this
# file. This includes Django's development server, if the WSGI_APPLICATION
# setting points here.
from django.core.wsgi import get_wsgi_application
from dj_static import Cling

application = Cling(get_wsgi_application())

# Apply WSGI middleware here.
# from helloworld.wsgi import HelloWorldApplication
# application = HelloWorldApplication(application)


text:
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries

# You can see what browsers were selected by your queries by running:
#   npx browserslist

> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

repo_name: arjunyel/angular-spacex-graphql-codegen

repo_language: TypeScript

mime_type: text/x-java


text:
# Where to get schema data
schema:
  - https://api.spacex.land/graphql/
# The client side queries to turn into services
documents:
  - src/**/*.graphql
# Where to output the services and the list of plugins
generates:
  ./src/app/services/spacexGraphql.service.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-apollo-angular


text:
module.exports = {
  client: {
    service: {
      name: 'angular-spacex-graphql-codegen',
      url: 'https://api.spacex.land/graphql/'
    }
  }
};


text:
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, './coverage/angular-spacex-graphql-codegen'),
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    restartOnFileChange: true
  });
};


text:
# Angular Spacex Graphql Codegen

## Mission

Our goal is to make an Angular app with a list of the past SpaceX launches along with an associated details page. Data is provided via the [SpaceX GraphQL API](https://medium.com/open-graphql/launching-spacex-graphql-api-b3d7029086e0) and Angular services are generated via [GraphQL Code Generator](https://graphql-code-generator.com/). We use [Apollo Angular](https://www.apollographql.com/docs/angular/) to access data from the frontend. The API is free so please be nice and don't abuse it.

## End Result

![List view](list-example.jpg)

![Details view](details-example.jpg)

## Steps

1. Generate a new angular application with routing

   ```bash
   ng new angular-spacex-graphql-codegen --routing=true --style=css
   ```

   Make sure to delete the default template in `src/app/app.component.html`

1. Install the [Apollo VS Code plugin](https://marketplace.visualstudio.com/items?itemName=apollographql.vscode-apollo) and in the root of the project add `apollo.config.js`

   ```javascript
   module.exports = {
     client: {
       service: {
         name: 'angular-spacex-graphql-codegen',
         url: 'https://api.spacex.land/graphql/'
       }
     }
   };
   ```

   This points the extension at the SpaceX GraphQL API so we get autocomplete, type information, and other cool features in GraphQL files. You may need to restart VS Code.

1. Generate our two components:

   ```bash
   ng g component launch-list --changeDetection=OnPush
   ```

   ```bash
   ng g component launch-details --changeDetection=OnPush
   ```

   Because our generated services use observables we choose OnPush change detection for the best performance.

1. In `src/app/app-routing.module.ts` we setup the routing:

   ```typescript
   import { LaunchListComponent } from './launch-list/launch-list.component';
   import { LaunchDetailsComponent } from './launch-details/launch-details.component';

   const routes: Routes = [
     {
       path: '',
       component: LaunchListComponent
     },
     {
       path: ':id',
       component: LaunchDetailsComponent
     }
   ];
   ```

1. Each component will have its own data requirments so we co-locate our graphql query files next to them

   ```graphql
   # src/app/launch-list/launch-list.graphql

   query pastLaunchesList($limit: Int!) {
     launchesPast(limit: $limit) {
       id
       mission_name
       links {
         flickr_images
         mission_patch_small
       }
       rocket {
         rocket_name
       }
       launch_date_utc
     }
   }
   ```

   ```graphql
   # src/app/launch-details/launch-details.graphql

   query launchDetails($id: ID!) {
     launch(id: $id) {
       id
       mission_name
       details
       links {
         flickr_images
         mission_patch
       }
     }
   }
   ```

   Note the first line: `query launchDetails($id: ID!)` When we generate the Angular service the query name is turned into PascalCase and GQL is appended to the end, so the service name for the launch details would be LaunchDetailsGQL. Also in the first line we define any variables we'll need to pass into the query. Please note it's import to include id in the query return so apollo can cache the data.

1. We add [Apollo Angular](https://www.apollographql.com/docs/angular/) to our app with `ng add apollo-angular`. In `src/app/graphql.module.ts` we set our API url `const uri = 'https://api.spacex.land/graphql/';`.

1. Install Graphql Code Generator and the needed plugins `npm i --save-dev @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-apollo-angular @graphql-codegen/typescript-operations`

1. In the root of the project create a `codegen.yml` file:

   ```yml
   # Where to get schema data
   schema:
     - https://api.spacex.land/graphql/
   # The client side queries to turn into services
   documents:
     - src/**/*.graphql
   # Where to output the services and the list of plugins
   generates:
     ./src/app/services/spacexGraphql.service.ts:
       plugins:
         - typescript
         - typescript-operations
         - typescript-apollo-angular
   ```

1. In package.json add a script `"codegen": "gql-gen"` then `npm run codegen` to generate the Angular Services.

1. To make it look nice we add Angular Material `ng add @angular/material` then in the `app.module.ts` we import the card module and add to the imports array: `import { MatCardModule } from '@angular/material/card';`

1. Lets start with the list of past launches displayed on the screen:

   ```typescript
   import { map } from 'rxjs/operators';
   import { PastLaunchesListGQL } from '../services/spacexGraphql.service';

   export class LaunchListComponent {
     constructor(private readonly pastLaunchesService: PastLaunchesListGQL) {}

     // Please be careful to not fetch too much, but this amount lets us see lazy loading imgs in action
     pastLaunches$ = this.pastLaunchesService
       .fetch({ limit: 30 })
       // Here we extract our query data, we can also get info like errors or loading state from res
       .pipe(map((res) => res.data.launchesPast));
   }
   ```

   ```html
   <ng-container *ngIf="pastLaunches$ | async as pastLaunches">
     <main>
       <section class="container">
         <mat-card
           *ngFor="let launch of pastLaunches"
           [routerLink]="['/', launch.id]"
         >
           <mat-card-header>
             <img
               height="50"
               width="50"
               mat-card-avatar
               loading="lazy"
               [src]="launch.links.mission_patch_small"
               alt="Mission patch of {{ launch.mission_name }}"
             />
             <mat-card-title>{{ launch.mission_name }}</mat-card-title>
             <mat-card-subtitle
               >{{ launch.rocket.rocket_name }}</mat-card-subtitle
             >
           </mat-card-header>
           <img
             height="300"
             width="300"
             mat-card-image
             loading="lazy"
             [src]="launch.links.flickr_images[0]"
             alt="Photo of {{ launch.mission_name }}"
           />
         </mat-card>
       </section>
     </main>
   </ng-container>
   ```

   Notice the cool addition of [lazy loading images](https://web.dev/native-lazy-loading), if you emulate a mobile device in Chrome and fetch enough launches you should see the images lazy load while you scroll!

   To make it look nice we add CSS Grid

   ```css
   .container {
     padding-top: 20px;
     display: grid;
     grid-gap: 30px;
     grid-template-columns: repeat(auto-fill, 350px);
     justify-content: center;
   }

   .mat-card {
     cursor: pointer;
   }
   ```

1. Next we make the details page for a launch, we get the id from the route params and pass that to our service

   ```typescript
   import { ActivatedRoute } from '@angular/router';
   import { map, switchMap } from 'rxjs/operators';
   import { LaunchDetailsGQL } from '../services/spacexGraphql.service';

   export class LaunchDetailsComponent {
     constructor(
       private readonly route: ActivatedRoute,
       private readonly launchDetailsService: LaunchDetailsGQL
     ) {}

     launchDetails$ = this.route.paramMap.pipe(
       map((params) => params.get('id') as string),
       switchMap((id) => this.launchDetailsService.fetch({ id })),
       map((res) => res.data.launch)
     );
   }
   ```

   The HTML looks very similar to the list of launches

   ```html
   <ng-container *ngIf="launchDetails$ | async as launchDetails">
     <section style="padding-top: 20px;">
       <mat-card style="width: 400px; margin: auto;">
         <mat-card-header>
           <mat-card-title>{{ launchDetails.mission_name }}</mat-card-title>
         </mat-card-header>
         <img
           height="256"
           width="256"
           mat-card-image
           [src]="launchDetails.links.mission_patch"
           alt="Mission patch of {{ launchDetails.mission_name }}"
         />
         <mat-card-content>
           <p>{{ launchDetails.details }}</p>
         </mat-card-content>
       </mat-card>
     </section>
     <section class="photo-grid">
       <img
         *ngFor="let pic of launchDetails.links.flickr_images"
         [src]="pic"
         alt="Picture of {{ launchDetails.mission_name }}"
         height="300"
         width="300"
         loading="lazy"
       />
     </section>
   </ng-container>
   ```

   Finally we add CSS Grid for the pictures

   ```css
   .photo-grid {
     padding-top: 30px;
     display: grid;
     grid-gap: 10px;
     grid-template-columns: repeat(auto-fill, 300px);
     justify-content: center;
   }
   ```

1. `npm start`, navigate to `http://localhost:4200/`, and it should work!

### Extra Credit: Relative launch times

Thanks to the new builtin [relative time formating](https://v8.dev/features/intl-relativetimeformat) in V8, we can add `launched x days ago`

1. Generate the pipe: `ng g pipe relative-time --module=app.module --flat=false`

1. The pipe takes in the UTC time and returns a formatted string

   ```typescript
   import { Pipe, PipeTransform } from '@angular/core';

   const milliSecondsInDay = 1000 * 3600 * 24;

   // Cast as any because typescript typing haven't updated yet
   const rtf = new (Intl as any).RelativeTimeFormat('en');

   @Pipe({
     name: 'relativeTime'
   })
   export class RelativeTimePipe implements PipeTransform {
     transform(utcTime: string): string {
       const diffInMillicseconds =
         new Date(utcTime).getTime() - new Date().getTime();
       const diffInDays = Math.round(diffInMillicseconds / milliSecondsInDay);
       return rtf.format(diffInDays, 'day');
     }
   }
   ```

1. Add the pipe to our launch card in src/app/launch-list/launch-list.component.html

   ```html
   <mat-card-subtitle
     >{{ launch.rocket.rocket_name }} - launched {{ launch.launch_date_utc |
     relativeTime }}</mat-card-subtitle
   >
   ```


text:
/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/guide/browser-support
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install --save classlist.js`.

/**
 * Web Animations `@angular/platform-browser/animations`
 * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
 * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
 */
// import 'web-animations-js';  // Run `npm install --save web-animations-js`.

/**
 * By default, zone.js will patch all possible macroTask and DomEvents
 * user can disable parts of macroTask/DomEvents patch by setting following flags
 * because those flags need to be set before `zone.js` being loaded, and webpack
 * will put import in the top of bundle, so user need to create a separate file
 * in this directory (for example: zone-flags.ts), and put the following flags
 * into that file, and then add the following code before importing zone.js.
 * import './zone-flags.ts';
 *
 * The flags allowed in zone-flags.ts are listed here.
 *
 * The following flags will work for all browsers.
 *
 * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
 * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
 *
 *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
 *  with the following flag, it will bypass `zone.js` patch for IE/Edge
 *
 *  (window as any).__Zone_enable_cross_context_check = true;
 *
 */

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import 'zone.js/dist/zone';  // Included with Angular CLI.

/***************************************************************************************************
 * APPLICATION IMPORTS
 */


text:
// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';

declare const require: any;

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);


text:
/* You can add global styles to this file, and also import other style files */

html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }


text:
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AngularSpacexGraphqlCodegen</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
  <app-root></app-root>
</body>
</html>


text:
import 'hammerjs';
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));


text:
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  });

  it(`should have as title 'angular-spacex-graphql-codegen'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual('angular-spacex-graphql-codegen');
  });

  it('should render title', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('.content span').textContent).toContain('angular-spacex-graphql-codegen app is running!');
  });
});


text:
import {NgModule} from '@angular/core';
import {ApolloModule, APOLLO_OPTIONS} from 'apollo-angular';
import {HttpLinkModule, HttpLink} from 'apollo-angular-link-http';
import {InMemoryCache} from 'apollo-cache-inmemory';

const uri = 'https://api.spacex.land/graphql/'; // <-- add the URL of the GraphQL server here
export function createApollo(httpLink: HttpLink) {
  return {
    link: httpLink.create({uri}),
    cache: new InMemoryCache(),
  };
}

@NgModule({
  exports: [ApolloModule, HttpLinkModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink],
    },
  ],
})
export class GraphQLModule {}


text:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LaunchListComponent } from './launch-list/launch-list.component';
import { LaunchDetailsComponent } from './launch-details/launch-details.component';

const routes: Routes = [
  {
    path: '',
    component: LaunchListComponent
  },
  {
    path: ':id',
    component: LaunchDetailsComponent
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}


text: <router-outlet></router-outlet>


text:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LaunchListComponent } from './launch-list/launch-list.component';
import { LaunchDetailsComponent } from './launch-details/launch-details.component';
import { GraphQLModule } from './graphql.module';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatCardModule } from '@angular/material/card';
import { RelativeTimePipe } from './relative-time/relative-time.pipe';

@NgModule({
  declarations: [AppComponent, LaunchListComponent, LaunchDetailsComponent, RelativeTimePipe],
  imports: [
    BrowserModule,
    AppRoutingModule,
    GraphQLModule,
    HttpClientModule,
    BrowserAnimationsModule,
    MatCardModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {}


text:
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'angular-spacex-graphql-codegen';
  test = new Date();
}


text:
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { map, switchMap } from 'rxjs/operators';
import { LaunchDetailsGQL } from '../services/spacexGraphql.service';

@Component({
  selector: 'app-launch-details',
  templateUrl: './launch-details.component.html',
  styleUrls: ['./launch-details.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class LaunchDetailsComponent {
  constructor(
    private readonly route: ActivatedRoute,
    private readonly launchDetailsService: LaunchDetailsGQL
  ) {}

  launchDetails$ = this.route.paramMap.pipe(
    map((params) => params.get('id') as string),
    switchMap((id) => this.launchDetailsService.fetch({ id })),
    map((res) => res.data.launch)
  );
}


text:
# src/app/launch-details/launch-details.frontend.graphql

query launchDetails($id: ID!) {
  launch(id: $id) {
    id
    mission_name
    details
    links {
      flickr_images
      mission_patch
    }
  }
}


text:
.photo-grid {
  padding-top: 30px;
  display: grid;
  grid-gap: 10px;
  grid-template-columns: repeat(auto-fill, 300px);
  justify-content: center;
}


text:
<ng-container *ngIf="launchDetails$ | async as launchDetails">

    <mat-card style="width: 400px; margin: auto;">
      <mat-card-header>
        <mat-card-title>{{ launchDetails.mission_name }}</mat-card-title>
      </mat-card-header>

      <mat-card-content>

{{ launchDetails.details }}

      </mat-card-content>
    </mat-card>

</ng-container>


text:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LaunchDetailsComponent } from './launch-details.component';

describe('LaunchDetailsComponent', () => {
  let component: LaunchDetailsComponent;
  let fixture: ComponentFixture<LaunchDetailsComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LaunchDetailsComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LaunchDetailsComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


text:
import { RelativeTimePipe } from './relative-time.pipe';

describe('RelativeTimePipe', () => {
  it('create an instance', () => {
    const pipe = new RelativeTimePipe();
    expect(pipe).toBeTruthy();
  });
});


text:
import { Pipe, PipeTransform } from '@angular/core';

const milliSecondsInDay = 1000 * 3600 * 24;

// Cast as any because typescript typing haven't updated yet
const rtf = new (Intl as any).RelativeTimeFormat('en');

@Pipe({
  name: 'relativeTime'
})
export class RelativeTimePipe implements PipeTransform {
  transform(utcTime: string): string {
    const diffInMillicseconds =
      new Date(utcTime).getTime() - new Date().getTime();
    const diffInDays = Math.round(diffInMillicseconds / milliSecondsInDay);
    return rtf.format(diffInDays, 'day');
  }
}


text:
import gql from 'graphql-tag';
import { Injectable } from '@angular/core';
import * as Apollo from 'apollo-angular';
export type Maybe<T> = T | null;
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: string,
  String: string,
  Boolean: boolean,
  Int: number,
  Float: number,
  Date: any,
  ObjectID: any,
};

export type Address = {
   __typename?: 'Address',
  address?: Maybe<Scalars['String']>,
  city?: Maybe<Scalars['String']>,
  state?: Maybe<Scalars['String']>,
};

export type Capsule = {
   __typename?: 'Capsule',
  id?: Maybe<Scalars['ID']>,
  landings?: Maybe<Scalars['Int']>,
  missions?: Maybe<Array<Maybe<CapsuleMission>>>,
  original_launch?: Maybe<Scalars['Date']>,
  reuse_count?: Maybe<Scalars['Int']>,
  status?: Maybe<Scalars['String']>,
  type?: Maybe<Scalars['String']>,
  dragon?: Maybe<Dragon>,
};

export type CapsuleMission = {
   __typename?: 'CapsuleMission',
  flight?: Maybe<Scalars['Int']>,
  name?: Maybe<Scalars['String']>,
};

export type CapsulesFind = {
  id?: Maybe<Scalars['ID']>,
  landings?: Maybe<Scalars['Int']>,
  mission?: Maybe<Scalars['String']>,
  original_launch?: Maybe<Scalars['Date']>,
  reuse_count?: Maybe<Scalars['Int']>,
  status?: Maybe<Scalars['String']>,
  type?: Maybe<Scalars['String']>,
};

export type Core = {
   __typename?: 'Core',
  asds_attempts?: Maybe<Scalars['Int']>,
  asds_landings?: Maybe<Scalars['Int']>,
  block?: Maybe<Scalars['Int']>,
  id?: Maybe<Scalars['ID']>,
  missions?: Maybe<Array<Maybe<CapsuleMission>>>,
  original_launch?: Maybe<Scalars['Date']>,
  reuse_count?: Maybe<Scalars['Int']>,
  rtls_attempts?: Maybe<Scalars['Int']>,
  rtls_landings?: Maybe<Scalars['Int']>,
  status?: Maybe<Scalars['String']>,
  water_landing?: Maybe<Scalars['Boolean']>,
};

export type CoreMission = {
   __typename?: 'CoreMission',
  name?: Maybe<Scalars['String']>,
  flight?: Maybe<Scalars['Int']>,
};

export type CoresFind = {
  asds_attempts?: Maybe<Scalars['Int']>,
  asds_landings?: Maybe<Scalars['Int']>,
  block?: Maybe<Scalars['Int']>,
  id?: Maybe<Scalars['String']>,
  missions?: Maybe<Scalars['String']>,
  original_launch?: Maybe<Scalars['Date']>,
  reuse_count?: Maybe<Scalars['Int']>,
  rtls_attempts?: Maybe<Scalars['Int']>,
  rtls_landings?: Maybe<Scalars['Int']>,
  status?: Maybe<Scalars['String']>,
  water_landing?: Maybe<Scalars['Boolean']>,
};

export type Distance = {
   __typename?: 'Distance',
  feet?: Maybe<Scalars['Float']>,
  meters?: Maybe<Scalars['Float']>,
};

export type Dragon = {
   __typename?: 'Dragon',
  active?: Maybe<Scalars['Boolean']>,
  crew_capacity?: Maybe<Scalars['Int']>,
  description?: Maybe<Scalars['String']>,
  diameter?: Maybe<Distance>,
  dry_mass_kg?: Maybe<Scalars['Int']>,
  dry_mass_lb?: Maybe<Scalars['Int']>,
  first_flight?: Maybe<Scalars['String']>,
  heat_shield?: Maybe<DragonHeatShield>,
  height_w_trunk?: Maybe<Distance>,
  id?: Maybe<Scalars['ID']>,
  launch_payload_mass?: Maybe<Mass>,
  launch_payload_vol?: Maybe<Volume>,
  name?: Maybe<Scalars['String']>,
  orbit_duration_yr?: Maybe<Scalars['Int']>,
  pressurized_capsule?: Maybe<DragonPressurizedCapsule>,
  return_payload_mass?: Maybe<Mass>,
  return_payload_vol?: Maybe<Volume>,
  sidewall_angle_deg?: Maybe<Scalars['Float']>,
  thrusters?: Maybe<Array<Maybe<DragonThrust>>>,
  trunk?: Maybe<DragonTrunk>,
  type?: Maybe<Scalars['String']>,
  wikipedia?: Maybe<Scalars['String']>,
};

export type DragonHeatShield = {
   __typename?: 'DragonHeatShield',
  dev_partner?: Maybe<Scalars['String']>,
  material?: Maybe<Scalars['String']>,
  size_meters?: Maybe<Scalars['Float']>,
  temp_degrees?: Maybe<Scalars['Int']>,
};

export type DragonPressurizedCapsule = {
   __typename?: 'DragonPressurizedCapsule',
  payload_volume?: Maybe<Volume>,
};

export type DragonThrust = {
   __typename?: 'DragonThrust',
  amount?: Maybe<Scalars['Int']>,
  fuel_1?: Maybe<Scalars['String']>,
  fuel_2?: Maybe<Scalars['String']>,
  pods?: Maybe<Scalars['Int']>,
  thrust?: Maybe<Force>,
  type?: Maybe<Scalars['String']>,
};

export type DragonTrunk = {
   __typename?: 'DragonTrunk',
  cargo?: Maybe<DragonTrunkCargo>,
  trunk_volume?: Maybe<Volume>,
};

export type DragonTrunkCargo = {
   __typename?: 'DragonTrunkCargo',
  solar_array?: Maybe<Scalars['Int']>,
  unpressurized_cargo?: Maybe<Scalars['Boolean']>,
};

export type Force = {
   __typename?: 'Force',
  kN?: Maybe<Scalars['Float']>,
  lbf?: Maybe<Scalars['Float']>,
};

export type HistoriesResult = {
   __typename?: 'HistoriesResult',
  result?: Maybe<Result>,
  data?: Maybe<Array<Maybe<History>>>,
};

export type History = {
   __typename?: 'History',
  details?: Maybe<Scalars['String']>,
  event_date_unix?: Maybe<Scalars['Date']>,
  event_date_utc?: Maybe<Scalars['Date']>,
  id?: Maybe<Scalars['ID']>,
  links?: Maybe<Link>,
  title?: Maybe<Scalars['String']>,
  flight?: Maybe<Launch>,
};

export type HistoryFind = {
  end?: Maybe<Scalars['Date']>,
  flight_number?: Maybe<Scalars['Int']>,
  id?: Maybe<Scalars['ID']>,
  start?: Maybe<Scalars['Date']>,
};

export type Info = {
   __typename?: 'Info',
  ceo?: Maybe<Scalars['String']>,
  coo?: Maybe<Scalars['String']>,
  cto_propulsion?: Maybe<Scalars['String']>,
  cto?: Maybe<Scalars['String']>,
  employees?: Maybe<Scalars['Int']>,
  founded?: Maybe<Scalars['Int']>,
  founder?: Maybe<Scalars['String']>,
  headquarters?: Maybe<Address>,
  launch_sites?: Maybe<Scalars['Int']>,
  links?: Maybe<InfoLinks>,
  name?: Maybe<Scalars['String']>,
  summary?: Maybe<Scalars['String']>,
  test_sites?: Maybe<Scalars['Int']>,
  valuation?: Maybe<Scalars['Float']>,
  vehicles?: Maybe<Scalars['Int']>,
};

export type InfoLinks = {
   __typename?: 'InfoLinks',
  elon_twitter?: Maybe<Scalars['String']>,
  flickr?: Maybe<Scalars['String']>,
  twitter?: Maybe<Scalars['String']>,
  website?: Maybe<Scalars['String']>,
};

export type Landpad = {
   __typename?: 'Landpad',
  attempted_landings?: Maybe<Scalars['String']>,
  details?: Maybe<Scalars['String']>,
  full_name?: Maybe<Scalars['String']>,
  id?: Maybe<Scalars['ID']>,
  landing_type?: Maybe<Scalars['String']>,
  location?: Maybe<Location>,
  status?: Maybe<Scalars['String']>,
  successful_landings?: Maybe<Scalars['String']>,
  wikipedia?: Maybe<Scalars['String']>,
};

export type Launch = {
   __typename?: 'Launch',
  details?: Maybe<Scalars['String']>,
  id?: Maybe<Scalars['ID']>,
  is_tentative?: Maybe<Scalars['Boolean']>,
  launch_date_local?: Maybe<Scalars['Date']>,
  launch_date_unix?: Maybe<Scalars['Date']>,
  launch_date_utc?: Maybe<Scalars['Date']>,
  launch_site?: Maybe<LaunchSite>,
  launch_success?: Maybe<Scalars['Boolean']>,
  launch_year?: Maybe<Scalars['String']>,
  links?: Maybe<LaunchLinks>,
  mission_id?: Maybe<Array<Maybe<Scalars['String']>>>,
  mission_name?: Maybe<Scalars['String']>,
  rocket?: Maybe<LaunchRocket>,
  static_fire_date_unix?: Maybe<Scalars['Date']>,
  static_fire_date_utc?: Maybe<Scalars['Date']>,
  telemetry?: Maybe<LaunchTelemetry>,
  tentative_max_precision?: Maybe<Scalars['String']>,
  upcoming?: Maybe<Scalars['Boolean']>,
  ships?: Maybe<Array<Maybe<Ship>>>,
};

export type LaunchesPastResult = {
   __typename?: 'LaunchesPastResult',
  result?: Maybe<Result>,
  data?: Maybe<Array<Maybe<Launch>>>,
};

export type LaunchFind = {
  apoapsis_km?: Maybe<Scalars['Float']>,
  block?: Maybe<Scalars['Int']>,
  cap_serial?: Maybe<Scalars['String']>,
  capsule_reuse?: Maybe<Scalars['String']>,
  core_flight?: Maybe<Scalars['Int']>,
  core_reuse?: Maybe<Scalars['String']>,
  core_serial?: Maybe<Scalars['String']>,
  customer?: Maybe<Scalars['String']>,
  eccentricity?: Maybe<Scalars['Float']>,
  end?: Maybe<Scalars['Date']>,
  epoch?: Maybe<Scalars['Date']>,
  fairings_recovered?: Maybe<Scalars['String']>,
  fairings_recovery_attempt?: Maybe<Scalars['String']>,
  fairings_reuse?: Maybe<Scalars['String']>,
  fairings_reused?: Maybe<Scalars['String']>,
  fairings_ship?: Maybe<Scalars['String']>,
  gridfins?: Maybe<Scalars['String']>,
  id?: Maybe<Scalars['ID']>,
  inclination_deg?: Maybe<Scalars['Float']>,
  land_success?: Maybe<Scalars['String']>,
  landing_intent?: Maybe<Scalars['String']>,
  landing_type?: Maybe<Scalars['String']>,
  landing_vehicle?: Maybe<Scalars['String']>,
  launch_date_local?: Maybe<Scalars['Date']>,
  launch_date_utc?: Maybe<Scalars['Date']>,
  launch_success?: Maybe<Scalars['String']>,
  launch_year?: Maybe<Scalars['String']>,
  legs?: Maybe<Scalars['String']>,
  lifespan_years?: Maybe<Scalars['Float']>,
  longitude?: Maybe<Scalars['Float']>,
  manufacturer?: Maybe<Scalars['String']>,
  mean_motion?: Maybe<Scalars['Float']>,
  mission_id?: Maybe<Scalars['String']>,
  mission_name?: Maybe<Scalars['String']>,
  nationality?: Maybe<Scalars['String']>,
  norad_id?: Maybe<Scalars['Int']>,
  orbit?: Maybe<Scalars['String']>,
  payload_id?: Maybe<Scalars['String']>,
  payload_type?: Maybe<Scalars['String']>,
  periapsis_km?: Maybe<Scalars['Float']>,
  period_min?: Maybe<Scalars['Float']>,
  raan?: Maybe<Scalars['Float']>,
  reference_system?: Maybe<Scalars['String']>,
  regime?: Maybe<Scalars['String']>,
  reused?: Maybe<Scalars['String']>,
  rocket_id?: Maybe<Scalars['String']>,
  rocket_name?: Maybe<Scalars['String']>,
  rocket_type?: Maybe<Scalars['String']>,
  second_stage_block?: Maybe<Scalars['String']>,
  semi_major_axis_km?: Maybe<Scalars['Float']>,
  ship?: Maybe<Scalars['String']>,
  side_core1_reuse?: Maybe<Scalars['String']>,
  side_core2_reuse?: Maybe<Scalars['String']>,
  site_id?: Maybe<Scalars['String']>,
  site_name_long?: Maybe<Scalars['String']>,
  site_name?: Maybe<Scalars['String']>,
  start?: Maybe<Scalars['Date']>,
  tbd?: Maybe<Scalars['String']>,
  tentative_max_precision?: Maybe<Scalars['String']>,
  tentative?: Maybe<Scalars['String']>,
};

export type LaunchLinks = {
   __typename?: 'LaunchLinks',
  article_link?: Maybe<Scalars['String']>,
  flickr_images?: Maybe<Array<Maybe<Scalars['String']>>>,
  mission_patch_small?: Maybe<Scalars['String']>,
  mission_patch?: Maybe<Scalars['String']>,
  presskit?: Maybe<Scalars['String']>,
  reddit_campaign?: Maybe<Scalars['String']>,
  reddit_launch?: Maybe<Scalars['String']>,
  reddit_media?: Maybe<Scalars['String']>,
  reddit_recovery?: Maybe<Scalars['String']>,
  video_link?: Maybe<Scalars['String']>,
  wikipedia?: Maybe<Scalars['String']>,
};

export type Launchpad = {
   __typename?: 'Launchpad',
  attempted_launches?: Maybe<Scalars['Int']>,
  details?: Maybe<Scalars['String']>,
  id?: Maybe<Scalars['ID']>,
  location?: Maybe<Location>,
  name?: Maybe<Scalars['String']>,
  status?: Maybe<Scalars['String']>,
  successful_launches?: Maybe<Scalars['Int']>,
  vehicles_launched?: Maybe<Array<Maybe<Rocket>>>,
  wikipedia?: Maybe<Scalars['String']>,
};

export type LaunchRocket = {
   __typename?: 'LaunchRocket',
  fairings?: Maybe<LaunchRocketFairings>,
  first_stage?: Maybe<LaunchRocketFirstStage>,
  rocket_name?: Maybe<Scalars['String']>,
  rocket_type?: Maybe<Scalars['String']>,
  rocket?: Maybe<Rocket>,
  second_stage?: Maybe<LaunchRocketSecondStage>,
};

export type LaunchRocketFairings = {
   __typename?: 'LaunchRocketFairings',
  recovered?: Maybe<Scalars['Boolean']>,
  recovery_attempt?: Maybe<Scalars['Boolean']>,
  reused?: Maybe<Scalars['Boolean']>,
  ship?: Maybe<Scalars['String']>,
};

export type LaunchRocketFirstStage = {
   __typename?: 'LaunchRocketFirstStage',
  cores?: Maybe<Array<Maybe<LaunchRocketFirstStageCore>>>,
};

export type LaunchRocketFirstStageCore = {
   __typename?: 'LaunchRocketFirstStageCore',
  block?: Maybe<Scalars['Int']>,
  core?: Maybe<Core>,
  flight?: Maybe<Scalars['Int']>,
  gridfins?: Maybe<Scalars['Boolean']>,
  land_success?: Maybe<Scalars['Boolean']>,
  landing_intent?: Maybe<Scalars['Boolean']>,
  landing_type?: Maybe<Scalars['String']>,
  landing_vehicle?: Maybe<Scalars['String']>,
  legs?: Maybe<Scalars['Boolean']>,
  reused?: Maybe<Scalars['Boolean']>,
};

export type LaunchRocketSecondStage = {
   __typename?: 'LaunchRocketSecondStage',
  block?: Maybe<Scalars['Int']>,
  payloads?: Maybe<Array<Maybe<Payload>>>,
};

export type LaunchSite = {
   __typename?: 'LaunchSite',
  site_id?: Maybe<Scalars['String']>,
  site_name_long?: Maybe<Scalars['String']>,
  site_name?: Maybe<Scalars['String']>,
};

export type LaunchTelemetry = {
   __typename?: 'LaunchTelemetry',
  flight_club?: Maybe<Scalars['String']>,
};

export type Link = {
   __typename?: 'Link',
  article?: Maybe<Scalars['String']>,
  reddit?: Maybe<Scalars['String']>,
  wikipedia?: Maybe<Scalars['String']>,
};

export type Location = {
   __typename?: 'Location',
  latitude?: Maybe<Scalars['Float']>,
  longitude?: Maybe<Scalars['Float']>,
  name?: Maybe<Scalars['String']>,
  region?: Maybe<Scalars['String']>,
};

export type Mass = {
   __typename?: 'Mass',
  kg?: Maybe<Scalars['Int']>,
  lb?: Maybe<Scalars['Int']>,
};

export type Mission = {
   __typename?: 'Mission',
  description?: Maybe<Scalars['String']>,
  id?: Maybe<Scalars['ID']>,
  manufacturers?: Maybe<Array<Maybe<Scalars['String']>>>,
  name?: Maybe<Scalars['String']>,
  twitter?: Maybe<Scalars['String']>,
  website?: Maybe<Scalars['String']>,
  wikipedia?: Maybe<Scalars['String']>,
  payloads?: Maybe<Array<Maybe<Payload>>>,
};

export type MissionResult = {
   __typename?: 'MissionResult',
  result?: Maybe<Result>,
  data?: Maybe<Array<Maybe<Mission>>>,
};

export type MissionsFind = {
  id?: Maybe<Scalars['ID']>,
  manufacturer?: Maybe<Scalars['String']>,
  name?: Maybe<Scalars['String']>,
  payload_id?: Maybe<Scalars['String']>,
};

export type Payload = {
   __typename?: 'Payload',
  customers?: Maybe<Array<Maybe<Scalars['String']>>>,
  id?: Maybe<Scalars['ID']>,
  manufacturer?: Maybe<Scalars['String']>,
  nationality?: Maybe<Scalars['String']>,
  norad_id?: Maybe<Array<Maybe<Scalars['Int']>>>,
  orbit_params?: Maybe<PayloadOrbitParams>,
  orbit?: Maybe<Scalars['String']>,
  payload_mass_kg?: Maybe<Scalars['Float']>,
  payload_mass_lbs?: Maybe<Scalars['Float']>,
  payload_type?: Maybe<Scalars['String']>,
  reused?: Maybe<Scalars['Boolean']>,
};

export type PayloadOrbitParams = {
   __typename?: 'PayloadOrbitParams',
  apoapsis_km?: Maybe<Scalars['Float']>,
  arg_of_pericenter?: Maybe<Scalars['Float']>,
  eccentricity?: Maybe<Scalars['Float']>,
  epoch?: Maybe<Scalars['Date']>,
  inclination_deg?: Maybe<Scalars['Float']>,
  lifespan_years?: Maybe<Scalars['Float']>,
  longitude?: Maybe<Scalars['Float']>,
  mean_anomaly?: Maybe<Scalars['Float']>,
  mean_motion?: Maybe<Scalars['Float']>,
  periapsis_km?: Maybe<Scalars['Float']>,
  period_min?: Maybe<Scalars['Float']>,
  raan?: Maybe<Scalars['Float']>,
  reference_system?: Maybe<Scalars['String']>,
  regime?: Maybe<Scalars['String']>,
  semi_major_axis_km?: Maybe<Scalars['Float']>,
};

export type PayloadsFind = {
  apoapsis_km?: Maybe<Scalars['Float']>,
  customer?: Maybe<Scalars['String']>,
  eccentricity?: Maybe<Scalars['Float']>,
  epoch?: Maybe<Scalars['Date']>,
  inclination_deg?: Maybe<Scalars['Float']>,
  lifespan_years?: Maybe<Scalars['Float']>,
  longitude?: Maybe<Scalars['Float']>,
  manufacturer?: Maybe<Scalars['String']>,
  mean_motion?: Maybe<Scalars['Float']>,
  nationality?: Maybe<Scalars['String']>,
  norad_id?: Maybe<Scalars['Int']>,
  orbit?: Maybe<Scalars['String']>,
  payload_id?: Maybe<Scalars['ID']>,
  payload_type?: Maybe<Scalars['String']>,
  periapsis_km?: Maybe<Scalars['Float']>,
  period_min?: Maybe<Scalars['Float']>,
  raan?: Maybe<Scalars['Float']>,
  reference_system?: Maybe<Scalars['String']>,
  regime?: Maybe<Scalars['String']>,
  reused?: Maybe<Scalars['Boolean']>,
  semi_major_axis_km?: Maybe<Scalars['Float']>,
};

export type Query = {
   __typename?: 'Query',
  capsules?: Maybe<Array<Maybe<Capsule>>>,
  capsulesPast?: Maybe<Array<Maybe<Capsule>>>,
  capsulesUpcoming?: Maybe<Array<Maybe<Capsule>>>,
  capsule?: Maybe<Capsule>,
  company?: Maybe<Info>,
  cores?: Maybe<Array<Maybe<Core>>>,
  coresPast?: Maybe<Array<Maybe<Core>>>,
  coresUpcoming?: Maybe<Array<Maybe<Core>>>,
  core?: Maybe<Core>,
  dragons?: Maybe<Array<Maybe<Dragon>>>,
  dragon?: Maybe<Dragon>,
  histories?: Maybe<Array<Maybe<History>>>,
  historiesResult?: Maybe<HistoriesResult>,
  history?: Maybe<History>,
  landpads?: Maybe<Array<Maybe<Landpad>>>,
  landpad?: Maybe<Landpad>,
  launches?: Maybe<Array<Maybe<Launch>>>,
  launchesPast?: Maybe<Array<Maybe<Launch>>>,
  launchesPastResult?: Maybe<LaunchesPastResult>,
  launchesUpcoming?: Maybe<Array<Maybe<Launch>>>,
  launch?: Maybe<Launch>,
  launchLatest?: Maybe<Launch>,
  launchNext?: Maybe<Launch>,
  launchpads?: Maybe<Array<Maybe<Launchpad>>>,
  launchpad?: Maybe<Launchpad>,
  missions?: Maybe<Array<Maybe<Mission>>>,
  missionsResult?: Maybe<MissionResult>,
  mission?: Maybe<Mission>,
  payloads?: Maybe<Array<Maybe<Payload>>>,
  payload?: Maybe<Payload>,
  roadster?: Maybe<Roadster>,
  rockets?: Maybe<Array<Maybe<Rocket>>>,
  rocketsResult?: Maybe<RocketsResult>,
  rocket?: Maybe<Rocket>,
  ships?: Maybe<Array<Maybe<Ship>>>,
  shipsResult?: Maybe<ShipsResult>,
  ship?: Maybe<Ship>,
};

export type QueryCapsulesArgs = {
  find?: Maybe<CapsulesFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryCapsulesPastArgs = {
  find?: Maybe<CapsulesFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryCapsulesUpcomingArgs = {
  find?: Maybe<CapsulesFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryCapsuleArgs = {
  id: Scalars['ID']
};

export type QueryCoresArgs = {
  find?: Maybe<CoresFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryCoresPastArgs = {
  find?: Maybe<CoresFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryCoresUpcomingArgs = {
  find?: Maybe<CoresFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryCoreArgs = {
  id: Scalars['ID']
};

export type QueryDragonsArgs = {
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>
};

export type QueryDragonArgs = {
  id: Scalars['ID']
};

export type QueryHistoriesArgs = {
  find?: Maybe<HistoryFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryHistoriesResultArgs = {
  find?: Maybe<HistoryFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryHistoryArgs = {
  id: Scalars['ID']
};

export type QueryLandpadsArgs = {
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>
};

export type QueryLandpadArgs = {
  id: Scalars['ID']
};

export type QueryLaunchesArgs = {
  find?: Maybe<LaunchFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryLaunchesPastArgs = {
  find?: Maybe<LaunchFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryLaunchesPastResultArgs = {
  find?: Maybe<LaunchFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryLaunchesUpcomingArgs = {
  find?: Maybe<LaunchFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryLaunchArgs = {
  id: Scalars['ID']
};

export type QueryLaunchLatestArgs = {
  offset?: Maybe<Scalars['Int']>
};

export type QueryLaunchNextArgs = {
  offset?: Maybe<Scalars['Int']>
};

export type QueryLaunchpadsArgs = {
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>
};

export type QueryLaunchpadArgs = {
  id: Scalars['ID']
};

export type QueryMissionsArgs = {
  find?: Maybe<MissionsFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>
};

export type QueryMissionsResultArgs = {
  find?: Maybe<MissionsFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>
};

export type QueryMissionArgs = {
  id: Scalars['ID']
};

export type QueryPayloadsArgs = {
  find?: Maybe<PayloadsFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryPayloadArgs = {
  id: Scalars['ID']
};

export type QueryRocketsArgs = {
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>
};

export type QueryRocketsResultArgs = {
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>
};

export type QueryRocketArgs = {
  id: Scalars['ID']
};

export type QueryShipsArgs = {
  find?: Maybe<ShipsFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryShipsResultArgs = {
  find?: Maybe<ShipsFind>,
  limit?: Maybe<Scalars['Int']>,
  offset?: Maybe<Scalars['Int']>,
  order?: Maybe<Scalars['String']>,
  sort?: Maybe<Scalars['String']>
};

export type QueryShipArgs = {
  id: Scalars['ID']
};

export type Result = {
   __typename?: 'Result',
  totalCount?: Maybe<Scalars['Int']>,
};

export type Roadster = {
   __typename?: 'Roadster',
  apoapsis_au?: Maybe<Scalars['Float']>,
  details?: Maybe<Scalars['String']>,
  earth_distance_km?: Maybe<Scalars['Float']>,
  earth_distance_mi?: Maybe<Scalars['Float']>,
  eccentricity?: Maybe<Scalars['Float']>,
  epoch_jd?: Maybe<Scalars['Float']>,
  inclination?: Maybe<Scalars['Float']>,
  launch_date_unix?: Maybe<Scalars['Date']>,
  launch_date_utc?: Maybe<Scalars['Date']>,
  launch_mass_kg?: Maybe<Scalars['Int']>,
  launch_mass_lbs?: Maybe<Scalars['Int']>,
  longitude?: Maybe<Scalars['Float']>,
  mars_distance_km?: Maybe<Scalars['Float']>,
  mars_distance_mi?: Maybe<Scalars['Float']>,
  name?: Maybe<Scalars['String']>,
  norad_id?: Maybe<Scalars['Int']>,
  orbit_type?: Maybe<Scalars['Float']>,
  periapsis_arg?: Maybe<Scalars['Float']>,
  periapsis_au?: Maybe<Scalars['Float']>,
  period_days?: Maybe<Scalars['Float']>,
  semi_major_axis_au?: Maybe<Scalars['Float']>,
  speed_kph?: Maybe<Scalars['Float']>,
  speed_mph?: Maybe<Scalars['Float']>,
  wikipedia?: Maybe<Scalars['String']>,
};

export type Rocket = {
   __typename?: 'Rocket',
  active?: Maybe<Scalars['Boolean']>,
  boosters?: Maybe<Scalars['Int']>,
  company?: Maybe<Scalars['String']>,
  cost_per_launch?: Maybe<Scalars['Int']>,
  country?: Maybe<Scalars['String']>,
  description?: Maybe<Scalars['String']>,
  diameter?: Maybe<Distance>,
  engines?: Maybe<RocketEngines>,
  first_flight?: Maybe<Scalars['Date']>,
  first_stage?: Maybe<RocketFirstStage>,
  height?: Maybe<Distance>,
  id?: Maybe<Scalars['ID']>,
  landing_legs?: Maybe<RocketLandingLegs>,
  mass?: Maybe<Mass>,
  name?: Maybe<Scalars['String']>,
  payload_weights?: Maybe<Array<Maybe<RocketPayloadWeight>>>,
  second_stage?: Maybe<RocketSecondStage>,
  stages?: Maybe<Scalars['Int']>,
  success_rate_pct?: Maybe<Scalars['Int']>,
  type?: Maybe<Scalars['String']>,
  wikipedia?: Maybe<Scalars['String']>,
};

export type RocketEngines = {
   __typename?: 'RocketEngines',
  number?: Maybe<Scalars['Int']>,
  type?: Maybe<Scalars['String']>,
  version?: Maybe<Scalars['String']>,
  layout?: Maybe<Scalars['String']>,
  engine_loss_max?: Maybe<Scalars['String']>,
  propellant_1?: Maybe<Scalars['String']>,
  propellant_2?: Maybe<Scalars['String']>,
  thrust_sea_level?: Maybe<Force>,
  thrust_vacuum?: Maybe<Force>,
  thrust_to_weight?: Maybe<Scalars['Float']>,
};

export type RocketFirstStage = {
   __typename?: 'RocketFirstStage',
  burn_time_sec?: Maybe<Scalars['Int']>,
  engines?: Maybe<Scalars['Int']>,
  fuel_amount_tons?: Maybe<Scalars['Float']>,
  reusable?: Maybe<Scalars['Boolean']>,
  thrust_sea_level?: Maybe<Force>,
  thrust_vacuum?: Maybe<Force>,
};

export type RocketLandingLegs = {
   __typename?: 'RocketLandingLegs',
  number?: Maybe<Scalars['Int']>,
  material?: Maybe<Scalars['String']>,
};

export type RocketPayloadWeight = {
   __typename?: 'RocketPayloadWeight',
  id?: Maybe<Scalars['String']>,
  kg?: Maybe<Scalars['Int']>,
  lb?: Maybe<Scalars['Int']>,
  name?: Maybe<Scalars['String']>,
};

export type RocketSecondStage = {
   __typename?: 'RocketSecondStage',
  burn_time_sec?: Maybe<Scalars['Int']>,
  engines?: Maybe<Scalars['Int']>,
  fuel_amount_tons?: Maybe<Scalars['Float']>,
  payloads?: Maybe<RocketSecondStagePayloads>,
  thrust?: Maybe<Force>,
};

export type RocketSecondStagePayloadCompositeFairing = {
   __typename?: 'RocketSecondStagePayloadCompositeFairing',
  height?: Maybe<Distance>,
  diameter?: Maybe<Distance>,
};

export type RocketSecondStagePayloads = {
   __typename?: 'RocketSecondStagePayloads',
  option_1?: Maybe<Scalars['String']>,
  composite_fairing?: Maybe<RocketSecondStagePayloadCompositeFairing>,
};

export type RocketsResult = {
   __typename?: 'RocketsResult',
  result?: Maybe<Result>,
  data?: Maybe<Array<Maybe<Rocket>>>,
};

export type Ship = {
   __typename?: 'Ship',
  abs?: Maybe<Scalars['Int']>,
  active?: Maybe<Scalars['Boolean']>,
  attempted_landings?: Maybe<Scalars['Int']>,
  class?: Maybe<Scalars['Int']>,
  course_deg?: Maybe<Scalars['Int']>,
  home_port?: Maybe<Scalars['String']>,
  id?: Maybe<Scalars['ID']>,
  image?: Maybe<Scalars['String']>,
  imo?: Maybe<Scalars['Int']>,
  missions?: Maybe<Array<Maybe<ShipMission>>>,
  mmsi?: Maybe<Scalars['Int']>,
  model?: Maybe<Scalars['String']>,
  name?: Maybe<Scalars['String']>,
  position?: Maybe<ShipLocation>,
  roles?: Maybe<Array<Maybe<Scalars['String']>>>,
  speed_kn?: Maybe<Scalars['Float']>,
  status?: Maybe<Scalars['String']>,
  successful_landings?: Maybe<Scalars['Int']>,
  type?: Maybe<Scalars['String']>,
  url?: Maybe<Scalars['String']>,
  weight_kg?: Maybe<Scalars['Int']>,
  weight_lbs?: Maybe<Scalars['Int']>,
  year_built?: Maybe<Scalars['Int']>,
};

export type ShipLocation = {
   __typename?: 'ShipLocation',
  latitude?: Maybe<Scalars['Float']>,
  longitude?: Maybe<Scalars['Float']>,
};

export type ShipMission = {
   __typename?: 'ShipMission',
  flight?: Maybe<Scalars['String']>,
  name?: Maybe<Scalars['String']>,
};

export type ShipsFind = {
  id?: Maybe<Scalars['ID']>,
  name?: Maybe<Scalars['String']>,
  model?: Maybe<Scalars['String']>,
  type?: Maybe<Scalars['String']>,
  role?: Maybe<Scalars['String']>,
  active?: Maybe<Scalars['Boolean']>,
  imo?: Maybe<Scalars['Int']>,
  mmsi?: Maybe<Scalars['Int']>,
  abs?: Maybe<Scalars['Int']>,
  class?: Maybe<Scalars['Int']>,
  weight_lbs?: Maybe<Scalars['Int']>,
  weight_kg?: Maybe<Scalars['Int']>,
  year_built?: Maybe<Scalars['Int']>,
  home_port?: Maybe<Scalars['String']>,
  status?: Maybe<Scalars['String']>,
  speed_kn?: Maybe<Scalars['Int']>,
  course_deg?: Maybe<Scalars['Int']>,
  latitude?: Maybe<Scalars['Float']>,
  longitude?: Maybe<Scalars['Float']>,
  successful_landings?: Maybe<Scalars['Int']>,
  attempted_landings?: Maybe<Scalars['Int']>,
  mission?: Maybe<Scalars['String']>,
};

export type ShipsResult = {
   __typename?: 'ShipsResult',
  result?: Maybe<Result>,
  data?: Maybe<Array<Maybe<Ship>>>,
};

export type Volume = {
   __typename?: 'Volume',
  cubic_feet?: Maybe<Scalars['Int']>,
  cubic_meters?: Maybe<Scalars['Int']>,
};
export type LaunchDetailsQueryVariables = {
  id: Scalars['ID']
};

export type LaunchDetailsQuery = (
  { __typename?: 'Query' }
  & { launch: Maybe<(
    { __typename?: 'Launch' }
    & Pick<Launch, 'id' | 'mission_name' | 'details'>
    & { links: Maybe<(
      { __typename?: 'LaunchLinks' }
      & Pick<LaunchLinks, 'flickr_images' | 'mission_patch'>
    )> }
  )> }
);

export type PastLaunchesListQueryVariables = {
  limit: Scalars['Int']
};

export type PastLaunchesListQuery = (
  { __typename?: 'Query' }
  & { launchesPast: Maybe<Array<Maybe<(
    { __typename?: 'Launch' }
    & Pick<Launch, 'id' | 'mission_name' | 'launch_date_utc'>
    & { links: Maybe<(
      { __typename?: 'LaunchLinks' }
      & Pick<LaunchLinks, 'flickr_images' | 'mission_patch_small'>
    )>, rocket: Maybe<(
      { __typename?: 'LaunchRocket' }
      & Pick<LaunchRocket, 'rocket_name'>
    )> }
  )>>> }
);

export const LaunchDetailsDocument = gql`
    query launchDetails($id: ID!) {
  launch(id: $id) {
    id
    mission_name
    details
    links {
      flickr_images
      mission_patch
    }
  }
}
    `;

  @Injectable({
    providedIn: 'root'
  })
  export class LaunchDetailsGQL extends Apollo.Query<LaunchDetailsQuery, LaunchDetailsQueryVariables> {
    document = LaunchDetailsDocument;

  }
export const PastLaunchesListDocument = gql`
    query pastLaunchesList($limit: Int!) {
  launchesPast(limit: $limit) {
    id
    mission_name
    links {
      flickr_images
      mission_patch_small
    }
    rocket {
      rocket_name
    }
    launch_date_utc
  }
}
    `;

  @Injectable({
    providedIn: 'root'
  })
  export class PastLaunchesListGQL extends Apollo.Query<PastLaunchesListQuery, PastLaunchesListQueryVariables> {
    document = PastLaunchesListDocument;

  }


text:
# src/app/launch-list/launch-list.graphql

query pastLaunchesList($limit: Int!) {
  launchesPast(limit: $limit) {
    id
    mission_name
    links {
      flickr_images
      mission_patch_small
    }
    rocket {
      rocket_name
    }
    launch_date_utc
  }
}


text:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { LaunchListComponent } from './launch-list.component';

describe('LaunchListComponent', () => {
  let component: LaunchListComponent;
  let fixture: ComponentFixture<LaunchListComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LaunchListComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LaunchListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});