:green_heart: Fix warning.

:green_heart: Fix warning.

import module namespace components = "http://28.io/modules/xbrl/components";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";
import module namespace sec-networks = "http://28.io/modules/xbrl/profiles/sec/networks";
import module namespace fiscal-core = "http://28.io/modules/xbrl/profiles/sec/fiscal/core";

import module namespace rules = "http://28.io/modules/xbrl/rules";

import module namespace config = "http://apps.28.io/config";
import module namespace session = "http://apps.28.io/session";
import module namespace api = "http://apps.28.io/api";
import module namespace request = "http://www.28msec.com/modules/http-request";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token              as string? external;
declare  %rest:env                              variable $request-uri        as string  external;
declare  %rest:case-insensitive                 variable $format             as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode         as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker             as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear         as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod       as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType   as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $filingKind         as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $eid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $networkIdentifier  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $concept            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $disclosure         as string* external;
declare  %rest:case-insensitive                 variable $validate           as boolean external := false;
declare  %rest:case-insensitive                 variable $eliminate          as boolean external := false;
declare  %rest:case-insensitive                 variable $merge              as boolean external := false;
declare  %rest:case-insensitive                 variable $elimination-threshold as integer external := 0;
declare  %rest:case-insensitive %rest:distinct  variable $reportElement      as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $label              as string* external;
declare  %rest:case-insensitive                 variable $additional-rules   as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $role               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $hypercube          as string* external;
declare  %rest:case-insensitive                 variable $profile-name       as string  external := $config:profile-name;
declare  %rest:case-insensitive                 variable $language           as string  external := "en-US";
declare  %rest:case-insensitive                 variable $debug              as boolean external := false;
declare  %rest:case-insensitive                 variable $override           as boolean external := false;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $tag as string* := api:preprocess-tags($tag)
let $reportElement := ($reportElement, $concept)
let $networkIdentifier := distinct-values(($networkIdentifier, $role))

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic,
  (),
  (),
  (),
  ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $archives as object* := multiplexer:filings(
  $profile-name,
  $entities,
  $fiscalPeriod,
  $fiscalYear,
  $filingKind,
  $aid)

let $archives-not-found as boolean :=
  exists(($entities, $fiscalPeriod, $fiscalYear, $filingKind, $aid)) and empty($archives)

let $component as object? :=
    multiplexer:component(
      $profile-name,
      $archives,
      $cid,
      $reportElement,
      $disclosure,
      $networkIdentifier,
      $label[$profile-name ne "sec"],
      $label[$profile-name eq "sec"],
      $merge
    )

let $component-not-found as boolean :=
  exists(($archives, $cid, $reportElement, $disclosure, $networkIdentifier, $label)) and empty($component)

let $selected-hypercube as string? := multiplexer:hypercube(
    $component,
    $hypercube)

let $hypercube-not-found as boolean := exists($hypercube) and empty($selected-hypercube)

let $rules as object* := if(exists($additional-rules)) then rules:rules($additional-rules) else ()

let $filter-override as object? :=
    if($profile-name eq "sec")
    then fiscal-core:filter-override(
        $entities,
        $fiscalYear,
        $fiscalPeriod,
        $fiscalPeriodType,
        $aid)
    else components:filter-override($entities, $aid)

return if(empty($component)) then {
    { status: 404 },
    session:error("component not found", "json")
} else
(: Fact resolution :)
let $definition-model :=
  switch($profile-name)
  case "sec" return sec-networks:standard-definition-models-for-components(
      $component,
      $selected-hypercube,
      {Language: $language}
  )
  case "japan" return components:standard-definition-models-for-components(
      $component,
      $selected-hypercube,
      {
        Language: $language,
        HideDimensions: [ "fsa:Submitted", "fsa:FiscalPeriod", "fsa:FiscalPeriodType", "fsa:FiscalYear" ]
      }
  )
  default return components:standard-definition-models-for-components(
    $component,
    $selected-hypercube,
    {Language: $language}
  )

let $spreadsheet as object? :=
    components:spreadsheet(
        $component,
        $selected-hypercube,
        {|
            { FilterOverride: $filter-override}[$override],
            {
                FlattenRows: true,
                Eliminate: $eliminate,
                EliminationThreshold: double($elimination-threshold) div 100,
                Validate: $validate,
                DefinitionModel: $definition-model,
                Language: $language,
                Debug: $debug
            },
            if(exists($rules))
            then { Rules : [ $rules ] }
            else ()
        |})

let $results :=
        if($format eq "html")
        then {
          {
            status: 302,
            content-type: "text/html",
            serialization: { "method" : "html" },
            headers: { "Location" : "http://rendering.secxbrl.info/#?url=" || encode-for-uri("http://" || request:server-name() || ":" || request:server-port() || replace(request:uri(), "format=html", "format=json")) }
          },
          <html/>
        }
        else
        {
            { serialization: { method: "json", indent : true } },
            $spreadsheet
        }
return switch(true)
       case $entities-not-found return api:not-found("entity")
       case $archives-not-found return api:not-found("archive")
       case $component-not-found return api:not-found("component")
       case $hypercube-not-found return api:not-found("hypercube")
       default return api:check-and-return-results($token, $results, $format)

import module namespace components = "http://28.io/modules/xbrl/components";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";
import module namespace sec-networks = "http://28.io/modules/xbrl/profiles/sec/networks";
import module namespace fiscal-core = "http://28.io/modules/xbrl/profiles/sec/fiscal/core";

import module namespace rules = "http://28.io/modules/xbrl/rules";

import module namespace config = "http://apps.28.io/config";
import module namespace session = "http://apps.28.io/session";
import module namespace api = "http://apps.28.io/api";
import module namespace request = "http://www.28msec.com/modules/http-request";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token              as string? external;
declare  %rest:env                              variable $request-uri        as string  external;
declare  %rest:case-insensitive                 variable $format             as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $cik                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode         as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker             as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear         as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod       as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriodType   as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $filingKind         as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $eid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $aid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $networkIdentifier  as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cid                as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $concept            as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $disclosure         as string* external;
declare  %rest:case-insensitive                 variable $validate           as boolean external := false;
declare  %rest:case-insensitive                 variable $eliminate          as boolean external := false;
declare  %rest:case-insensitive                 variable $merge              as boolean external := false;
declare  %rest:case-insensitive                 variable $elimination-threshold as integer external := 0;
declare  %rest:case-insensitive %rest:distinct  variable $reportElement      as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $label              as string* external;
declare  %rest:case-insensitive                 variable $additional-rules   as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $role               as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $hypercube          as string* external;
declare  %rest:case-insensitive                 variable $profile-name       as string  external := $config:profile-name;
declare  %rest:case-insensitive                 variable $language           as string  external := "en-US";
declare  %rest:case-insensitive                 variable $debug              as boolean external := false;
declare  %rest:case-insensitive                 variable $override           as boolean external := false;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $tag as string* := api:preprocess-tags($tag)
let $reportElement := ($reportElement, $concept)
let $networkIdentifier := distinct-values(($networkIdentifier, $role))

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic,
  (),
  (),
  (),
  ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $archives as object* := multiplexer:filings(
  $profile-name,
  $entities,
  $fiscalPeriod,
  $fiscalYear,
  $filingKind,
  $aid)

let $archives-not-found as boolean :=
  exists(($entities, $fiscalPeriod, $fiscalYear, $filingKind, $aid)) and empty($archives)

let $component as object? :=
    multiplexer:component(
      $profile-name,
      $archives,
      $cid,
      $reportElement,
      $disclosure,
      $networkIdentifier,
      $label[$profile-name ne "sec"],
      $label[$profile-name eq "sec"],
      $merge
    )

let $component-not-found as boolean :=
  exists(($archives, $cid, $reportElement, $disclosure, $networkIdentifier, $label)) and empty($component)

let $selected-hypercube as string? := multiplexer:hypercube(
    $component,
    $hypercube)

let $hypercube-not-found as boolean := exists($hypercube) and empty($selected-hypercube)

let $rules as object* := if(exists($additional-rules)) then rules:rules($additional-rules) else ()

let $filter-override as object? :=
    if($profile-name eq "sec")
    then fiscal-core:filter-override(
        $entities,
        $fiscalYear,
        $fiscalPeriod,
        $fiscalPeriodType,
        $aid)
    else components:filter-override($entities, $aid)

return if(empty($component)) then {
    { status: 404 },
    session:error("component not found", "json")
} else
(: Fact resolution :)
let $definition-model :=
  switch($profile-name)
  case "sec" return sec-networks:standard-definition-models-for-components(
      $component,
      $selected-hypercube,
      {Language: $language}
  )
  case "japan" return components:standard-definition-models-for-components(
      $component,
      $selected-hypercube,
      {
        Language: $language,
        HideDimensions: [ "fsa:Submitted", "fsa:FiscalPeriod", "fsa:FiscalPeriodType", "fsa:FiscalYear" ]
      }
  )
  default return components:standard-definition-models-for-components(
    $component,
    $selected-hypercube,
    {Language: $language}
  )

let $spreadsheet as object? :=
    components:spreadsheet(
        $component,
        $selected-hypercube,
        {|
            { FilterOverride: $filter-override}[$override],
            {
                FlattenRows: true,
                Eliminate: $eliminate,
                EliminationThreshold: double($elimination-threshold) div 100,
                Validate: $validate,
                DefinitionModel: $definition-model,
                Language: $language,
                Debug: $debug
            },
            if(exists($rules))
            then { Rules : [ $rules ] }
            else ()
        |})

let $results :=
        if($format eq "html")
        then {
          {
            status: 302,
            content-type: "text/html",
            serialization: { "method" : "html" },
            headers: { "Location" : "http://rendering.secxbrl.info/#?url=" || encode-for-uri("http://" || request:server-name() || ":" || request:server-port() || replace(request:uri(), "format=html", "format=json")) }
          },
          <html/>
        }
        else
        {
            { serialization: { method: "json", indent : true } },
            $spreadsheet
        }
return switch(true)
       case $entities-not-found return api:not-found("entity")
       case $archives-not-found return api:not-found("archive")
       case $component-not-found return api:not-found("component")
       case $hypercube-not-found return api:not-found("hypercube")
       default return api:check-and-return-results($token, $results, $format)


:green_heart: Fix periods test.

:green_heart: Fix periods test.


Added test for generic filings.

Added test for generic filings.


:green_heart: Fix tests.

:green_heart: Fix tests.

import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace test = "http://apps.28.io/test";
import module namespace config = "http://apps.28.io/config";

declare %an:nondeterministic function local:test-facttable($expected as integer, $params as object) as item
{
    let $endpoint := "facts"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].FactTable[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-404($params as object) as item
{
    let $endpoint := "facts"
    let $res as object := test:invoke-raw($endpoint, $params)
    return if($res.status eq 404)
           then true
           else {
              url: test:url($endpoint, $params),
              unexpectedResponse: $res
           }
};

declare %an:sequential function local:check($o as object) as object
{
    if (not(every $k in (keys($o) ! $o.$$) satisfies ($k instance of boolean and $k)))
    then {
            response:status-code(500);
            $o
    } else
            $o
};

declare %an:nondeterministic function local:test-labels() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: [ "fac:Assets", "us-gaap:CashAndCashEquivalentsAtCarryingValue" ],
        report: "FundamentalAccountingConcepts",
        format: "csv",
        ticker: "ko",
        fiscalYear: 2013,
        fiscalPeriod: "Q3",
        labels: true
    }
    let $res as object := test:invoke-raw($endpoint, $params)
    let $actual := $res.body.content
    let $expectedLines := (
        "Concept [Axis],Reporting Entity [Axis],Period [Axis],Unit [Axis],Archive [Axis],Fiscal Year [Axis],Fiscal Period [Axis],Legal Entity [Axis],Fiscal Period Type [Axis],Value,Decimals,Balance,Period Type,Data Type\r\nAssets,COCA COLA CO (21344),\"September 27, 2013\",USD,0000021344-13-000050,2013,Q3,Default Legal Entity [Member],instant,89432000000,-6,Debit,instant,Monetary\r\n\"Cash and Cash Equivalents, at Carrying Value\",COCA COLA CO (21344),\"September 27, 2013\",USD,0000021344-14-000036,2013,Q3,Default Legal Entity [Member],instant,11118000000,-6,Debit,instant,Monetary\r\n"
    )
    return if($res.status eq 200 and (every $line in $expectedLines satisfies contains($actual,$line))) then true else {
        url: test:url($endpoint, $params),
        unexpectedResponse: $res,
        expected: $expectedLines
    }
};

declare %an:nondeterministic function local:test-labels-aids() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: "disc:AdvertisingCostsPolicyTextBlock",
        report: "Disclosures",
        format: "json",
        fiscalYear: 2014,
        fiscalPeriod: "FY",
        labels: true,
        aid: "0000858877-14-000029"
    }
    let $res := test:invoke($endpoint, $params)
    let $actual := [
            for $labels in $res[2].FactTable[].Labels
            return (keys($labels) ! $labels.$$)
        ]
    let $expected := [ "Archive [Axis]", "Fiscal Year [Axis]", "Non Numeric", "Default Legal Entity [Member]", "Unit [Axis]", "Advertising Costs, Policy", "Fiscal Period [Axis]", "Concept [Axis]", "Reporting Entity [Axis]", "Legal Entity [Axis]", "Fiscal Period Type [Axis]", "Period [Axis]", "CISCO SYSTEMS, INC. (858877)", "From July 28, 2013 to July 26, 2014", "Period Type", "Data Type" ]
    let $status as integer := $res[1]
    return test:assert-eq-array($expected, $actual, $status, test:url($endpoint, $params))
};

if(not $config:content[] = ("SEC-big", "SEC-small"))
then true
else
local:check({
    cocacola-latest:
    (: this test will fail and needs to be updated if a newer report has been filed.
       current latest filing: 2014 :)
    local:test-facttable(462, {
        ticker:"ko",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    cocacola-all: local:test-facttable(477, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-all-q3: local:test-facttable(341, {
        ticker:"ko",
        fiscalYear: 2014,
        fiscalPeriod: [ "Q3" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-instant: local:test-facttable(170, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant" ]
    }),
    cocacola-ytd: local:test-facttable(307, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "YTD" ]
    }),
    cocacola-ytd-several-years: local:test-facttable(607, {
        ticker:"ko",
        fiscalYear: [ 2013, 2014 ],
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "YTD" ]
    }),
    cocacola-qtd: local:test-facttable(102, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "Q2" ],
        fiscalPeriodType: [ "QTD" ]
    }),
    cocacolaCSVLabels: local:test-labels(),
    ciscoLabelsByAid: local:test-labels-aids(),
    tickerconcept: local:test-facttable(1, {
        ticker:"ko",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tickerfyfprole: local:test-facttable(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    tagconcept: local:test-facttable(30, { (: AT&T wrongly stamped a filing as FY, but it's now FIXED :-):)
        tag:"DOW30",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tagfyfprole: local:test-facttable(30, {
        tag:"DOW30",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    testNoEntity4Sic: local:test-404({
        concept:"fac:Assets",
        map:"FundamentalAccountingConcepts",
        format:"csv",
        sic:"5052",
        fiscalYear:"2010",
        fiscalPeriod:"Q3"
    }),
    quartersfact: local:test-facttable(0, { (: this fact is not reported in the raw XML filing :)
        aid: "0000051143-13-000007",
        concept: "disc:AssetRetirementObligationsPolicy",
        fiscalPeriod: "Q3",
        fiscalYear: "2013",
        map: "Disclosures"
    }),
    quartersfact2: local:test-facttable(1, {
        aid: "0001193125-12-207154",
        concept: "disc:CashFlowOperatingCapitalTableTextBlock",
        fiscalPeriod: "Q1",
        fiscalYear: "2012",
        map: "Disclosures"
    }),
    generic: local:test-facttable(2, {
        "xbrl:Entity":"http://www.sec.gov/CIK 0000021344",
        "sec:Accepted":"20140227132423",
        concept:"us-gaap:Assets",
        profile-name: "generic"
    }),
    labels: test:invoke-and-assert-deep-equal(
          "facts",
          {
            tag : "DOW30",
            concept:"us-gaap:Assets",
            labels: "true",
            fiscalPeriod: "FY",
            fiscalYear: "2014"
          },
          function($res as item*) as item* {
            count(distinct-values($res.FactTable[].Labels."dei:LegalEntityAxis")),
            count($res.FactTable[].Labels."dei:LegalEntityAxis")
          },
          (1, 31)
      ),
    count: test:invoke-and-assert-deep-equal(
          "facts",
          {
              "ticker" : "ko",
              "fiscalYear" : ["2012", "2013", "2014"],
              "fiscalPeriod" : "FY",
              "count" : "true"
          },
          function($res as item*) as item* { $res.Statistics.NumFacts },
          1444
        ),
    odata: test:invoke-and-assert-deep-equal(
      "facts",
      {
          "ticker" : "ko",
          "fiscalYear" : "ALL",
          "fiscalPeriod" : "FY",
          "concept" : [ "us-gaap:Assets", "us-gaap:LiabilitiesCurrent", "us-gaap:StockholdersEquityIncludingPortionAttributableToNoncontrollingInterest" ],
          "format" : "atom-xml"
      },
      function($res as item*) as item* { count($res/*:feed/*:entry) },
      18,
      { Format: "xml" }
    )
})

import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace test = "http://apps.28.io/test";
import module namespace config = "http://apps.28.io/config";

declare %an:nondeterministic function local:test-facttable($expected as integer, $params as object) as item
{
    let $endpoint := "facts"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].FactTable[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-404($params as object) as item
{
    let $endpoint := "facts"
    let $res as object := test:invoke-raw($endpoint, $params)
    return if($res.status eq 404)
           then true
           else {
              url: test:url($endpoint, $params),
              unexpectedResponse: $res
           }
};

declare %an:sequential function local:check($o as object) as object
{
    if (not(every $k in (keys($o) ! $o.$$) satisfies ($k instance of boolean and $k)))
    then {
            response:status-code(500);
            $o
    } else
            $o
};

declare %an:nondeterministic function local:test-labels() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: [ "fac:Assets", "us-gaap:CashAndCashEquivalentsAtCarryingValue" ],
        report: "FundamentalAccountingConcepts",
        format: "csv",
        ticker: "ko",
        fiscalYear: 2013,
        fiscalPeriod: "Q3",
        labels: true
    }
    let $res as object := test:invoke-raw($endpoint, $params)
    let $actual := $res.body.content
    let $expectedLines := (
        "Concept [Axis],Reporting Entity [Axis],Period [Axis],Unit [Axis],Archive [Axis],Fiscal Year [Axis],Fiscal Period [Axis],Legal Entity [Axis],Fiscal Period Type [Axis],Value,Decimals,Balance,PeriodType,DataType\r\nAssets,COCA COLA CO (21344),\"September 27, 2013\",USD,0000021344-13-000050,2013,Q3,Default Legal Entity [Member],instant,89432000000,-6,DEBIT,instant,xbrli:monetaryItemType\r\n\"Cash and Cash Equivalents, at Carrying Value\",COCA COLA CO (21344),\"September 27, 2013\",USD,0000021344-14-000036,2013,Q3,Default Legal Entity [Member],instant,11118000000,-6,DEBIT,instant,xbrli:monetaryItemType\r\n"
    )
    return if($res.status eq 200 and (every $line in $expectedLines satisfies contains($actual,$line))) then true else {
        url: test:url($endpoint, $params),
        unexpectedResponse: $res,
        expected: $expectedLines
    }
};

declare %an:nondeterministic function local:test-labels-aids() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: "disc:AdvertisingCostsPolicyTextBlock",
        report: "Disclosures",
        format: "json",
        fiscalYear: 2014,
        fiscalPeriod: "FY",
        labels: true,
        aid: "0000858877-14-000029"
    }
    let $res := test:invoke($endpoint, $params)
    let $actual := [
            for $labels in $res[2].FactTable[].Labels
            return (keys($labels) ! $labels.$$)
        ]
    let $expected := [ "Archive [Axis]", "Fiscal Year [Axis]", "Non Numeric", "Default Legal Entity [Member]", "Unit [Axis]", "Advertising Costs, Policy", "Fiscal Period [Axis]", "Concept [Axis]", "Reporting Entity [Axis]", "Legal Entity [Axis]", "Fiscal Period Type [Axis]", "Period [Axis]", "From July 28, 2013 to July 26, 2014", "CISCO SYSTEMS, INC. (858877)" ]
    let $status as integer := $res[1]
    return test:assert-eq-array($expected, $actual, $status, test:url($endpoint, $params))
};

if(not $config:content[] = ("SEC-big", "SEC-small"))
then true
else
local:check({
    cocacola-latest:
    (: this test will fail and needs to be updated if a newer report has been filed.
       current latest filing: 2014 :)
    local:test-facttable(462, {
        ticker:"ko",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    cocacola-all: local:test-facttable(477, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-all-q3: local:test-facttable(341, {
        ticker:"ko",
        fiscalYear: 2014,
        fiscalPeriod: [ "Q3" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-instant: local:test-facttable(170, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant" ]
    }),
    cocacola-ytd: local:test-facttable(307, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "YTD" ]
    }),
    cocacola-ytd-several-years: local:test-facttable(607, {
        ticker:"ko",
        fiscalYear: [ 2013, 2014 ],
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "YTD" ]
    }),
    cocacola-qtd: local:test-facttable(102, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "Q2" ],
        fiscalPeriodType: [ "QTD" ]
    }),
    cocacolaCSVLabels: local:test-labels(),
    ciscoLabelsByAid: local:test-labels-aids(),
    tickerconcept: local:test-facttable(1, {
        ticker:"ko",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tickerfyfprole: local:test-facttable(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    tagconcept: local:test-facttable(30, { (: AT&T wrongly stamped a filing as FY, but it's now FIXED :-):)
        tag:"DOW30",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tagfyfprole: local:test-facttable(30, {
        tag:"DOW30",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    testNoEntity4Sic: local:test-404({
        concept:"fac:Assets",
        map:"FundamentalAccountingConcepts",
        format:"csv",
        sic:"5052",
        fiscalYear:"2010",
        fiscalPeriod:"Q3"
    }),
    quartersfact: local:test-facttable(0, { (: this fact is not reported in the raw XML filing :)
        aid: "0000051143-13-000007",
        concept: "disc:AssetRetirementObligationsPolicy",
        fiscalPeriod: "Q3",
        fiscalYear: "2013",
        map: "Disclosures"
    }),
    quartersfact2: local:test-facttable(1, {
        aid: "0001193125-12-207154",
        concept: "disc:CashFlowOperatingCapitalTableTextBlock",
        fiscalPeriod: "Q1",
        fiscalYear: "2012",
        map: "Disclosures"
    }),
    generic: local:test-facttable(2, {
        "xbrl:Entity":"http://www.sec.gov/CIK 0000021344",
        "sec:Accepted":"20140227132423",
        concept:"us-gaap:Assets",
        profile-name: "generic"
    }),
    labels: test:invoke-and-assert-deep-equal(
          "facts",
          {
            tag : "DOW30",
            concept:"us-gaap:Assets",
            labels: "true",
            fiscalPeriod: "FY",
            fiscalYear: "2014"
          },
          function($res as item*) as item* {
            count(distinct-values($res.FactTable[].Labels."dei:LegalEntityAxis")),
            count($res.FactTable[].Labels."dei:LegalEntityAxis")
          },
          (1, 31)
      ),
    count: test:invoke-and-assert-deep-equal(
          "facts",
          {
              "ticker" : "ko",
              "fiscalYear" : ["2012", "2013", "2014"],
              "fiscalPeriod" : "FY",
              "count" : "true"
          },
          function($res as item*) as item* { $res.Statistics.NumFacts },
          1444
        ),
    odata: test:invoke-and-assert-deep-equal(
      "facts",
      {
          "ticker" : "ko",
          "fiscalYear" : "ALL",
          "fiscalPeriod" : "FY",
          "concept" : [ "us-gaap:Assets", "us-gaap:LiabilitiesCurrent", "us-gaap:StockholdersEquityIncludingPortionAttributableToNoncontrollingInterest" ],
          "format" : "atom-xml"
      },
      function($res as item*) as item* { count($res/*:feed/*:entry) },
      18,
      { Format: "xml" }
    )
})


:pencil: Modify order of field in HTML output.

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";
import module namespace session = "http://apps.28.io/session";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace backend = "http://apps.28.io/backend";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token        as string? external;
declare  %rest:env                              variable $request-uri  as string  external;
declare  %rest:case-insensitive                 variable $format       as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $eid          as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cik          as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode   as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag          as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker       as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic          as string* external;
declare  %rest:case-insensitive %rest:distinct  %rest:encoding("UTF-8") variable $entity-search as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $entity-search-offset as integer? external;
declare  %rest:case-insensitive %rest:distinct  variable $entity-search-limit as integer? external;
declare  %rest:case-insensitive                 variable $language     as string  external := "en-US";
declare  %rest:case-insensitive                 variable $profile-name as string  external := $config:profile-name;
declare  %rest:case-insensitive                 variable $count        as boolean external := false;

(: HTTP parameter post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $tag as string* := api:preprocess-tags($tag)
let $tag := if (exists(($cik[$profile-name eq "sec"],
                        $edinetcode[$profile-name eq "japan"],
                        $tag,
                        $ticker,
                        $sic,
                        $entity-search,
                        $eid)))
             then $tag
             else "ALL"

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic,
  $entity-search,
  $language,
  (),
  {|
    {full-text-offset: $entity-search-offset}[exists($entity-search-offset)],
    {full-text-limit: $entity-search-limit}[exists($entity-search-limit)]
  |})
let $entities :=
  for $entity in $entities
  return {|
    { EIDs: [ $entity._id ] }[empty($entity.EIDs)],
    {
      Archives: backend:url(
        "filings",
        {|
          {
            token: $token,
            eid: [ entities:eid($entity) ],
            format: $format,
            profile-name: $profile-name
          },
          {
            fiscalYear: "ALL",
            fiscalPeriod: "ALL"
          }[$profile-name = ("sec", "japan")]
        |})
    },
    project($entity, "_id")[$profile-name eq "sec"],
    { ID: $entity._id }[$profile-name eq "sec"],
    trim($entity, "_id")
  |}

let $entities :=
  if($profile-name eq "sec")
  then
    for $entity in $entities
    return copy $e := $entity
           modify (
             insert json { EntityName: $e.Profiles.SEC.CompanyName,
                           EntityRegistrantName: $e.Profiles.SEC.CompanyName}
             into $e.Profiles.SEC,
             insert json { EID: $e.EIDs[] } into $e
           )
           return $e
  else
    $entities

let $comment :=
{
    NumEntities: count($entities),
    TotalNumEntities: session:num-entities()
}

let $result := { "Entities" : [ if($count) then () else $entities ] }
let $serializers := {
    to-xml : function($res as object) as node() {
        <Entities>{
          api:json-to-xml($res.Entities[], "Entity")
        }</Entities>
    },
    to-csv : function($res as object) as string {
        api:json-to-csv($res.Entities[])
    }
}
return api:serialize($result, $comment, $serializers, $format, "entities")

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";
import module namespace session = "http://apps.28.io/session";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace backend = "http://apps.28.io/backend";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token        as string? external;
declare  %rest:env                              variable $request-uri  as string  external;
declare  %rest:case-insensitive                 variable $format       as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $eid          as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cik          as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode   as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag          as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker       as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic          as string* external;
declare  %rest:case-insensitive %rest:distinct  %rest:encoding("UTF-8") variable $entity-search as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $entity-search-offset as integer? external;
declare  %rest:case-insensitive %rest:distinct  variable $entity-search-limit as integer? external;
declare  %rest:case-insensitive                 variable $language     as string  external := "en-US";
declare  %rest:case-insensitive                 variable $profile-name as string  external := $config:profile-name;
declare  %rest:case-insensitive                 variable $count        as boolean external := false;

(: HTTP parameter post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $tag as string* := api:preprocess-tags($tag)
let $tag := if (exists(($cik[$profile-name eq "sec"],
                        $edinetcode[$profile-name eq "japan"],
                        $tag,
                        $ticker,
                        $sic,
                        $entity-search,
                        $eid)))
             then $tag
             else "ALL"

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic,
  $entity-search,
  $language,
  (),
  {|
    {full-text-offset: $entity-search-offset}[exists($entity-search-offset)],
    {full-text-limit: $entity-search-limit}[exists($entity-search-limit)]
  |})
let $entities :=
  for $entity in $entities
  return {|
    project($entity, "_id")[$profile-name eq "sec"],
    { ID: $entity._id }[$profile-name eq "sec"],
    { EIDs: [ $entity._id ] }[empty($entity.EIDs)],
    {
      Archives: backend:url(
        "filings",
        {|
          {
            token: $token,
            eid: [ entities:eid($entity) ],
            format: $format,
            profile-name: $profile-name
          },
          {
            fiscalYear: "ALL",
            fiscalPeriod: "ALL"
          }[$profile-name = ("sec", "japan")]
        |})
    },
    trim($entity, "_id")
  |}

let $entities :=
  if($profile-name eq "sec")
  then
    for $entity in $entities
    return copy $e := $entity
           modify (
             insert json { EntityName: $e.Profiles.SEC.CompanyName,
                           EntityRegistrantName: $e.Profiles.SEC.CompanyName}
             into $e.Profiles.SEC,
             insert json { EID: $e.EIDs[] } into $e
           )
           return $e
  else
    $entities

let $comment :=
{
    NumEntities: count($entities),
    TotalNumEntities: session:num-entities()
}

let $result := { "Entities" : [ if($count) then () else $entities ] }
let $serializers := {
    to-xml : function($res as object) as node() {
        <Entities>{
          api:json-to-xml($res.Entities[], "Entity")
        }</Entities>
    },
    to-csv : function($res as object) as string {
        api:json-to-csv($res.Entities[])
    }
}
return api:serialize($result, $comment, $serializers, $format, "entities")


fixed unused variable.

fixed unused variable.


:green_heart: Fix test.

:green_heart: Fix test.

import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace test = "http://apps.28.io/test";

declare %an:nondeterministic function local:test-facttable($expected as integer, $params as object) as item
{
    let $endpoint := "facts"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].FactTable[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-404($params as object) as item
{
    let $endpoint := "facts"
    let $res as object := test:invoke-raw($endpoint, $params)
    return if($res.status eq 404)
           then true
           else {
              url: test:url($endpoint, $params),
              unexpectedResponse: $res
           }
};

declare %an:sequential function local:check($o as object) as object
{
    if (not(every $k in (keys($o) ! $o.$$) satisfies ($k instance of boolean and $k)))
    then {
            response:status-code(500);
            $o
    } else
            $o
};

declare %an:nondeterministic function local:test-labels() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: [ "fac:Assets", "us-gaap:CashAndCashEquivalentsAtCarryingValue" ],
        report: "FundamentalAccountingConcepts",
        format: "csv",
        ticker: "ko",
        fiscalYear: 2013,
        fiscalPeriod: "Q3",
        labels: true
    }
    let $res as object := test:invoke-raw($endpoint, $params)
    let $actual := $res.body.content
    let $expectedLines := (
        "Archive [Axis],Fiscal Period [Axis],Fiscal Year [Axis],Fiscal Period Type [Axis],Period [Axis],Reporting Entity [Axis],Concept [Axis],Unit [Axis],Legal Entity [Axis],Value,Decimals\r\n0000021344-14-000036,Q3,2013,instant,\"September 27, 2013\",COCA COLA CO (21344),\"Cash and Cash Equivalents, at Carrying Value\",USD,Default Legal Entity [Member],11118000000,-6\r\n0000021344-13-000050,Q3,2013,instant,\"September 27, 2013\",COCA COLA CO (21344),Assets,USD,Default Legal Entity [Member],89432000000,-6\r\n"
    )
    return if($res.status eq 200 and (every $line in $expectedLines satisfies contains($actual,$line))) then true else {
        url: test:url($endpoint, $params),
        unexpectedResponse: $res,
        expected: $expectedLines
    }
};

declare %an:nondeterministic function local:test-labels-aids() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: "disc:AdvertisingCostsPolicyTextBlock",
        report: "Disclosures",
        format: "json",
        fiscalYear: 2014,
        fiscalPeriod: "FY",
        labels: true,
        aid: "0000858877-14-000029"
    }
    let $res := test:invoke($endpoint, $params)
    let $actual := [
            for $labels in $res[2].FactTable[].Labels
            return (keys($labels) ! $labels.$$)
        ]
    let $expected := [ "Archive [Axis]", "Fiscal Year [Axis]", "Non-Numeric [Member]", "Default Legal Entity [Member]", "Unit [Axis]", "Advertising Costs, Policy", "Fiscal Period [Axis]", "Concept [Axis]", "Reporting Entity [Axis]", "Legal Entity [Axis]", "Fiscal Period Type [Axis]", "Period [Axis]", "From July 28, 2013 to July 26, 2014", "CISCO SYSTEMS, INC. (858877)" ]
    let $status as integer := $res[1]
    return test:assert-eq-array($expected, $actual, $status, test:url($endpoint, $params))
};

local:check({
    cocacola-latest:
    (: this test will fail and needs to be updated if a newer report has been filed.
       current latest filing: 2014 :)
    local:test-facttable(462, {
        ticker:"ko",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    cocacola-all: local:test-facttable(477, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-all-q3: local:test-facttable(341, {
        ticker:"ko",
        fiscalYear: 2014,
        fiscalPeriod: [ "Q3" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-instant: local:test-facttable(170, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant" ]
    }),
    cocacola-ytd: local:test-facttable(307, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "YTD" ]
    }),
    cocacola-qtd: local:test-facttable(102, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "Q2" ],
        fiscalPeriodType: [ "QTD" ]
    }),
    cocacolaCSVLabels: local:test-labels(),
    ciscoLabelsByAid: local:test-labels-aids(),
    tickerconcept: local:test-facttable(1, {
        ticker:"ko",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tickerfyfprole: local:test-facttable(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    tagconcept: local:test-facttable(31, { (: AT&T wrongly stamped a filing as FY :)
        tag:"DOW30",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tagfyfprole: local:test-facttable(30, {
        tag:"DOW30",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    testNoEntity4Sic: local:test-404({
        concept:"fac:Assets",
        map:"FundamentalAccountingConcepts",
        format:"csv",
        sic:"5052",
        fiscalYear:"2010",
        fiscalPeriod:"Q3"
    }),
    quartersfact: local:test-facttable(0, { (: this fact is not reported in the raw XML filing :)
        aid: "0000051143-13-000007",
        concept: "disc:AssetRetirementObligationsPolicy",
        fiscalPeriod: "Q3",
        fiscalYear: "2013",
        map: "Disclosures"
    }),
    quartersfact2: local:test-facttable(1, {
        aid: "0001193125-12-207154",
        concept: "disc:CashFlowOperatingCapitalTableTextBlock",
        fiscalPeriod: "Q1",
        fiscalYear: "2012",
        map: "Disclosures"
    }),
    generic: local:test-facttable(2, {
        "xbrl:Entity":"http://www.sec.gov/CIK 0000021344",
        "sec:Accepted":"20140227132423",
        concept:"us-gaap:Assets",
        profile-name: "generic"
    }),
    count: test:invoke-and-assert-deep-equal(
          "facts",
          {
              "ticker" : "ko",
              "fiscalYear" : ["2012", "2013", "2014"],
              "fiscalPeriod" : "FY",
              "count" : "true"
          },
          function($res as item*) as item* { $res.Statistics.NumFacts },
          1444
        )
})

import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace test = "http://apps.28.io/test";

declare %an:nondeterministic function local:test-facttable($expected as integer, $params as object) as item
{
    let $endpoint := "facts"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].FactTable[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-404($params as object) as item
{
    let $endpoint := "facts"
    let $res as object := test:invoke-raw($endpoint, $params)
    return if($res.status eq 404)
           then true
           else {
              url: test:url($endpoint, $params),
              unexpectedResponse: $res
           }
};

declare %an:sequential function local:check($o as object) as object
{
    if (not(every $k in (keys($o) ! $o.$$) satisfies ($k instance of boolean and $k)))
    then {
            response:status-code(500);
            $o
    } else
            $o
};

declare %an:nondeterministic function local:test-labels() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: [ "fac:Assets", "us-gaap:CashAndCashEquivalentsAtCarryingValue" ],
        report: "FundamentalAccountingConcepts",
        format: "csv",
        ticker: "ko",
        fiscalYear: 2013,
        fiscalPeriod: "Q3",
        labels: true
    }
    let $res as object := test:invoke-raw($endpoint, $params)
    let $actual := $res.body.content
    let $expectedLines := (
        "Archive [Axis],Fiscal Period [Axis],Fiscal Year [Axis],Fiscal Period Type [Axis],Period [Axis],Reporting Entity [Axis],Concept [Axis],Unit [Axis],Legal Entity [Axis],Value,Decimals\r\n0000021344-14-000036,Q3,2013,instant,\"September 27, 2013\",COCA COLA CO (21344),\"Cash and Cash Equivalents, at Carrying Value\",USD,Default Legal Entity [Member],11118000000,-6\r\n0000021344-13-000050,Q3,2013,instant,\"September 27, 2013\",COCA COLA CO (21344),Assets,USD,Default Legal Entity [Member],89432000000,-6\r\n"
    )
    return if($res.status eq 200 and (every $line in $expectedLines satisfies contains($actual,$line))) then true else {
        url: test:url($endpoint, $params),
        unexpectedResponse: $res,
        expected: $expectedLines
    }
};

declare %an:nondeterministic function local:test-labels-aids() as item
{
    let $endpoint := "facts"
    let $params := {
        concept: "disc:AdvertisingCostsPolicyTextBlock",
        report: "Disclosures",
        format: "json",
        fiscalYear: 2014,
        fiscalPeriod: "FY",
        labels: true,
        aid: "0000858877-14-000029"
    }
    let $res := test:invoke($endpoint, $params)
    let $actual := [
            for $labels in $res[2].FactTable[].Labels
            return (keys($labels) ! $labels.$$)
        ]
    let $expected := [ "Archive [Axis]", "Fiscal Year [Axis]", "Non-Numeric [Member]", "Default Legal Entity [Member]", "Unit [Axis]", "Advertising Costs, Policy", "Fiscal Period [Axis]", "Concept [Axis]", "Reporting Entity [Axis]", "Legal Entity [Axis]", "Fiscal Period Type [Axis]", "Period [Axis]", "From July 28, 2013 to July 26, 2014", "CISCO SYSTEMS, INC. (858877)" ]
    let $status as integer := $res[1]
    return test:assert-eq-array($expected, $actual, $status, test:url($endpoint, $params))
};

local:check({
    cocacola-latest:
    (: this test will fail and needs to be updated if a newer report has been filed.
       current latest filing: 2014 :)
    local:test-facttable(858, {
        ticker:"ko",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    cocacola-all: local:test-facttable(477, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-all-q3: local:test-facttable(341, {
        ticker:"ko",
        fiscalYear: 2014,
        fiscalPeriod: [ "Q3" ],
        fiscalPeriodType: [ "instant", "YTD", "QTD" ]
    }),
    cocacola-instant: local:test-facttable(170, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "instant" ]
    }),
    cocacola-ytd: local:test-facttable(307, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "FY" ],
        fiscalPeriodType: [ "YTD" ]
    }),
    cocacola-qtd: local:test-facttable(102, {
        ticker:"ko",
        fiscalYear: 2013,
        fiscalPeriod: [ "Q2" ],
        fiscalPeriodType: [ "QTD" ]
    }),
    cocacolaCSVLabels: local:test-labels(),
    ciscoLabelsByAid: local:test-labels-aids(),
    tickerconcept: local:test-facttable(1, {
        ticker:"ko",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tickerfyfprole: local:test-facttable(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    tagconcept: local:test-facttable(31, { (: AT&T wrongly stamped a filing as FY :)
        tag:"DOW30",
        concept:"us-gaap:Assets",
        fiscalYear: "LATEST",
        fiscalPeriod: "FY"
    }),
    tagfyfprole: local:test-facttable(30, {
        tag:"DOW30",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        concept:"us-gaap:Assets"
    }),
    testNoEntity4Sic: local:test-404({
        concept:"fac:Assets",
        map:"FundamentalAccountingConcepts",
        format:"csv",
        sic:"5052",
        fiscalYear:"2010",
        fiscalPeriod:"Q3"
    }),
    quartersfact: local:test-facttable(0, { (: this fact is not reported in the raw XML filing :)
        aid: "0000051143-13-000007",
        concept: "disc:AssetRetirementObligationsPolicy",
        fiscalPeriod: "Q3",
        fiscalYear: "2013",
        map: "Disclosures"
    }),
    quartersfact2: local:test-facttable(1, {
        aid: "0001193125-12-207154",
        concept: "disc:CashFlowOperatingCapitalTableTextBlock",
        fiscalPeriod: "Q1",
        fiscalYear: "2012",
        map: "Disclosures"
    }),
    generic: local:test-facttable(2, {
        "xbrl:Entity":"http://www.sec.gov/CIK 0000021344",
        "sec:Accepted":"20140227132423",
        concept:"us-gaap:Assets",
        profile-name: "generic"
    }),
    count: test:invoke-and-assert-deep-equal(
          "facts",
          {
              "ticker" : "ko",
              "fiscalYear" : ["2012", "2013", "2014"],
              "fiscalPeriod" : "FY",
              "count" : "true"
          },
          function($res as item*) as item* { $res.Statistics.NumFacts },
          1444
        )
})


:eyeglasses: addressed review comments.

:eyeglasses: addressed review comments.

jsoniq version "1.0";

module namespace request = "http://apps.28.io/request";

import module namespace req = "http://www.28msec.com/modules/http-request";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace archives = "http://28.io/modules/xbrl/archives";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace api = "http://apps.28.io/api";

declare function request:request(
  $profile-name as string,
  $entities as object*,
  $request-uri as string?,
  $format as string?,
  $concept as string*,
  $fiscalYear as string*,
  $fiscalPeriod as string*,
  $fiscalPeriodType as string*,
  $cik as string*,
  $tag as string*,
  $ticker as string*,
  $sic as string*,
  $aid as string*,
  $open as boolean) as object
{
  let $request as object :=
    {
      profile-name: $profile-name,
      format: api:preprocess-format($format, $request-uri),
      concept: [ $concept ],
      fiscalYear: [ $fiscalYear ],
      fiscalPeriod: [ api:preprocess-fiscal-periods($fiscalPeriod) ],
      fiscalPeriodType: [ $fiscalPeriodType ],
      cik: [ $cik ],
      tag: [ api:preprocess-tags($tag) ],
      ticker: [ $ticker ],
      sic: [ $sic ],
      aid: [ $aid ],
      open: $open
    }
  let $dimensions as object* :=
    for $parameter in request:param-names($request)
    group by $dimension-name :=
      switch(true)
      case ends-with(lower-case($parameter), "::default")
        return substring-before($parameter, "::default")
      case ends-with(lower-case($parameter), ":default")
        return substring-before($parameter, ":default")
      case ends-with(lower-case($parameter), "::type")
        return substring-before($parameter, "::type")
      default
        return $parameter
    let $all as boolean :=
      (request:param-values($dimension-name, $request, $entities) ! upper-case(string($$))) = "ALL"
    let $type as string? :=
      (request:param-values($dimension-name || "::type", $request, $entities),
       request:param-values($dimension-name || ":type", $request, $entities))[1]

    let $values := request:param-values($dimension-name, $request, $entities)
    let $typed-values := if (exists($type)) then request:cast-sequence($values[$$ ne "ALL"], $type) else $values

    let $has-default := ($parameter = $dimension-name || "::default") or ($parameter = $dimension-name || ":default")
    let $default-value := (request:param-values($dimension-name || "::default", $request, $entities),
                           request:param-values($dimension-name || ":default", $request, $entities))[1]
    let $typed-default-value := if (exists($type)) then request:cast-sequence($default-value, $type) else $default-value

    return
    {
       $dimension-name : {|
         { "Type" : $type }[exists($type)],
         { "Domain" : [ $typed-values ] }
             [exists($typed-values) and not($all)],
         { "Default" : $typed-default-value }[$has-default],
         { "Default" : null }
             [$all and (not $has-default) and exists($type)],
         { "Default" : "xbrl28:Domain" }
             [$all and (not $has-default) and empty($type)]
       |}
    }
  return
    {|
      $request,
      { dimensions: {| $dimensions |} }
    |}
};

declare function request:param-values(
  $name as string) as string*
{
  req:param-values($name)
};

declare %private function request:param-values(
    $name as string,
    $request as object,
    $entities as object*) as string*
{
  let $profile-name as string := $request.profile-name
  let $concept as string* := $request.concept[]
  let $fiscalYear as string* := $request.fiscalYear[]
  let $fiscalPeriod as string* := $request.fiscalPeriod[]
  let $fiscalPeriodType as string* := $request.fiscalPeriodType[]
  let $cik as string* := $request.cik[]
  let $tag as string* := $request.tag[]
  let $ticker as string* := $request.ticker[]
  let $sic as string* := $request.sic[]
  let $aid as string* := $request.aid[]
  return
    switch(true)
     case $name eq "xbrl:Concept"
        return ($concept, req:param-values("xbrl:Concept"))

    (: sec profile :)
     case $name eq "sec:FiscalYear" and $profile-name eq "sec"
        return ($fiscalYear, req:param-values("sec:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "sec:FiscalYear::type" and $profile-name eq "sec"
        return "integer"
     case $name eq "sec:FiscalPeriod" and $profile-name eq "sec"
        return ($fiscalPeriod, req:param-values("sec:FiscalPeriod"))
     case $name eq "sec:FiscalPeriod::type" and $profile-name eq "sec"
        return "string"
     case $name eq "sec:FiscalPeriodType" and $profile-name eq "sec"
        return ($fiscalPeriodType, req:param-values("sec:FiscalPeriodType"))
     case $name eq "sec:FiscalPeriodType::type" and $profile-name eq "sec"
        return "string"
     case $name eq "dei:LegalEntityAxis" and $profile-name eq "sec"
        return
         if(empty((req:param-values("sec:LegalEntityAxis"), req:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else req:param-values("sec:LegalEntityAxis")
     case $name eq "dei:LegalEntityAxis::default" return
         if(empty((req:param-values("sec:LegalEntityAxis"), req:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else req:param-values("sec:LegalEntityAxis::default")

     case $name eq "xbrl:Entity" and $profile-name = ("sec", "japan")
         return (
                if(empty(($cik,$tag,$ticker,$sic)) or exists($entities))
                then entities:eid($entities)
                else "dummy",
                req:param-values("xbrl:Entity")
            )
     case $name eq "xbrl28:Archive" and $profile-name = ("sec", "japan") return (
            let $prefix as string :=
                switch($profile-name)
                case "sec" return "sec"
                case "japan" return "fsa"
                default return (: not reachable :) ()
            let $fiscalYears :=
                let $years := req:param-values( $prefix || ":FiscalYear")
                return if ($years) then $years else $fiscalYear
            let $fiscalPeriods := request:param-values($prefix || ":FiscalPeriod", $request, $entities)
            return
                if($fiscalYears = "LATEST")
                then archives:aid(multiplexer:latest-filings($profile-name, $entities, $fiscalPeriods))
                else (),
            $aid,
            req:param-values("xbrl28:Archive")
        )

     (: japan profile :)
     case $name eq "fsa:FiscalYear" and $profile-name eq "japan"
     return ($fiscalYear ! string($$), req:param-values("fsa:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "fsa:FiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:FiscalPeriod" and $profile-name eq "japan"
     return ($fiscalPeriod, req:param-values("fsa:FiscalPeriod"))
     case $name eq "fsa:FiscalPeriod::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:FiscalPeriodType" and $profile-name eq "japan"
     return ($fiscalPeriodType, req:param-values("fsa:FiscalPeriodType"))
     case $name eq "fsa:FiscalPeriodType::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:ArchiveFiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:ArchiveFiscalPeriod::type" and $profile-name eq "japan"
     return "string"

     default return req:param-values($name)
};

declare %private function request:param-names($request as object) as string*
{
  let $profile-name as string := $request.profile-name
  let $concept as string* := $request.concept[]
  let $fiscalYear as string* := $request.fiscalYear[]
  let $fiscalPeriod as string* := $request.fiscalPeriod[]
  let $fiscalPeriodType as string* := $request.fiscalPeriodType[]
  let $names := req:param-names()
  return distinct-values((
    (: generic xbrl :)
    $names[contains($$, ":")],
    "xbrl:Entity"[$names = ("eid")],
    "xbrl28:Archive"[$profile-name = ("sec", "japan")],
    "xbrl:Concept"[exists($concept)],

    (: sec profile :)
    "sec:Accepted"[$profile-name eq "sec"],
    "sec:FiscalPeriod"[$profile-name eq "sec"],
    "sec:FiscalPeriodType"[$profile-name eq "sec"],
    "sec:FiscalYear"[$profile-name eq "sec"],
    "xbrl:Entity"[$profile-name eq "sec" and
                  $names = ("cik", "tag", "ticker", "sic")],
    "dei:LegalEntityAxis"[$profile-name eq "sec"],
    "dei:LegalEntityAxis::default"[$profile-name eq "sec"],

    (: japan profile :)
    "fsa:FiscalPeriod"[$profile-name eq "japan" and
                       exists($fiscalPeriod)],
    "fsa:FiscalPeriodType"[$profile-name eq "japan" and
                           exists($fiscalPeriodType)],
    "fsa:FiscalYear"[$profile-name eq "japan" and
                     exists($fiscalYear)],
    "xbrl:Entity"[$profile-name eq "japan" and
                  $names = ("edinetcode", "tag", "ticker")]
  ))
};

declare %private function request:cast-sequence($values as atomic*, $type as string) as atomic*
{
  for $value in $values
  return
      switch ($type)
      case "integer" return $value cast as integer
      case "string" return $value cast as string
      default return error(xs:QName("local:unsupported-type"), $type || ": unsupported type")
};

declare function request:hypercube(
  $request as object,
  $entities as object*) as object
{
  let $open as boolean := $request.open
  let $hypercube-spec := $request.dimensions
  return hypercubes:user-defined-hypercube($hypercube-spec, { IsOpen: $open })
};

jsoniq version "1.0";

module namespace request = "http://apps.28.io/request";

import module namespace req = "http://www.28msec.com/modules/http-request";
import module namespace entities = "http://28.io/modules/xbrl/entities";
import module namespace archives = "http://28.io/modules/xbrl/archives";
import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";
import module namespace hypercubes = "http://28.io/modules/xbrl/hypercubes";
import module namespace api = "http://apps.28.io/api";

declare function request:request(
  $profile-name as string,
  $entities as object*,
  $request-uri as string?,
  $format as string?,
  $concept as string*,
  $fiscalYear as string*,
  $fiscalPeriod as string*,
  $fiscalPeriodType as string*,
  $cik as string*,
  $tag as string*,
  $ticker as string*,
  $sic as string*,
  $aid as string*,
  $open as boolean) as object
{
  let $request as object :=
    {
      profile-name: $profile-name,
      format: api:preprocess-format($format, $request-uri),
      concept: [ $concept ],
      fiscalYear: [ $fiscalYear ],
      fiscalPeriod: [ api:preprocess-fiscal-periods($fiscalPeriod) ],
      fiscalPeriodType: [ $fiscalPeriodType ],
      cik: [ $cik ],
      tag: [ api:preprocess-tags($tag) ],
      ticker: [ $ticker ],
      sic: [ $sic ],
      aid: [ $aid ],
      open: $open
    }
  let $dimensions as object* :=
    for $parameter in request:param-names($request)
    group by $dimension-name :=
      switch(true)
      case ends-with(lower-case($parameter), "::default")
        return substring-before($parameter, "::default")
      case ends-with(lower-case($parameter), ":default")
        return substring-before($parameter, ":default")
      case ends-with(lower-case($parameter), "::type")
        return substring-before($parameter, "::type")
      default
        return $parameter
    let $all as boolean :=
      (request:param-values($dimension-name, $request, $entities) ! upper-case(string($$))) = "ALL"
    let $type as string? :=
      (request:param-values($dimension-name || "::type", $request, $entities),
       request:param-values($dimension-name || ":type", $request, $entities))[1]

    let $values := request:param-values($dimension-name, $request, $entities)
    let $typed-values := if (exists($type)) then request:cast-sequence($values[$$ ne "ALL"], $type) else $values

    let $has-default := ($parameter = $dimension-name || "::default") or ($parameter = $dimension-name || ":default")
    let $default-value := (request:param-values($dimension-name || "::default", $request, $entities),
                           request:param-values($dimension-name || ":default", $request, $entities))[1]
    let $typed-default-value := if (exists($type)) then request:cast-sequence($default-value, $type) else $default-value

    return
    {
       $dimension-name : {|
         { "Type" : $type }[exists($type)],
         { "Domain" : [ $typed-values ] }
             [exists($typed-values) and not($all)],
         { "Default" : $typed-default-value }[$has-default],
         { "Default" : null }
             [$all and (not $has-default) and exists($type)],
         { "Default" : "xbrl28:Domain" }
             [$all and (not $has-default) and empty($type)]
       |}
    }
  return
    {|
      $request,
      { dimensions: [ $dimensions ] }
    |}
};

declare function request:param-values(
  $name as string) as string*
{
  req:param-values($name)
};

declare %private function request:param-values(
    $name as string,
    $request as object,
    $entities as object*) as string*
{
  let $profile-name as string := $request.profile-name
  let $concept as string* := $request.concept[]
  let $fiscalYear as string* := $request.fiscalYear[]
  let $fiscalPeriod as string* := $request.fiscalPeriod[]
  let $fiscalPeriodType as string* := $request.fiscalPeriodType[]
  let $cik as string* := $request.cik[]
  let $tag as string* := $request.tag[]
  let $ticker as string* := $request.ticker[]
  let $sic as string* := $request.sic[]
  let $aid as string* := $request.aid[]
  return
    switch(true)
     case $name eq "xbrl:Concept"
        return ($concept, req:param-values("xbrl:Concept"))

    (: sec profile :)
     case $name eq "sec:FiscalYear" and $profile-name eq "sec"
        return ($fiscalYear, req:param-values("sec:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "sec:FiscalYear::type" and $profile-name eq "sec"
        return "integer"
     case $name eq "sec:FiscalPeriod" and $profile-name eq "sec"
        return ($fiscalPeriod, req:param-values("sec:FiscalPeriod"))
     case $name eq "sec:FiscalPeriod::type" and $profile-name eq "sec"
        return "string"
     case $name eq "sec:FiscalPeriodType" and $profile-name eq "sec"
        return ($fiscalPeriodType, req:param-values("sec:FiscalPeriodType"))
     case $name eq "sec:FiscalPeriodType::type" and $profile-name eq "sec"
        return "string"
     case $name eq "dei:LegalEntityAxis" and $profile-name eq "sec"
        return
         if(empty((req:param-values("sec:LegalEntityAxis"), req:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else req:param-values("sec:LegalEntityAxis")
     case $name eq "dei:LegalEntityAxis::default" return
         if(empty((req:param-values("sec:LegalEntityAxis"), req:param-values("sec:LegalEntityAxis::default"))))
         then "sec:DefaultLegalEntity"
         else req:param-values("sec:LegalEntityAxis::default")

     case $name eq "xbrl:Entity" and $profile-name = ("sec", "japan")
         return (
                if(empty(($cik,$tag,$ticker,$sic)) or exists($entities))
                then entities:eid($entities)
                else "dummy",
                req:param-values("xbrl:Entity")
            )
     case $name eq "xbrl28:Archive" and $profile-name = ("sec", "japan") return (
            let $prefix as string :=
                switch($profile-name)
                case "sec" return "sec"
                case "japan" return "fsa"
                default return (: not reachable :) ()
            let $fiscalYears :=
                let $years := req:param-values( $prefix || ":FiscalYear")
                return if ($years) then $years else $fiscalYear
            let $fiscalPeriods := request:param-values($prefix || ":FiscalPeriod", $request, $entities)
            return
                if($fiscalYears = "LATEST")
                then archives:aid(multiplexer:latest-filings($profile-name, $entities, $fiscalPeriods))
                else (),
            $aid,
            req:param-values("xbrl28:Archive")
        )

     (: japan profile :)
     case $name eq "fsa:FiscalYear" and $profile-name eq "japan"
     return ($fiscalYear ! string($$), req:param-values("fsa:FiscalYear"))[$$ ne "LATEST"]
     case $name eq "fsa:FiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:FiscalPeriod" and $profile-name eq "japan"
     return ($fiscalPeriod, req:param-values("fsa:FiscalPeriod"))
     case $name eq "fsa:FiscalPeriod::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:FiscalPeriodType" and $profile-name eq "japan"
     return ($fiscalPeriodType, req:param-values("fsa:FiscalPeriodType"))
     case $name eq "fsa:FiscalPeriodType::type" and $profile-name eq "japan"
     return "string"
     case $name eq "fsa:ArchiveFiscalYear::type" and $profile-name eq "japan"
     return "integer"
     case $name eq "fsa:ArchiveFiscalPeriod::type" and $profile-name eq "japan"
     return "string"

     default return req:param-values($name)
};

declare %private function request:param-names($request as object) as string*
{
  let $profile-name as string := $request.profile-name
  let $concept as string* := $request.concept[]
  let $fiscalYear as string* := $request.fiscalYear[]
  let $fiscalPeriod as string* := $request.fiscalPeriod[]
  let $fiscalPeriodType as string* := $request.fiscalPeriodType[]
  let $names := req:param-names()
  return distinct-values((
    (: generic xbrl :)
    $names[contains($$, ":")],
    "xbrl:Entity"[$names = ("eid")],
    "xbrl28:Archive"[$profile-name = ("sec", "japan")],
    "xbrl:Concept"[exists($concept)],

    (: sec profile :)
    "sec:Accepted"[$profile-name eq "sec"],
    "sec:FiscalPeriod"[$profile-name eq "sec"],
    "sec:FiscalPeriodType"[$profile-name eq "sec"],
    "sec:FiscalYear"[$profile-name eq "sec"],
    "xbrl:Entity"[$profile-name eq "sec" and
                  $names = ("cik", "tag", "ticker", "sic")],
    "dei:LegalEntityAxis"[$profile-name eq "sec"],
    "dei:LegalEntityAxis::default"[$profile-name eq "sec"],

    (: japan profile :)
    "fsa:FiscalPeriod"[$profile-name eq "japan" and
                       exists($fiscalPeriod)],
    "fsa:FiscalPeriodType"[$profile-name eq "japan" and
                           exists($fiscalPeriodType)],
    "fsa:FiscalYear"[$profile-name eq "japan" and
                     exists($fiscalYear)],
    "xbrl:Entity"[$profile-name eq "japan" and
                  $names = ("edinetcode", "tag", "ticker")]
  ))
};

declare %private function request:cast-sequence($values as atomic*, $type as string) as atomic*
{
  for $value in $values
  return
      switch ($type)
      case "integer" return $value cast as integer
      case "string" return $value cast as string
      default return error(xs:QName("local:unsupported-type"), $type || ": unsupported type")
};

declare function request:hypercube(
  $request as object,
  $entities as object*) as object
{
  let $open as boolean := $request.open
  let $hypercube-spec :=
  {|
    for $parameter in request:param-names($request)
    group by $dimension-name :=
        switch(true)
        case ends-with(lower-case($parameter), "::default")
            return substring-before($parameter, "::default")
        case ends-with(lower-case($parameter), ":default")
            return substring-before($parameter, ":default")
        case ends-with(lower-case($parameter), "::type")
            return substring-before($parameter, "::type")
        default
            return $parameter
    let $all as boolean :=
        (request:param-values($dimension-name, $request, $entities) ! upper-case($$)) = "ALL"
    let $type as string? :=
        (request:param-values($dimension-name || "::type", $request, $entities),
         request:param-values($dimension-name || ":type", $request, $entities))[1]

    let $values := request:param-values($dimension-name, $request, $entities)
    let $typed-values := if (exists($type)) then request:cast-sequence($values[$$ ne "ALL"], $type) else $values

    let $has-default := ($parameter = $dimension-name || "::default") or ($parameter = $dimension-name || ":default")
    let $default-value := (request:param-values($dimension-name || "::default", $request, $entities),
                           request:param-values($dimension-name || ":default", $request, $entities))[1]
    let $typed-default-value := if (exists($type)) then request:cast-sequence($default-value, $type) else $default-value

    return
    {
      $dimension-name : {|
        { "Type" : $type }[exists($type)],
        { "Domain" : [ $typed-values ] }
            [exists($typed-values) and not($all)],
        { "Default" : $typed-default-value }[$has-default],
        { "Default" : null }
            [$all and (not $has-default) and exists($type)],
        { "Default" : "xbrl28:Domain" }
            [$all and (not $has-default) and empty($type)]
      |}
    }
  |}
  return hypercubes:user-defined-hypercube($hypercube-spec, { IsOpen: $open })
};


:lipstick: Implement comment.

:lipstick: Implement comment.

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token         as string? external;
declare  %rest:env                              variable $request-uri   as string  external;
declare  %rest:case-insensitive                 variable $format        as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $eid           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cik           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode    as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker        as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear    as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod  as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $filingKind    as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $aid           as string* external;
declare  %rest:case-insensitive                 variable $profile-name  as string  external := $config:profile-name;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $tag as string* := api:preprocess-tags($tag)

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic, ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $archives as object* := multiplexer:filings(
  $profile-name,
  $entities,
  $fiscalPeriod,
  $fiscalYear,
  $filingKind,
  $aid)

let $archives-not-found as boolean :=
  exists(($entities, $fiscalPeriod, $fiscalYear, $filingKind, $aid)) and empty($archives)

let $periods :=
    switch($profile-name)
    case "sec" return
      for $a in $archives
      group by $fy := $a.Profiles.SEC.DocumentFiscalYearFocus, $fp := $a.Profiles.SEC.DocumentFiscalPeriodFocus
      order by $fy descending, $fp
      return { FiscalYear: $fy, FiscalPeriod: $fp }
    case "japan" return
      for $a in $archives
      group by $fy := $a.Profiles.FSA.DocumentFiscalYearFocus, $fp := $a.Profiles.FSA.DocumentFiscalPeriodFocus
      order by $fy descending, $fp
      where not empty(($fy, $fp))
      return { FiscalYear: $fy, FiscalPeriod: $fp }
    default return ()

let $result := { "Periods" : [ $periods ] }
let $comment :=
{
    NumPeriods: count($periods)
}
let $serializers := {
    to-xml : function($res as object) as node() {
        switch($profile-name)
        case "sec"
        case "japan" return
            <Periods>{
              api:json-to-xml($res.Periods[], "Period")
            }</Periods>
        default return ()
    },
    to-csv : function($res as object) as string {
        switch($profile-name)
        case "sec"
        case "japan" return api:json-to-csv($res.Periods[])
        default return ()
    }
}

let $results := api:serialize($result, $comment, $serializers, $format, "filings")
return switch(true)
       case $entities-not-found return api:not-found("entity")
       case $archives-not-found return api:not-found("archive")
       default return api:check-and-return-results($token, $results, $format)

import module namespace config = "http://apps.28.io/config";
import module namespace api = "http://apps.28.io/api";

import module namespace multiplexer = "http://28.io/modules/xbrl/profiles/multiplexer";

declare option rest:response "first-item";

(: Query parameters :)
declare  %rest:case-insensitive                 variable $token         as string? external;
declare  %rest:env                              variable $request-uri   as string  external;
declare  %rest:case-insensitive                 variable $format        as string? external;
declare  %rest:case-insensitive %rest:distinct  variable $eid           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $cik           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $edinetcode    as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $tag           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $ticker        as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $sic           as string* external;
declare  %rest:case-insensitive %rest:distinct  variable $fiscalYear    as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $fiscalPeriod  as string* external := "ALL";
declare  %rest:case-insensitive %rest:distinct  variable $filingKind    as string* external := ();
declare  %rest:case-insensitive %rest:distinct  variable $aid           as string* external;
declare  %rest:case-insensitive                 variable $profile-name  as string  external := $config:profile-name;

(: Post-processing :)
let $format as string? := api:preprocess-format($format, $request-uri)
let $fiscalYear as integer* := api:preprocess-fiscal-years($fiscalYear)
let $fiscalPeriod as string* := api:preprocess-fiscal-periods($fiscalPeriod)
let $tag as string* := api:preprocess-tags($tag)

let $cik as string* :=
    switch($profile-name)
    case "sec" return $cik
    case "japan" return $edinetcode
    default return ()

(: Entity resolution :)
let $entities := multiplexer:entities(
  $profile-name,
  $eid,
  $cik,
  $tag,
  $ticker,
  $sic, ())

let $entities-not-found as boolean :=
  exists(($eid, $cik, $tag, $ticker, $sic)) and empty($entities)

let $archives as object* := multiplexer:filings(
  $profile-name,
  $entities,
  $fiscalPeriod,
  $fiscalYear,
  $filingKind,
  $aid)

let $archives-not-found as boolean :=
  exists(($entities, $fiscalPeriod, $fiscalYear, $filingKind, $aid)) and empty($archives)

let $periods :=
    switch($profile-name)
    case "sec" return
      for $a in $archives
      group by $fy := $a.Profiles.SEC.DocumentFiscalYearFocus, $fp := $a.Profiles.SEC.DocumentFiscalPeriodFocus
      order by $fy descending, $fp
      return { FiscalYear: $fy, FiscalPeriod: $fp }
    case "japan" return
      for $a in $archives
      group by $fy := $a.Profiles.FSA.DocumentFiscalYearFocus, $fp := $a.Profiles.FSA.DocumentFiscalPeriodFocus
      order by $fy descending, $fp
      where not empty(($fy, $fp))
      return { FiscalYear: $fy, FiscalPeriod: $fp }
    default return ()

let $result := { "Periods" : [ $periods ] }
let $comment :=
{
    NumPeriods: count($periods)
}
let $serializers := {
    to-xml : function($res as object) as node() {
        switch($profile-name)
        case "sec"
        case "japan" return
            <Periods>{
              api:json-to-xml($res.Periods[], "Period")
            }</Periods>
        default return ()
    },
    to-csv : function($res as object) as string {
        switch($profile-name)
        case "sec"
        case "japan" return api:json-to-csv($res.Periods[])
        default return ()
    }
}

let $results := api:serialize($result, $comment, $serializers, $format, "filings")
return if($entities-not-found)
       then api:not-found("entity")
       else if($archives-not-found)
            then api:not-found("archive")
            else api:check-and-return-results($token, $results, $format)