diff --git a/lib/model/query/submissions.js b/lib/model/query/submissions.js index 379a0249c..3e70d0f59 100644 --- a/lib/model/query/submissions.js +++ b/lib/model/query/submissions.js @@ -8,7 +8,7 @@ // except according to the terms contained in the LICENSE file. const config = require('config'); -const { map } = require('ramda'); +const { always, equals, identity, ifElse, map, pick, without } = require('ramda'); const { sql } = require('slonik'); const { Frame, table } = require('../frame'); const { Actor, Form, Submission } = require('../frames'); @@ -338,13 +338,16 @@ const _exportUnjoiner = unjoiner(Submission, Submission.Def, Submission.Xml, Sub // TODO: this is a terrible hack to add some logic to one of our select fields. this is // the /only/ place we need to do this in the entire codebase right now. so for now // we just use the terrible hack. -const { raw } = require('slonik-sql-tag-raw'); -const _exportFields = raw(_exportUnjoiner.fields.sql - .replace(',"submissions"."userAgent" as "submissions!userAgent"', '') - .replace( - '"submission_defs"."xml" as "submission_defs!xml"', - '(case when submission_defs."localKey" is null then submission_defs.xml end) as "submission_defs!xml"' - )); +const xmlUnlessEncrypted = { + name: 'xml', + selectSql: tableAlias => sql`(case when ${sql.identifier([tableAlias, 'localKey'])} is null then ${sql.identifier([tableAlias, 'xml'])} end)`, +}; +const cloneFrame = (frame, fieldMutator) => ({ ...pick(['from', 'to'], frame), fields: fieldMutator(frame.fields) }); +const ExportSubmission = cloneFrame(Submission, without(['userAgent'])); +const ExportSubmissionDef = cloneFrame(Submission.Def, map(ifElse(equals('xml'), always(xmlUnlessEncrypted), identity))); +const _exportFields = unjoiner(ExportSubmission, ExportSubmissionDef, Submission.Xml, Submission.Encryption, + Actor.alias('actors', 'submitter'), Frame.define(table('attachments'), 'present', 'expected'), + Frame.define(table('edits'), 'count'), Submission.Exports).fields; const _export = (formId, draft, keyIds = [], options) => { const encrypted = keyIds.length !== 0; diff --git a/lib/util/db.js b/lib/util/db.js index fa7997f0b..163122ff9 100644 --- a/lib/util/db.js +++ b/lib/util/db.js @@ -86,9 +86,15 @@ const unjoiner = (...frames) => { const isOption = MaybeInstance instanceof Option; const Instance = isOption ? MaybeInstance.get() : MaybeInstance; for (const field of Instance.fields) { - const fullname = (Instance.from == null) ? sql.identifier([field]) : sql.identifier([Instance.from, field]); - const sqlname = (Instance.from == null) ? field : `${Instance.from}!${field}`; - fields.push(sql`${fullname} as ${sql.identifier([sqlname])}`); + const fieldName = field.name ?? field; + const sqlname = (Instance.from == null) ? fieldName : `${Instance.from}!${fieldName}`; + const sqlAlias = sql.identifier([sqlname]); + if (field.selectSql) { + fields.push(sql`${field.selectSql(Instance.from)} as ${sqlAlias}`); + } else { + const fullname = (Instance.from == null) ? sql.identifier([field]) : sql.identifier([Instance.from, field]); + fields.push(sql`${fullname} as ${sqlAlias}`); + } unmap[sqlname] = Instance.to; unprefix[sqlname] = field; constructors[Instance.to] = isOption ? maybeConstruct(Instance) : construct(Instance); diff --git a/package-lock.json b/package-lock.json index 215af6a59..c1e6cd763 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,7 +42,6 @@ "sanitize-filename": "~1", "semver": "^7.6.2", "slonik": "npm:@getodk/slonik@23.6.2-4", - "slonik-sql-tag-raw": "npm:@getodk/slonik-sql-tag-raw@1.0.3-1", "tmp-promise": "~3", "uuid": "^9.0.0", "yauzl": "~2.10" @@ -9882,69 +9881,6 @@ "node": ">=10.0" } }, - "node_modules/slonik-sql-tag-raw": { - "name": "@getodk/slonik-sql-tag-raw", - "version": "1.0.3-1", - "resolved": "https://registry.npmjs.org/@getodk/slonik-sql-tag-raw/-/slonik-sql-tag-raw-1.0.3-1.tgz", - "integrity": "sha512-bn+T4rAtN707vZRHly7ZhPl2OUXvIsh/T/WKuQEYbDJTGvR4k4exjni1LUqMt8K1vMX8D4fnNd0Dwryj5RsmZQ==", - "dependencies": { - "lodash": "^4.17.15", - "roarr": "^2.15.2", - "serialize-error": "^6.0.0" - }, - "engines": { - "node": ">=10.0" - }, - "peerDependencies": { - "slonik": "*" - } - }, - "node_modules/slonik-sql-tag-raw/node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/slonik-sql-tag-raw/node_modules/serialize-error": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-6.0.0.tgz", - "integrity": "sha512-3vmBkMZLQO+BR4RPHcyRGdE09XCF6cvxzk2N2qn8Er3F91cy8Qt7VvEbZBOpaL53qsBbe2cFOefU6tRY6WDelA==", - "dependencies": { - "type-fest": "^0.12.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/slonik-sql-tag-raw/node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" - }, - "node_modules/slonik-sql-tag-raw/node_modules/type-fest": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", - "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/slonik/node_modules/concat-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", @@ -19003,49 +18939,6 @@ } } }, - "slonik-sql-tag-raw": { - "version": "npm:@getodk/slonik-sql-tag-raw@1.0.3-1", - "resolved": "https://registry.npmjs.org/@getodk/slonik-sql-tag-raw/-/slonik-sql-tag-raw-1.0.3-1.tgz", - "integrity": "sha512-bn+T4rAtN707vZRHly7ZhPl2OUXvIsh/T/WKuQEYbDJTGvR4k4exjni1LUqMt8K1vMX8D4fnNd0Dwryj5RsmZQ==", - "requires": { - "lodash": "^4.17.15", - "roarr": "^2.15.2", - "serialize-error": "^6.0.0" - }, - "dependencies": { - "roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "requires": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" - } - }, - "serialize-error": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-6.0.0.tgz", - "integrity": "sha512-3vmBkMZLQO+BR4RPHcyRGdE09XCF6cvxzk2N2qn8Er3F91cy8Qt7VvEbZBOpaL53qsBbe2cFOefU6tRY6WDelA==", - "requires": { - "type-fest": "^0.12.0" - } - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" - }, - "type-fest": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.12.0.tgz", - "integrity": "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==" - } - } - }, "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", diff --git a/package.json b/package.json index f0bfa5578..e51188ad3 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "sanitize-filename": "~1", "semver": "^7.6.2", "slonik": "npm:@getodk/slonik@23.6.2-4", - "slonik-sql-tag-raw": "npm:@getodk/slonik-sql-tag-raw@1.0.3-1", "tmp-promise": "~3", "uuid": "^9.0.0", "yauzl": "~2.10"