Permalink
Browse files

Merge pull request #489 from AdrianGaudebert/734087-job-service

Fixes bug 734087 - Moved the job SQL query to a new middleware service.
  • Loading branch information...
2 parents c7d1553 + c2ffda1 commit 4a8eb0cb506e78cad2998521f3f4a4a1556f3556 @adngdb adngdb committed Apr 11, 2012
View
@@ -17,6 +17,7 @@ New-style, documented services
* `/crashes/paireduuid <#crashes-paireduuid>`_
* `/crashes/signatures <#crashes-signatures>`_
* `extensions/ <#id7>`_
+* `job/ <#job>`_
* `priorityjobs/ <#priorityjobs>`_
* products/
* `products/builds/ <#products-builds>`_
@@ -455,6 +456,68 @@ Return a list of extensions::
.. ############################################################################
+ Job API
+ ############################################################################
+
+Job
+---
+
+Handle the jobs queue for crash reports processing.
+
+API specifications
+^^^^^^^^^^^^^^^^^^
+
++----------------+--------------------------------------------------------------------------------+
+| HTTP method | GET |
++----------------+--------------------------------------------------------------------------------+
+| URL schema | /job/(parameters) |
++----------------+--------------------------------------------------------------------------------+
+| Full URL | /job/uuid/(uuid)/ |
++----------------+--------------------------------------------------------------------------------+
+| Example | http://socorro-api/bpapi/job/uuid/e8820616-1462-49b6-9784-e99a32120201/ |
++----------------+--------------------------------------------------------------------------------+
+
+Mandatory parameters
+^^^^^^^^^^^^^^^^^^^^
+
++----------------+------------------+---------------+-------------------------+
+| Name | Type of value | Default value | Description |
++================+==================+===============+=========================+
+| uuid | String | None | Unique identifier of the|
+| | | | crash report to find. |
++----------------+------------------+---------------+-------------------------+
+
+Optional parameters
+^^^^^^^^^^^^^^^^^^^
+
+None
+
+Return value
+^^^^^^^^^^^^
+
+With a GET HTTP method, the service will return data in the following
+form::
+
+ {
+ "hits": [
+ {
+ "id": 1,
+ "pathname": "",
+ "uuid": "e8820616-1462-49b6-9784-e99a32120201",
+ "owner": 3,
+ "priority": 0,
+ "queueddatetime": "2012-02-29T01:23:45+00:00",
+ "starteddatetime": "2012-02-29T01:23:45+00:00",
+ "completeddatetime": "2012-02-29T01:23:45+00:00",
+ "success": True,
+ "message": "Hello"
+ }
+ ],
+ "total": 1
+ }
+
+
+.. ############################################################################
Priorityjobs API
############################################################################
@@ -135,6 +135,7 @@ import socorro.middleware.crashes_paireduuid_service as crashes_paireduuid
import socorro.middleware.products_versions_service as products_versions
import socorro.middleware.priorityjobs_service as priorityjobs
import socorro.middleware.crashes_frequency_service as crashes_frequency
+import socorro.middleware.job_service as job
servicesList = cm.Option()
@@ -166,6 +167,7 @@ servicesList.default = [
products_versions.ProductsVersions,
priorityjobs.Priorityjobs,
crashes_frequency.CrashesFrequency,
+ job.Job,
]
crashBaseUrl = cm.Option()
@@ -0,0 +1,77 @@
+import datetime
+import logging
+import psycopg2
+
+from socorro.external.postgresql.base import PostgreSQLBase
+from socorro.lib import datetimeutil, external_common
+
+import socorro.database.database as db
+
+logger = logging.getLogger("webapi")
+
+
+class MissingOrBadArgumentError(Exception):
+ pass
+
+
+class Job(PostgreSQLBase):
+ """Implement the /job service with PostgreSQL. """
+
+ def get(self, **kwargs):
+ """Return a job in the job queue. """
+ filters = [
+ ("uuid", None, "str"),
+ ]
+ params = external_common.parse_arguments(filters, kwargs)
+
+ if not params.uuid:
+ raise MissingOrBadArgumentError(
+ "Mandatory parameter 'uuid' is missing or empty")
+
+ fields = [
+ "id",
+ "pathname",
+ "uuid",
+ "owner",
+ "priority",
+ "queueddatetime",
+ "starteddatetime",
+ "completeddatetime",
+ "success",
+ "message"
+ ]
+ sql = """
+ /* socorro.external.postgresql.job.Job.get */
+ SELECT %s FROM jobs WHERE uuid=%%(uuid)s
+ """ % ", ".join(fields)
+
+ json_result = {
+ "total": 0,
+ "hits": []
+ }
+
+ connection = None
+ try:
+ # Creating the connection to the DB
+ connection = self.database.connection()
+ cur = connection.cursor()
+ results = db.execute(cur, sql, params)
+ except psycopg2.Error:
+ logger.error("Failed retrieving jobs data from PostgreSQL",
+ exc_info=True)
+ else:
+ for job in results:
+ row = dict(zip(fields, job))
+
+ # Make sure all dates are turned into strings
+ for i in row:
+ if isinstance(row[i], datetime.datetime):
+ row[i] = datetimeutil.date_to_string(row[i])
+
+ json_result["hits"].append(row)
+ json_result["total"] = len(json_result["hits"])
+ finally:
+ if connection:
+ connection.close()
+
+ return json_result
@@ -0,0 +1,23 @@
+import logging
+
+from socorro.middleware.service import DataAPIService
+
+logger = logging.getLogger("webapi")
+
+
+class Job(DataAPIService):
+ """Return a single job from its UUID. """
+
+ service_name = "job"
+ uri = "/job/(.*)"
+
+ def __init__(self, config):
+ super(Job, self).__init__(config)
+ logger.debug('Job service __init__')
+
+ def get(self, *args):
+ """Called when a get HTTP request is executed to /job. """
+ params = self.parse_query_string(args[0])
+ module = self.get_module(params)
+ impl = module.Job(config=self.context)
+ return impl.get(**params)
@@ -0,0 +1,120 @@
+from socorro.external.postgresql import job
+import socorro.unittest.testlib.util as testutil
+
+from unittestbase import PostgreSQLTestCase
+
+
+#------------------------------------------------------------------------------
+def setup_module():
+ testutil.nosePrintModule(__file__)
+
+
+#==============================================================================
+class IntegrationTestJob(PostgreSQLTestCase):
+ """Test socorro.external.postgresql.Job.Job class. """
+
+ #--------------------------------------------------------------------------
+ def setUp(self):
+ """Set up this test class by populating the reports table with fake
+ data. """
+ super(IntegrationTestJob, self).setUp()
+
+ cursor = self.connection.cursor()
+
+ # Create tables
+ cursor.execute("""
+ CREATE TABLE jobs
+ (
+ id serial NOT NULL PRIMARY KEY,
+ pathname character varying(1024) NOT NULL,
+ uuid varchar(50) NOT NULL UNIQUE,
+ owner integer,
+ priority integer DEFAULT 0,
+ queueddatetime timestamp with time zone,
+ starteddatetime timestamp with time zone,
+ completeddatetime timestamp with time zone,
+ success boolean,
+ message text
+ );
+ """)
+
+ # Insert data
+ cursor.execute("""
+ INSERT INTO jobs VALUES
+ (
+ 1, '', 'a1', 2, 0,
+ '2012-02-29T01:23:45.000000+00:00',
+ '2012-02-29T01:23:45.000000+00:00',
+ '2012-02-29T01:23:45.000000+00:00',
+ 't', ''
+ ),
+ (
+ 2, '', 'a2', 2, 0,
+ '2012-02-29T01:23:45.000000+00:00',
+ '2012-02-29T01:23:45.000000+00:00',
+ '2012-02-29T01:23:45.000000+00:00',
+ 't', ''
+ )
+ """)
+
+ self.connection.commit()
+
+ #--------------------------------------------------------------------------
+ def tearDown(self):
+ """Clean up the database, delete tables and functions. """
+ cursor = self.connection.cursor()
+ cursor.execute("""
+ DROP TABLE jobs;
+ """)
+ self.connection.commit()
+ super(IntegrationTestJob, self).tearDown()
+
+ #--------------------------------------------------------------------------
+ def test_get(self):
+ jobs = job.Job(config=self.config)
+
+ #......................................................................
+ # Test 1: a valid job
+ params = {
+ "uuid": "a1"
+ }
+ res = jobs.get(**params)
+ res_expected = {
+ "hits": [
+ {
+ "id": 1,
+ "pathname": "",
+ "uuid": "a1",
+ "owner": 2,
+ "priority": 0,
+ "queueddatetime": "2012-02-29T01:23:45.000000+00:00",
+ "starteddatetime": "2012-02-29T01:23:45.000000+00:00",
+ "completeddatetime": "2012-02-29T01:23:45.000000+00:00",
+ "success": True,
+ "message": ""
+ }
+ ],
+ "total": 1
+ }
+
+ self.assertEqual(res, res_expected)
+
+ #......................................................................
+ # Test 2: an invalid job
+ params = {
+ "uuid": "b2"
+ }
+ res = jobs.get(**params)
+ res_expected = {
+ "hits": [],
+ "total": 0
+ }
+
+ self.assertEqual(res, res_expected)
+
+ #......................................................................
+ # Test 3: missing argument
+ params = {}
+ self.assertRaises(job.MissingOrBadArgumentError,
+ jobs.get,
+ **params)
@@ -1,24 +1,38 @@
<?php
-/**
- *
- */
-class Job_Model extends Model {
+class Job_Model extends Model
+{
/**
- * Fetch a single job by UUID
- *
- * @param string UUID by which to look up job
- * @return object job data
+ * The Web Service class.
*/
- public function getByUUID($uuid) {
+ protected $service = null;
- $job = $this->db->query(
- '/* soc.web job.byuuid */ SELECT * FROM jobs WHERE uuid=?', $uuid
- )->current();
+ public function __construct()
+ {
+ parent::__construct();
- if (!$job) return FALSE;
+ $config = array();
+ $credentials = Kohana::config('webserviceclient.basic_auth');
+ if ($credentials) {
+ $config['basic_auth'] = $credentials;
+ }
- return $job;
+ $this->service = new Web_Service($config);
}
+ /**
+ * Fetch a single job by UUID
+ *
+ * @param string UUID by which to look up job
+ * @return object job data
+ */
+ public function getByUUID($uuid)
+ {
+ $uri = Kohana::config('webserviceclient.socorro_hostname') . '/job/uuid/' . urlencode($uuid);
+ $res = $this->service->get($uri);
+ if (!$res || !isset($res->total) || $res->total <= 0) {
+ return false;
+ }
+ return $res->hits[0];
+ }
}

0 comments on commit 4a8eb0c

Please sign in to comment.