Source

moz-tree-utopia / repodata / web / app.py

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from __future__ import unicode_literals

from cornice.ext.spore import generate_spore_description
from cornice.service import (
    Service,
    get_services,
)

from pyramid.config import Configurator
from pyramid.httpexceptions import (
    HTTPNotFound,
)

from repodata.config import get_global_state
import repodata.models as models

def get_service(*args, **kwargs):
    return Service(*args, cors_enabled=True, cors_origins=['*'], **kwargs)


status = get_service(name='status', path='/status',
    description='Service status')

@status.get()
def get_status(req):
    """Obtain current status of the service and database state.

    Currently, this simply returns some statistics about data in the database.
    """
    return {
        'changesets_total': req.dbsession.query(models.Changeset).count(),
        'changesets_scan_pending': req.dbsession.query(models.Changeset).\
            filter(models.Changeset.scan_version!=2).\
            count(),
    }

repos = get_service(name='repos', path='/repos',
    description='Information about repositories.')

@repos.get()
def get_repos(req):
    """Obtain information about every repository tracked by this service.

    Use this to discover what repositories can be queried and what their URLs
    are.
    """
    res = req.dbsession.query(models.LocalMercurialRepo).all()

    return {
        'repos': [r.to_dict() for r in res],
    }

changeset = get_service(name='changeset', path='/changeset/{node}',
    description='Look up information about a Mercurial changeset.')

@changeset.get()
def get_changeset(req):
    """Return information about a Mercurial changeset, specified by its node.

    It is highly recommended to specify nodes by their full 40 character
    SHA-1. If less than 40 characters are specified, a string prefix match will
    be performed and the first match returned. Because numeric revision
    numbers are repository specific and thus not stable across clones,
    they are not accepted.

    Changesets can be in one of 4 states:

    1. Not known. If a changeset isn't known by this service, a 404 will be
       issued. A changeset may not be known if it is very new (typically
       committed less than 30 seconds ago) or if it was committed to an
       untracked repository. See the /repos endpoint for information on
       what repositories are tracked.

    2. Not indexed. When changesets are first added to the database, only
       the base node and a repository association are stored. All other
       metadata (the author, commit message, etc) are loaded later by
       a background daemon. If the service is operating as expected,
       changeset metadata should populate seconds after the changeset
       is introduced to the database. However, service outages and imports
       of large amounts of new changesets may delay scanning.

    3. Out of date. If this service introduces new data indexes and mappings,
       it's possible that the changeset has not yet been scanned for these
       new associations. Currently, we are unable to identify this state
       through the web service. Stay tuned.

    4. Up to date. The changeset is fully scanned and all metadata is
       available. Currently, we are unable to distinguish this state from
       #3.
    """
    cset = req.dbsession.query(models.Changeset).\
        filter(models.Changeset.node.startswith(req.matchdict['node'])).\
        first()

    if not cset:
        raise HTTPNotFound()

    return cset.to_dict()


git_sha1 = get_service(name='git-sha1', path='/git-sha1/{sha1}',
    description='Map Mercurial SHA-1 to Git SHA-1.')

@git_sha1.get()
def get_git_sha1(req):
    """Obtain the Mercurial changeset info given a Git commit SHA-1.

    Given a Git commit SHA-1, return the Mercurial changeset info for
    the corresponding Mercurial commit. This is the same as /changeset/{node}
    except Git SHA-1's are the input.

    If the Git SHA-1 is unknown, a 404 is issued.
    """
    cset = req.dbsession.query(models.Changeset).\
        filter(models.Changeset.git_sha1.startswith(req.matchdict['sha1'])).\
        first()

    if not cset:
        raise HTTPNotFound()

    return cset.to_dict()

sha1_map = get_service(name='sha1-map', path='/sha1-map',
    description='Obtain a mapping of Mercurial to Git SHA-1.')

@sha1_map.get(renderer='string')
def get_sha1_map(req):
    """Obtain a mapping of Mercurial node SHA-1 to Git commit SHA-1.

    Returns a plaintext, newline delimited document. All lines consist
    of fields delimited by the tab character. The first line defines
    the columns.
    """
    csets = req.dbsession.query(models.Changeset.node,
                models.Changeset.git_sha1).\
            filter(models.Changeset.git_sha1!=None).\
            order_by(models.Changeset.node).\
            all()

    lines = ['hg git\n']

    for node, git_sha in csets:
        lines.append('%s %s\n' % (node, git_sha))

    return ''.join(lines)


bug = get_service(name='bug', path='/bug/{bug}',
    description='Obtain details about a bug.')

@bug.get()
def get_bug(req):
    """Obtain information about a bug.

    Look up information about a given bug.

    Currently, this returns info about changesets whose commit message
    references the bug.
    """
    bug = req.dbsession.query(models.Bug).\
        filter(models.Bug.bug_number==req.matchdict['bug']).\
        first()

    if not bug:
        raise HTTPNotFound()

    return {
        'changesets': [c.changeset.to_dict() for c in bug.changesets],
    }


spore = get_service('spore', path='/spore', description='SPORE descriptor.')
@spore.get()
def get_spore(req):
    """Obtain a SPORE descriptor for this web service.

    The SPORE descriptor can be used to automatically generate client
    libraries.
    """
    services = get_services()
    return generate_spore_description(services, 'Mozilla tree utopia',
        req.application_url, '1.0')


def main(*args, **settings):
    state = get_global_state()

    def dbsession(request):
        if not hasattr(request, '_db_session'):
            request._db_session = state.get_session()

        return request._db_session

    config = Configurator(route_prefix='/api/')
    config.include('cornice')

    config.add_request_method(dbsession, reify=True)

    config.scan()

    return config.make_wsgi_app()

application = main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.