Kirill Simonov avatar Kirill Simonov committed 5607b02

Merge `/:json` and `/:jsonex` formatters; use `/:jsonex` layout to
avoid CSRF issue.

Comments (0)

Files changed (6)

src/htsql/fmt/__init__.py

 
 from .format import FindRenderer
 from .json import JSONRenderer
-from .jsonex import JSONExRenderer
 from .spreadsheet import CSVRenderer
 from .html import HTMLRenderer
 from .text import TextRenderer
 class FindStandardRenderer(FindRenderer):
 
     def get_renderers(self):
-        return ([CSVRenderer, JSONRenderer, JSONExRenderer,
-                 HTMLRenderer, TextRenderer]
+        return ([CSVRenderer, JSONRenderer, HTMLRenderer, TextRenderer]
                 + super(FindStandardRenderer, self).get_renderers())
 
 

src/htsql/fmt/json.py

 """
 
 
-from ..adapter import adapts
+from ..adapter import Adapter, adapts
 from .format import Format, Formatter, Renderer
 from ..domain import (Domain, BooleanDomain, NumberDomain, FloatDomain,
                       StringDomain, EnumDomain, DateDomain)
 
     # Note: see `http://www.ietf.org/rfc/rfc4627.txt`.
     name = 'application/json'
-    aliases = ['json']
+    aliases = ['json', 'jsonex']
 
     def render(self, product):
         status = self.generate_status(product)
                   for element in product.profile.segment.elements]
         domains = [element.domain
                    for element in product.profile.segment.elements]
+        domain_titles = [escape(entitle_domain(domain)) for domain in domains]
         tool = JSONFormatter(self)
         formats = [Format(self, domain, tool) for domain in domains]
-        yield "[\n"
-        items = titles
+        yield "{\n"
+        yield "  \"meta\": [\n"
+        items = []
+        for title, domain_title in zip(titles, domain_titles):
+            item = "\"title\": %s, \"domain\": %s" % (title, domain_title)
+            items.append(item)
+        if items:
+            for item in items[:-1]:
+                yield "    {%s},\n" % item
+            yield "    {%s}\n" % items[-1]
+        yield "  ],\n"
+        yield "  \"data\": [\n"
+        items = None
         for record in product:
-            yield "  [%s],\n" % ", ".join(items)
+            if items is not None:
+                yield "    [%s],\n" % ", ".join(items)
             items = [format(value)
                      for format, value in zip(formats, record)]
-        yield "  [%s]\n" % ", ".join(items)
-        yield "]\n"
+        if items is not None:
+            yield "    [%s]\n" % ", ".join(items)
+        yield "  ]\n"
+        yield "}\n"
 
 
 class JSONFormatter(Formatter):
         return escape(str(value))
 
 
+class EntitleDomain(Adapter):
+
+    adapts(Domain)
+    name = "unknown"
+
+    def __init__(self, domain):
+        self.domain = domain
+
+    def __call__(self):
+        return self.name
+
+
+class EntitleBoolean(EntitleDomain):
+
+    adapts(BooleanDomain)
+    name = "boolean"
+
+
+class EntitleNumber(EntitleDomain):
+
+    adapts(NumberDomain)
+    name = "number"
+
+
+class EntitleString(EntitleDomain):
+
+    adapts(StringDomain)
+    name = "string"
+
+
+class EntitleEnum(EntitleDomain):
+
+    adapts(EnumDomain)
+    name = "enum"
+
+
+class EntitleDate(EntitleDomain):
+
+    adapts(DateDomain)
+    name = "date"
+
+
 class Escape(object):
 
     escape_pattern = r"""[\x00-\x1F\\/"]"""
 escape = Escape.escape
 
 
+def entitle_domain(domain):
+    entitle = EntitleDomain(domain)
+    return entitle()
+
+

src/htsql/fmt/jsonex.py

-#
-# Copyright (c) 2006-2011, Prometheus Research, LLC
-# Authors: Clark C. Evans <cce@clarkevans.com>,
-#          Kirill Simonov <xi@resolvent.net>
-#
-
-
-"""
-:mod:`htsql.fmt.jsonex`
-=======================
-
-This module implements the JSON renderer with extra metadata.
-"""
-
-
-from ..adapter import Adapter, adapts
-from ..domain import (Domain, BooleanDomain, NumberDomain, StringDomain,
-                      EnumDomain, DateDomain)
-from .format import Format
-from .json import JSONRenderer, JSONFormatter, escape
-from .entitle import entitle
-
-
-class JSONExRenderer(JSONRenderer):
-
-    name = 'jsonex'
-    aliases = []
-
-    def generate_body(self, product):
-        titles = [escape(entitle(element.binding))
-                  for element in product.profile.segment.elements]
-        domains = [element.domain
-                   for element in product.profile.segment.elements]
-        domain_titles = [escape(entitle_domain(domain)) for domain in domains]
-        tool = JSONFormatter(self)
-        formats = [Format(self, domain, tool) for domain in domains]
-        yield "{\n"
-        yield "  \"meta\": [\n"
-        items = []
-        for title, domain_title in zip(titles, domain_titles):
-            item = "\"title\": %s, \"domain\": %s" % (title, domain_title)
-            items.append(item)
-        if items:
-            for item in items[:-1]:
-                yield "    {%s},\n" % item
-            yield "    {%s}\n" % items[-1]
-        yield "  ],\n"
-        yield "  \"data\": [\n"
-        items = None
-        for record in product:
-            if items is not None:
-                yield "    [%s],\n" % ", ".join(items)
-            items = [format(value)
-                     for format, value in zip(formats, record)]
-        if items is not None:
-            yield "    [%s]\n" % ", ".join(items)
-        yield "  ]\n"
-        yield "}\n"
-
-
-class EntitleDomain(Adapter):
-
-    adapts(Domain)
-    name = "unknown"
-
-    def __init__(self, domain):
-        self.domain = domain
-
-    def __call__(self):
-        return self.name
-
-
-class EntitleBoolean(EntitleDomain):
-
-    adapts(BooleanDomain)
-    name = "boolean"
-
-
-class EntitleNumber(EntitleDomain):
-
-    adapts(NumberDomain)
-    name = "number"
-
-
-class EntitleString(EntitleDomain):
-
-    adapts(StringDomain)
-    name = "string"
-
-
-class EntitleEnum(EntitleDomain):
-
-    adapts(EnumDomain)
-    name = "enum"
-
-
-class EntitleDate(EntitleDomain):
-
-    adapts(DateDomain)
-    name = "date"
-
-
-def entitle_domain(domain):
-    entitle = EntitleDomain(domain)
-    return entitle()
-
-

test/input/format.yaml

 - title: Supported Output Formats
   tests:
   - uri: /school/:json
-  - uri: /school/:jsonex
   - uri: /school/:csv
   - uri: /school/:txt
   - uri: /school/:html
             /:json
     - uri: /(school :as 'List of Schools')
             {name :as Name, count(department) :as '# of Departments'}
-            /:jsonex
-    - uri: /(school :as 'List of Schools')
-            {name :as Name, count(department) :as '# of Departments'}
             /:csv
     - uri: /(school :as 'List of Schools')
             {name :as Name, count(department) :as '# of Departments'}
   - uri: /{null(),true(),false(),60,2.125,271828e-5,'HTSQL',date('2010-04-15')}
           /:json
   - uri: /{null(),true(),false(),60,2.125,271828e-5,'HTSQL',date('2010-04-15')}
-          /:jsonex
-  - uri: /{null(),true(),false(),60,2.125,271828e-5,'HTSQL',date('2010-04-15')}
           /:csv
   - uri: /{null(),true(),false(),60,2.125,271828e-5,'HTSQL',date('2010-04-15')}
           /:txt
 - title: No Rows
   tests:
   - uri: /school?false()/:json
-  - uri: /school?false()/:jsonex
   - uri: /school?false()/:csv
   - uri: /school?false()/:txt
   - uri: /school?false()/:html

test/output/pgsql.yaml

           - [Content-Type, application/json]
           - [Content-Disposition, attachment; filename="(school).json"]
           body: |
-            [
-              ["code", "name"],
-              ["art", "School of Art and Design"],
-              ["bus", "School of Business"],
-              ["edu", "College of Education"],
-              ["eng", "School of Engineering"],
-              ["la", "School of Arts and Humanities"],
-              ["mus", "School of Music & Dance"],
-              ["ns", "School of Natural Sciences"],
-              ["ph", "Public Honorariums"],
-              ["sc", "School of Continuing Studies"]
-            ]
+            {
+              "meta": [
+                {"title": "code", "domain": "string"},
+                {"title": "name", "domain": "string"}
+              ],
+              "data": [
+                ["art", "School of Art and Design"],
+                ["bus", "School of Business"],
+                ["edu", "College of Education"],
+                ["eng", "School of Engineering"],
+                ["la", "School of Arts and Humanities"],
+                ["mus", "School of Music & Dance"],
+                ["ns", "School of Natural Sciences"],
+                ["ph", "Public Honorariums"],
+                ["sc", "School of Continuing Studies"]
+              ]
+            }
         - uri: /course{department{code, name},number,title+} ?department.school='bus'/:csv
           status: 200 OK
           headers:
           - [Content-Type, application/json]
           - [Content-Disposition, attachment; filename="(school).json"]
           body: |
-            [
-              ["code", "name"],
-              ["art", "School of Art and Design"],
-              ["bus", "School of Business"],
-              ["edu", "College of Education"],
-              ["eng", "School of Engineering"],
-              ["la", "School of Arts and Humanities"],
-              ["mus", "School of Music & Dance"],
-              ["ns", "School of Natural Sciences"],
-              ["ph", "Public Honorariums"],
-              ["sc", "School of Continuing Studies"]
-            ]
-        - uri: /school/:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, attachment; filename="(school).json"]
-          body: |
             {
               "meta": [
                 {"title": "code", "domain": "string"},
           - [Content-Type, application/json]
           - [Content-Disposition, attachment; filename="(school).json"]
           body: |
-            [
-              ["code", "name"],
-              ["art", "School of Art and Design"],
-              ["bus", "School of Business"],
-              ["edu", "College of Education"],
-              ["eng", "School of Engineering"],
-              ["la", "School of Arts and Humanities"],
-              ["mus", "School of Music & Dance"],
-              ["ns", "School of Natural Sciences"],
-              ["ph", "Public Honorariums"],
-              ["sc", "School of Continuing Studies"]
-            ]
+            {
+              "meta": [
+                {"title": "code", "domain": "string"},
+                {"title": "name", "domain": "string"}
+              ],
+              "data": [
+                ["art", "School of Art and Design"],
+                ["bus", "School of Business"],
+                ["edu", "College of Education"],
+                ["eng", "School of Engineering"],
+                ["la", "School of Arts and Humanities"],
+                ["mus", "School of Music & Dance"],
+                ["ns", "School of Natural Sciences"],
+                ["ph", "Public Honorariums"],
+                ["sc", "School of Continuing Studies"]
+              ]
+            }
         - uri: /school
           status: 200 OK
           headers:
           - [Content-Disposition, 'attachment; filename="((school:as(''List of Schools'')){name:as(Name),count(department):as(''#
               of Departments'')}).json"']
           body: |
-            [
-              ["Name", "# of Departments"],
-              ["School of Art and Design", 2],
-              ["School of Business", 3],
-              ["College of Education", 2],
-              ["School of Engineering", 4],
-              ["School of Arts and Humanities", 5],
-              ["School of Music & Dance", 4],
-              ["School of Natural Sciences", 4],
-              ["Public Honorariums", 0],
-              ["School of Continuing Studies", 0]
-            ]
-        - uri: /(school :as 'List of Schools') {name :as Name, count(department) :as
-            '# of Departments'} /:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, 'attachment; filename="((school:as(''List of Schools'')){name:as(Name),count(department):as(''#
-              of Departments'')}).json"']
-          body: |
             {
               "meta": [
                 {"title": "Name", "domain": "string"},
           - [Content-Type, application/json]
           - [Content-Disposition, 'attachment; filename="({null(),true(),false(),60,2.125,271828e-5,''HTSQL'',date(''2010-04-15'')}).json"']
           body: |
-            [
-              ["null()", "true()", "false()", "60", "2.125", "271828e-5", "'HTSQL'", "date('2010-04-15')"],
-              [null, true, false, 60, 2.125, 2.71828, "HTSQL", "2010-04-15"]
-            ]
-        - uri: /{null(),true(),false(),60,2.125,271828e-5,'HTSQL',date('2010-04-15')}
-            /:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, 'attachment; filename="({null(),true(),false(),60,2.125,271828e-5,''HTSQL'',date(''2010-04-15'')}).json"']
-          body: |
             {
               "meta": [
                 {"title": "null()", "domain": "string"},
           - [Content-Type, application/json]
           - [Content-Disposition, 'attachment; filename="(school?false()).json"']
           body: |
-            [
-              ["code", "name"]
-            ]
-        - uri: /school?false()/:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, 'attachment; filename="(school?false()).json"']
-          body: |
             {
               "meta": [
                 {"title": "code", "domain": "string"},

test/output/sqlite.yaml

           - [Content-Type, application/json]
           - [Content-Disposition, attachment; filename="(school).json"]
           body: |
-            [
-              ["code", "name"],
-              ["art", "School of Art and Design"],
-              ["bus", "School of Business"],
-              ["edu", "College of Education"],
-              ["eng", "School of Engineering"],
-              ["la", "School of Arts and Humanities"],
-              ["mus", "School of Music & Dance"],
-              ["ns", "School of Natural Sciences"],
-              ["ph", "Public Honorariums"],
-              ["sc", "School of Continuing Studies"]
-            ]
+            {
+              "meta": [
+                {"title": "code", "domain": "string"},
+                {"title": "name", "domain": "string"}
+              ],
+              "data": [
+                ["art", "School of Art and Design"],
+                ["bus", "School of Business"],
+                ["edu", "College of Education"],
+                ["eng", "School of Engineering"],
+                ["la", "School of Arts and Humanities"],
+                ["mus", "School of Music & Dance"],
+                ["ns", "School of Natural Sciences"],
+                ["ph", "Public Honorariums"],
+                ["sc", "School of Continuing Studies"]
+              ]
+            }
         - uri: /course{department{code, name},number,title+} ?department.school='bus'/:csv
           status: 200 OK
           headers:
           - [Content-Type, application/json]
           - [Content-Disposition, attachment; filename="(school).json"]
           body: |
-            [
-              ["code", "name"],
-              ["art", "School of Art and Design"],
-              ["bus", "School of Business"],
-              ["edu", "College of Education"],
-              ["eng", "School of Engineering"],
-              ["la", "School of Arts and Humanities"],
-              ["mus", "School of Music & Dance"],
-              ["ns", "School of Natural Sciences"],
-              ["ph", "Public Honorariums"],
-              ["sc", "School of Continuing Studies"]
-            ]
-        - uri: /school/:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, attachment; filename="(school).json"]
-          body: |
             {
               "meta": [
                 {"title": "code", "domain": "string"},
           - [Content-Type, application/json]
           - [Content-Disposition, attachment; filename="(school).json"]
           body: |
-            [
-              ["code", "name"],
-              ["art", "School of Art and Design"],
-              ["bus", "School of Business"],
-              ["edu", "College of Education"],
-              ["eng", "School of Engineering"],
-              ["la", "School of Arts and Humanities"],
-              ["mus", "School of Music & Dance"],
-              ["ns", "School of Natural Sciences"],
-              ["ph", "Public Honorariums"],
-              ["sc", "School of Continuing Studies"]
-            ]
+            {
+              "meta": [
+                {"title": "code", "domain": "string"},
+                {"title": "name", "domain": "string"}
+              ],
+              "data": [
+                ["art", "School of Art and Design"],
+                ["bus", "School of Business"],
+                ["edu", "College of Education"],
+                ["eng", "School of Engineering"],
+                ["la", "School of Arts and Humanities"],
+                ["mus", "School of Music & Dance"],
+                ["ns", "School of Natural Sciences"],
+                ["ph", "Public Honorariums"],
+                ["sc", "School of Continuing Studies"]
+              ]
+            }
         - uri: /school
           status: 200 OK
           headers:
           - [Content-Disposition, 'attachment; filename="((school:as(''List of Schools'')){name:as(Name),count(department):as(''#
               of Departments'')}).json"']
           body: |
-            [
-              ["Name", "# of Departments"],
-              ["School of Art and Design", 2],
-              ["School of Business", 3],
-              ["College of Education", 2],
-              ["School of Engineering", 4],
-              ["School of Arts and Humanities", 5],
-              ["School of Music & Dance", 4],
-              ["School of Natural Sciences", 4],
-              ["Public Honorariums", 0],
-              ["School of Continuing Studies", 0]
-            ]
-        - uri: /(school :as 'List of Schools') {name :as Name, count(department) :as
-            '# of Departments'} /:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, 'attachment; filename="((school:as(''List of Schools'')){name:as(Name),count(department):as(''#
-              of Departments'')}).json"']
-          body: |
             {
               "meta": [
                 {"title": "Name", "domain": "string"},
           - [Content-Type, application/json]
           - [Content-Disposition, 'attachment; filename="({null(),true(),false(),60,2.125,271828e-5,''HTSQL'',date(''2010-04-15'')}).json"']
           body: |
-            [
-              ["null()", "true()", "false()", "60", "2.125", "271828e-5", "'HTSQL'", "date('2010-04-15')"],
-              [null, true, false, 60, 2.125, 2.71828, "HTSQL", "2010-04-15"]
-            ]
-        - uri: /{null(),true(),false(),60,2.125,271828e-5,'HTSQL',date('2010-04-15')}
-            /:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, 'attachment; filename="({null(),true(),false(),60,2.125,271828e-5,''HTSQL'',date(''2010-04-15'')}).json"']
-          body: |
             {
               "meta": [
                 {"title": "null()", "domain": "string"},
           - [Content-Type, application/json]
           - [Content-Disposition, 'attachment; filename="(school?false()).json"']
           body: |
-            [
-              ["code", "name"]
-            ]
-        - uri: /school?false()/:jsonex
-          status: 200 OK
-          headers:
-          - [Content-Type, application/json]
-          - [Content-Disposition, 'attachment; filename="(school?false()).json"']
-          body: |
             {
               "meta": [
                 {"title": "code", "domain": "string"},
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.