blob: 954a9f6787622b771c8c600b99cb6168d9bce54f [file] [log] [blame]
/**
*
* Copyright (c) 2020 Silicon Labs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This module provides queries related to packages.
*
* @module DB API: package-based queries.
*/
const dbApi = require('./db-api.js')
const dbMapping = require('./db-mapping.js')
const dbEnum = require('../../src-shared/db-enum.js')
const querySession = require('./query-session')
const querySelectFromPackage = `
SELECT
PACKAGE_ID,
PATH,
TYPE,
CRC,
VERSION,
CATEGORY,
DESCRIPTION,
IS_IN_SYNC
FROM PACKAGE `
/**
* Checks if the package with a given path exists and executes appropriate action.
* Returns the promise that resolves the the package or null if nothing was found.
*
* @export
* @param {*} db
* @param {*} path Path of a file to check.
*/
async function getPackageByPathAndParent(db, path, parentId, isCustom) {
return dbApi
.dbGet(
db,
`${querySelectFromPackage} WHERE PATH = ? AND ${
isCustom ? 'PARENT_PACKAGE_REF IS NULL' : '(PARENT_PACKAGE_REF = ?)'
}`,
isCustom ? [path] : [path, parentId]
)
.then(dbMapping.map.package)
}
/**
* Get packages that have a given parent.
*
* @param {*} db
* @param {*} parentId
* @returns promise that resolves into an array of packages.
*/
async function getPackageByParent(db, parentId) {
return dbApi
.dbAll(db, `${querySelectFromPackage} WHERE PARENT_PACKAGE_REF = ?`, [
parentId
])
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Get zcl properties package from an array of packages.
*
* @param {*} db
* @param {*} packages
* @returns promise that resolves into an array of packages.
*/
async function getZclPropertiesPackage(db, packages) {
const packageIds = packages.map((singlePackage) => singlePackage.packageRef)
return dbApi
.dbAll(
db,
`${querySelectFromPackage} WHERE TYPE = 'zcl-properties' AND PACKAGE_ID in (${packageIds.join(
','
)})`
)
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Returns the package by path and type.
*
* @param {*} db
* @param {*} path
* @param {*} type
* @returns Promise of a query.
*/
async function getPackageByPathAndType(db, path, type) {
return dbApi
.dbGet(
db,
`${querySelectFromPackage} WHERE PATH = ? AND TYPE = ? AND IS_IN_SYNC = 1`,
[path, type]
)
.then(dbMapping.map.package)
}
/**
* Returns the package ID by path and type and version.
*
* @param {*} db
* @param {*} path
* @param {*} type
* @param {*} version
* @returns Promise of a query.
*/
async function getPackageIdByPathAndTypeAndVersion(db, path, type, version) {
// Version can be null for custom xml
let packageQuery =
`
SELECT
PACKAGE_ID
FROM
PACKAGE
WHERE
IS_IN_SYNC = 1
AND
PATH = '${path}'
AND
TYPE = '${type}'
AND ` + (version ? `VERSION = '${version}'` : `VERSION IS NULL`)
return dbApi.dbGet(db, packageQuery).then((row) => {
if (row == null) return null
else return row.PACKAGE_ID
})
}
/**
* Returns packages of a given type.
*
* @param {*} db
* @param {*} type
* @returns A promise that resolves into the rows array of packages.
*/
async function getPackagesByType(db, type) {
return dbApi
.dbAll(db, `${querySelectFromPackage} WHERE TYPE = ? AND IS_IN_SYNC = 1`, [
type
])
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Returns packages of a given type.
*
* @param {*} db
* @param {*} type
* @returns A promise that resolves into the rows array of packages.
*/
async function getPackagesByCategoryAndType(db, type, category = '') {
return dbApi
.dbAll(
db,
`${querySelectFromPackage} WHERE IS_IN_SYNC = 1 AND TYPE = ? AND (CATEGORY IN (${category}) OR CATEGORY IS NULL)`,
[type]
)
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Returns packages of a given type and parent.
*
* @param {*} db
* @param {*} type
* @returns A promise that resolves into the rows array of packages.
*/
async function getPackagesByParentAndType(db, parentId, type) {
return dbApi
.dbAll(
db,
`${querySelectFromPackage} WHERE TYPE = ? AND PARENT_PACKAGE_REF = ?`,
[type, parentId]
)
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Checks if the package with a given package id exists and executes appropriate action.
* Returns the promise that resolves the package or null if nothing was found.
*
* @export
* @param {*} db
* @param {*} packageId
*/
async function getPackageByPackageId(db, packageId) {
return dbApi
.dbGet(db, `${querySelectFromPackage} WHERE PACKAGE_ID = ?`, [packageId])
.then(dbMapping.map.package)
}
/**
* Checks if packages with given package ids exist and executes appropriate action.
* Returns the promise that resolves the packages or null if nothing was found.
*
* @export
* @param {*} db
* @param {*} packageIds
*/
async function getPackagesByPackageIds(db, packageIds) {
return dbApi
.dbAll(
db,
`${querySelectFromPackage} WHERE PACKAGE_ID IN (${dbApi.toInClause(
packageIds
)})`
)
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Returns a package ref from the attribute ID
*
* @param db
* @param attributeId
* @returns package ref
*/
async function getPackageRefByAttributeId(db, attributeId) {
let package_ref = await dbApi.dbAll(
db,
'SELECT PACKAGE_REF FROM ATTRIBUTE WHERE ATTRIBUTE_ID = ?',
[attributeId]
)
return package_ref[0].PACKAGE_REF
}
/**
* Resolves with a CRC or null for a given path.
*
* @export
* @param {*} db
* @param {*} path
* @returns Promise resolving with a CRC or null.
*/
async function getPathCrc(db, path) {
return dbApi
.dbGet(db, 'SELECT CRC FROM PACKAGE WHERE PATH = ? AND IS_IN_SYNC = 1', [
path
])
.then(
(row) =>
new Promise((resolve, reject) => {
if (row == null) {
resolve(null)
} else {
resolve(row.CRC)
}
})
)
}
/**
* Updates the version inside the package.
*
* @param {*} db
* @param {*} packageId
* @param {*} version
* @returns A promise of an updated version.
*/
async function updateVersion(db, packageId, version, category, description) {
return dbApi.dbUpdate(
db,
'UPDATE PACKAGE SET VERSION = ?, CATEGORY = ?, DESCRIPTION = ? WHERE PACKAGE_ID = ?',
[version, category, description, packageId]
)
}
/**
* Inserts a given path CRC type combination into the package table.
*
* @param {*} db
* @param {*} path Path of the file.
* @param {*} crc CRC of the file.
* @returns Promise of an insertion.
*/
async function insertPathCrc(
db,
path,
crc,
type,
parentId = null,
version = null,
category = null,
description = null
) {
return dbApi.dbInsert(
db,
'INSERT INTO PACKAGE ( PATH, CRC, TYPE, PARENT_PACKAGE_REF, VERSION, CATEGORY, DESCRIPTION ) VALUES (?, ?, ?, ?, ?, ?, ?)',
[path, crc, type, parentId, version, category, description]
)
}
/**
* Inserts or updates a package. Resolves with a packageId.
*
* @param {*} db
* @param {*} path
* @param {*} crc
* @param {*} type
* @param {*} [parentId=null]
* @returns An object containing: id, existedPreviously
*/
async function registerTopLevelPackage(
db,
path,
crc,
type,
version = null,
category = null,
description = null,
isTopLevelPackageInSync = true
) {
let row = await getPackageByPathAndType(db, path, type)
if (row == null || !isTopLevelPackageInSync) {
// Doesn't exist. We have to add it.
let id = await dbApi.dbInsert(
db,
`
INSERT INTO PACKAGE (
PATH, CRC, TYPE, PARENT_PACKAGE_REF, VERSION, CATEGORY, DESCRIPTION
) VALUES (?,?,?,?,?,?,?)`,
[path, crc, type, null, version, category, description]
)
return {
id: id,
existedPreviously: false
}
} else {
// Exists. We just return it.
return {
id: row.id,
existedPreviously: true
}
}
}
/**
* Updates a CRC in the table.
*
* @export
* @param {*} db
* @param {*} path
* @param {*} crc
* @returns Promise of an update.
*/
async function updatePathCrc(db, path, crc, parentId) {
return dbApi.dbUpdate(
db,
'UPDATE PACKAGE SET CRC = ? WHERE PATH = ? AND PARENT_PACKAGE_REF = ?',
[crc, path, parentId]
)
}
/**
* Updates a is in sync in the package table.
* @param {*} db
* @param {*} packageRef
* @param {*} isInSync
* @returns Promise of an update.
*/
async function updatePackageIsInSync(db, packageRef, isInSync) {
return dbApi.dbUpdate(
db,
'UPDATE PACKAGE SET IS_IN_SYNC = ? WHERE PACKAGE_ID = ?',
[dbApi.toDbBool(isInSync), packageRef]
)
}
/**
* Inserts a mapping between session and package.
*
* @param {*} db
* @param {*} sessionPartitionId
* @param {*} packageId
* @param {*} required
* @returns Promise of an insert.
*/
async function insertSessionPackage(
db,
sessionPartitionId,
packageId,
required = false
) {
return dbApi.dbInsert(
db,
'INSERT OR REPLACE INTO SESSION_PACKAGE (SESSION_PARTITION_REF, PACKAGE_REF, REQUIRED, ENABLED) VALUES (?,?,?,1)',
[sessionPartitionId, packageId, required]
)
}
/**
* @param {*} db
* @param {*} sessionPartitionId
* @param {*} packageType
*/
async function deleteSessionPackage(db, sessionPartitionId, packageId) {
return dbApi.dbRemove(
db,
`UPDATE SESSION_PACKAGE SET ENABLED = 0 WHERE SESSION_PARTITION_REF = ? AND PACKAGE_REF = ?`,
[sessionPartitionId, packageId]
)
}
/**
* Deletes all session packages based on sessionPartitionIds.
*
* @param {*} db
* @param {*} sessionPartitionIds
* @returns promise
*/
async function deleteAllSessionPackages(db, sessionPartitionIds) {
await dbApi.dbRemove(
db,
`DELETE FROM SESSION_PACKAGE WHERE SESSION_PARTITION_REF IN (${dbApi.toInClause(
sessionPartitionIds
)})`
)
return dbApi.dbRemove(
db,
`DELETE FROM SESSION_PARTITION WHERE SESSION_PARTITION_ID IN (${dbApi.toInClause(
sessionPartitionIds
)})`
)
}
/**
* Returns session packages of a given type.
*
* @param {*} db
* @param {*} sessionId
* @param {*} packageType
* @returns Promise that resolves into array of retrieve packages.
*/
async function getSessionPackagesByType(db, sessionId, packageType) {
return dbApi
.dbAll(
db,
`
SELECT
PACKAGE.PACKAGE_ID,
PACKAGE.PATH,
PACKAGE.TYPE,
PACKAGE.CRC,
PACKAGE.VERSION,
PACKAGE.CATEGORY,
PACKAGE.DESCRIPTION
FROM PACKAGE
INNER JOIN SESSION_PACKAGE
ON PACKAGE.PACKAGE_ID = SESSION_PACKAGE.PACKAGE_REF
INNER JOIN
SESSION_PARTITION
ON
SESSION_PACKAGE.SESSION_PARTITION_REF= SESSION_PARTITION.SESSION_PARTITION_ID
WHERE SESSION_PARTITION.SESSION_REF = ?
AND PACKAGE.TYPE = ?
AND SESSION_PACKAGE.ENABLED = 1`,
[sessionId, packageType]
)
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Returns session generation template packages.
*
* @param {*} db
* @param {*} sessionId
* @returns Promise that resolves into array of retrieve packages.
*/
async function getSessionGenTemplates(db, sessionId) {
return dbApi
.dbAll(
db,
`
SELECT
PACKAGE.PACKAGE_ID,
PACKAGE.PATH,
PACKAGE.TYPE,
PACKAGE.CRC,
PACKAGE.VERSION,
PACKAGE.CATEGORY,
PACKAGE.DESCRIPTION
FROM PACKAGE
WHERE
PACKAGE.TYPE = ? AND
PACKAGE.PARENT_PACKAGE_REF =
(SELECT
PACKAGE.PACKAGE_ID
FROM PACKAGE
INNER JOIN SESSION_PACKAGE
ON PACKAGE.PACKAGE_ID = SESSION_PACKAGE.PACKAGE_REF
INNER JOIN
SESSION_PARTITION
ON
SESSION_PACKAGE.SESSION_PARTITION_REF= SESSION_PARTITION.SESSION_PARTITION_ID
WHERE SESSION_PARTITION.SESSION_REF = ?
AND PACKAGE.TYPE = ?
AND SESSION_PACKAGE.ENABLED = 1)
ORDER BY PACKAGE.PATH ASC`,
[
dbEnum.packageType.genSingleTemplate,
sessionId,
dbEnum.packageType.genTemplatesJson
]
)
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Resolves into an array of package objects that all the ZCL queries should resolve into.
* @param {*} db
* @param {*} sessionId
*/
async function getSessionZclPackages(db, sessionId) {
let inList = `('${dbEnum.packageType.zclProperties}', '${dbEnum.packageType.zclXmlStandalone}')`
return dbApi
.dbAll(
db,
`
SELECT
SP.PACKAGE_REF,
SESSION_PARTITION.SESSION_REF,
SESSION_PARTITION.SESSION_PARTITION_ID,
SP.REQUIRED,
P.CATEGORY,
P.TYPE
FROM
SESSION_PARTITION
INNER JOIN
SESSION_PACKAGE AS SP
ON
SP.SESSION_PARTITION_REF= SESSION_PARTITION.SESSION_PARTITION_ID
INNER JOIN
PACKAGE AS P
ON
SP.PACKAGE_REF = P.PACKAGE_ID
WHERE
SESSION_PARTITION.SESSION_REF = ? AND SP.ENABLED = 1 AND P.TYPE IN ${inList}
`,
[sessionId]
)
.then((rows) => rows.map(dbMapping.map.sessionPackage))
}
/**
* Resolves into an array of IDs that are the packageIds that all the ZCL queries should resolve into.
* @param {*} db
* @param {*} sessionId
*/
async function getSessionZclPackageIds(db, sessionId) {
return getSessionZclPackages(db, sessionId).then((rows) =>
rows.map((r) => r.packageRef)
)
}
/**
* Returns the session package IDs.
* @param {*} db
* @param {*} sessionId
* @returns The promise that resolves into an array of package IDs.
*/
async function getSessionPackages(db, sessionId) {
return dbApi
.dbAll(
db,
`
SELECT
SESSION_PACKAGE.PACKAGE_REF,
SESSION_PARTITION.SESSION_REF,
SESSION_PACKAGE.REQUIRED
FROM
SESSION_PACKAGE
INNER JOIN
SESSION_PARTITION
ON
SESSION_PACKAGE.SESSION_PARTITION_REF= SESSION_PARTITION.SESSION_PARTITION_ID
WHERE
SESSION_PARTITION.SESSION_REF = ? AND SESSION_PACKAGE.ENABLED = 1`,
[sessionId]
)
.then((rows) => rows.map(dbMapping.map.sessionPackage))
}
/**
* Returns the session package IDs with types.
* @param {*} db
* @param {*} sessionId
* @returns The promise that resolves into an array of package IDs.
*/
async function getSessionPackagesWithTypes(db, sessionId) {
return dbApi
.dbAll(
db,
`
SELECT
SP.PACKAGE_REF,
SESSION_PARTITION.SESSION_REF,
SP.REQUIRED,
P.TYPE,
SESSION_PARTITION.SESSION_PARTITION_ID
FROM
SESSION_PARTITION
INNER JOIN
SESSION_PACKAGE AS SP
ON
SP.SESSION_PARTITION_REF= SESSION_PARTITION.SESSION_PARTITION_ID
INNER JOIN
PACKAGE AS P
ON
SP.PACKAGE_REF = P.PACKAGE_ID
WHERE
SESSION_PARTITION.SESSION_REF = ? AND SP.ENABLED = 1`,
[sessionId]
)
.then((rows) => rows.map(dbMapping.map.sessionPackage))
}
/**
* Returns all packages associated w/ a given sessionId
* @param {*} db
* @param {*} sessionId
*/
async function getPackageSessionPackagePairBySessionId(db, sessionId) {
let rows = await dbApi.dbAll(
db,
`
SELECT
P.PACKAGE_ID,
P.PATH,
P.TYPE,
P.CRC,
P.VERSION,
P.CATEGORY,
P.DESCRIPTION,
P.PARENT_PACKAGE_REF,
SP.PACKAGE_REF,
SESSION_PARTITION.SESSION_REF,
SP.REQUIRED
FROM
PACKAGE AS P
INNER JOIN
SESSION_PACKAGE AS SP
ON
P.PACKAGE_ID = SP.PACKAGE_REF
INNER JOIN
SESSION_PARTITION
ON
SP.SESSION_PARTITION_REF= SESSION_PARTITION.SESSION_PARTITION_ID
WHERE
SESSION_PARTITION.SESSION_REF = ?
AND SP.ENABLED = 1`,
[sessionId]
)
return rows.map((x) => {
return {
pkg: dbMapping.map.package(x),
sessionPackage: dbMapping.map.sessionPackage(x)
}
})
}
/**
* Returns all packages
* @param {*} db
*/
async function getAllPackages(db) {
return dbApi
.dbAll(
db,
`SELECT
PACKAGE_ID,
PATH,
TYPE,
CRC,
VERSION,
CATEGORY,
DESCRIPTION,
PARENT_PACKAGE_REF
FROM
PACKAGE
WHERE
IS_IN_SYNC = 1`
)
.then((rows) => rows.map(dbMapping.map.package))
}
/**
* Retrieves attribute access interface options from the database.
*
* This function performs a complex query to fetch options related to a specific code and package IDs. It combines results from
* the PACKAGE_OPTION table with those from ATTRIBUTE and CLUSTER tables using a UNION. The purpose is to gather a comprehensive
* list of options that include both direct package options and those inferred from attributes' storage policies and their associated
* clusters. It supports querying for multiple package IDs by ensuring the packageIds parameter is treated as an array, allowing
* for more flexible queries.
*
* @param {Object} db - The database connection object.
* @param {string} code - The option code or storage policy code to query for.
* @param {number|Array<number>} packageIds - The ID(s) of the package(s) to which the options are related. Can be a single ID or an array of IDs.
* @returns {Promise<Array>} A promise that resolves to an array of option objects, each containing the option category, code, and label.
*/
async function getAttributeAccessInterface(db, code, packageIds) {
try {
// Ensure packageIds is always an array
if (!Array.isArray(packageIds)) {
packageIds = [packageIds]
}
let packageRefCondition = `po.PACKAGE_REF = ?`
let attributePackageRefCondition = `a.PACKAGE_REF = ?`
let queryParams = [code, ...packageIds, code, ...packageIds]
// Since packageIds is now always an array, adjust the query and parameters accordingly
const placeholders = packageIds.map(() => '?').join(', ')
packageRefCondition = `po.PACKAGE_REF IN (${placeholders})`
attributePackageRefCondition = `a.PACKAGE_REF IN (${placeholders})`
const extendedQuery = `
SELECT
po.OPTION_CATEGORY,
po.OPTION_CODE,
po.OPTION_LABEL
FROM
PACKAGE_OPTION po
WHERE
po.OPTION_CODE = ?
AND ${packageRefCondition}
UNION
SELECT
c.NAME AS OPTION_CATEGORY,
a.STORAGE_POLICY AS OPTION_CODE,
a.NAME AS OPTION_LABEL
FROM
ATTRIBUTE a
LEFT JOIN CLUSTER c ON a.CLUSTER_REF = c.CLUSTER_ID
WHERE
a.STORAGE_POLICY = ?
AND ${attributePackageRefCondition}
`
return dbApi
.dbAll(db, extendedQuery, queryParams)
.then((rows) => rows.map(dbMapping.map.options))
} catch (error) {
console.error('Error fetching attribute access interface:', error)
throw error // Rethrow the error for further handling if necessary
}
}
/**
* This async function inserts an option and its values into the DB.
*
* @param {*} db
* @param {*} packageId - Package Reference
* @param {*} optionCategory - The name of the option.
* @param {*} optionCodeLabels - The array of values associated with this option.
*/
async function insertOptionsKeyValues(
db,
packageId,
optionCategory,
optionCodeLabels
) {
return dbApi.dbMultiInsert(
db,
`INSERT INTO PACKAGE_OPTION
(PACKAGE_REF, OPTION_CATEGORY, OPTION_CODE, OPTION_LABEL)
VALUES
(?, ?, ?, ?)
ON CONFLICT
(PACKAGE_REF, OPTION_CATEGORY, OPTION_CODE, OPTION_LABEL)
DO NOTHING`,
optionCodeLabels.map((optionValue) => {
return [packageId, optionCategory, optionValue.code, optionValue.label]
})
)
}
/**
* Shortcut method that returns all ui options.
* @param {*} db
* @param {*} packageId
* @returns object of ui options.
*/
async function selectAllUiOptions(db, packageId) {
let rows = await selectAllOptionsValues(
db,
packageId,
dbEnum.packageOptionCategory.ui
)
let obj = rows.reduce((prev, cur) => {
prev[cur.optionCode] = cur.optionLabel
return prev
}, {})
return obj
}
/**
* This async function returns all options associated with a specific category.
* @param {*} db
* @param {*} packageId
* @param {*} optionCategory
* @returns promise to return option that matches arguments.
*/
async function selectAllOptionsValues(db, packageId, optionCategory) {
return dbApi
.dbAll(
db,
`SELECT OPTION_ID, PACKAGE_REF, OPTION_CATEGORY, OPTION_CODE, OPTION_LABEL FROM PACKAGE_OPTION WHERE PACKAGE_REF = ? AND OPTION_CATEGORY = ?`,
[packageId, optionCategory]
)
.then((rows) => rows.map(dbMapping.map.options))
}
/**
*
* This async function returns option associated with a specific category and code.
* @param {*} db
* @param {*} packageId
* @param {*} optionCategory
* @param {*} optionCode
* @returns promise to return option that matches arguments.
*/
async function selectSpecificOptionValue(
db,
packageId,
optionCategory,
optionCode
) {
return dbApi
.dbGet(
db,
`SELECT OPTION_ID, PACKAGE_REF, OPTION_CATEGORY, OPTION_CODE, OPTION_LABEL FROM PACKAGE_OPTION WHERE PACKAGE_REF = ? AND OPTION_CATEGORY = ? AND OPTION_CODE = ?`,
[packageId, optionCategory, optionCode]
)
.then(dbMapping.map.options)
}
/**
* This async function returns a specific option value given an option reference.
* @param {*} db
* @param {*} optionDefaultId
*/
async function selectOptionValueByOptionDefaultId(db, optionDefaultId) {
return dbApi
.dbGet(
db,
`SELECT OPTION_ID, PACKAGE_REF, OPTION_CATEGORY, OPTION_CODE, OPTION_LABEL FROM PACKAGE_OPTION WHERE OPTION_ID = ?`,
[optionDefaultId]
)
.then(dbMapping.map.options)
}
/**
* Returns a promise of an insertion of option value.
*
* @param {*} db
* @param {*} packageId
* @param {*} optionCategory
* @param {*} optionRef
* @returns promise to insert option value
*/
async function insertDefaultOptionValue(
db,
packageId,
optionCategory,
optionRef
) {
return dbApi.dbInsert(
db,
'INSERT INTO PACKAGE_OPTION_DEFAULT ( PACKAGE_REF, OPTION_CATEGORY, OPTION_REF) VALUES (?, ?, ?) ON CONFLICT DO NOTHING',
[packageId, optionCategory, optionRef]
)
}
/**
* Returns a promise for all option values.
* @param {*} db
* @param {*} packageId
*/
async function selectAllDefaultOptions(db, packageId) {
return dbApi
.dbAll(
db,
`SELECT OPTION_DEFAULT_ID, PACKAGE_REF, OPTION_CATEGORY, OPTION_REF FROM PACKAGE_OPTION_DEFAULT WHERE PACKAGE_REF = ?`,
[packageId]
)
.then((rows) => rows.map(dbMapping.map.optionDefaults))
}
/**
* Inserts an array of extension default values to a specific extension ID.
*
* @param {*} db
* @param {*} packageExtensionId
* @param {*} defaultArray Array containing objects with 'entityCode', 'parentCode', 'manufacturerCode', 'entityQualifier', 'value'
* @returns Promise of insertion for defaults.
*/
async function insertPackageExtensionDefault(
db,
packageExtensionId,
defaultArray
) {
return dbApi.dbMultiInsert(
db,
`
INSERT INTO PACKAGE_EXTENSION_DEFAULT (
PACKAGE_EXTENSION_REF,
ENTITY_CODE,
ENTITY_QUALIFIER,
PARENT_CODE,
MANUFACTURER_CODE,
VALUE
) VALUES ( ?, ?, ?, ?, ?, ? )
ON CONFLICT DO NOTHING
`,
defaultArray.map((d) => {
return [
packageExtensionId,
d.entityCode,
d.entityQualifier,
d.parentCode,
d.manufacturerCode,
d.value
]
})
)
}
/**
* Returns a promise of insertion of package extension
*
* @param {*} db
* @param {*} packageId
* @param propertyArray. Array of objects that contain property, type, configurability, label, globalDefault
* @param defaultsArrayOfArrays For each item in propertyArray, it contains array of default rows, or null.
*/
async function insertPackageExtension(
db,
packageId,
entity,
propertyArray,
defaultsArrayOfArrays
) {
return dbApi
.dbMultiInsert(
db,
`
INSERT INTO PACKAGE_EXTENSION (
PACKAGE_REF,
ENTITY,
PROPERTY,
TYPE,
CONFIGURABILITY,
LABEL,
GLOBAL_DEFAULT
) VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT DO NOTHING`,
propertyArray.map((p) => {
return [
packageId,
entity,
p.property,
p.type,
p.configurability,
p.label,
p.globalDefault
]
})
)
.then((rowIds) => {
let promises = []
// now, for each rowId in this list, we populate corresponding defaults
if (rowIds.length == defaultsArrayOfArrays.length) {
for (let i = 0; i < rowIds.length; i++) {
let rowId = rowIds[i]
let defaultsArray = defaultsArrayOfArrays[i]
if (defaultsArray != null) {
promises.push(
insertPackageExtensionDefault(db, rowId, defaultsArray)
)
}
}
}
return Promise.all(promises)
})
}
/**
*
* @param {*} db
* @param {*} packageId
* @param {*} property
* @param {*} entityType One of the packageExtensionEntity enums
*/
async function selectPackageExtensionByPropertyAndEntity(
db,
packageId,
property,
entity
) {
let rows = await dbApi.dbAll(
db,
`
SELECT
PE.TYPE,
PE.CONFIGURABILITY,
PE.LABEL,
PE.GLOBAL_DEFAULT,
PED.ENTITY_CODE,
PED.ENTITY_QUALIFIER,
PED.PARENT_CODE,
PED.MANUFACTURER_CODE,
PED.VALUE
FROM
PACKAGE_EXTENSION AS PE
LEFT OUTER JOIN
PACKAGE_EXTENSION_DEFAULT AS PED
ON
PE.PACKAGE_EXTENSION_ID = PED.PACKAGE_EXTENSION_REF
WHERE
PE.PACKAGE_REF = ?
AND PE.ENTITY = ?
AND PE.PROPERTY = ?
ORDER BY
PE.PROPERTY,
PED.PARENT_CODE,
PED.ENTITY_CODE`,
[packageId, entity, property]
)
if (rows != null && rows.length > 0) {
let res = {
type: rows[0].TYPE,
configurability: rows[0].CONFIGURABILITY,
label: rows[0].LABEL,
globalDefault: rows[0].GLOBAL_DEFAULT,
defaults: []
}
return rows.reduce((acc, x) => {
acc.defaults.push({
entityCode: x.ENTITY_CODE,
parentCode: x.PARENT_CODE,
manufacturerCode: x.MANUFACURER_CODE,
qualifier: x.ENTITY_QUALIFIER,
value: x.VALUE
})
return acc
}, res)
} else {
return null
}
}
/**
* Select extensions for a given entity type for a package.
*
* @param {*} db
* @param {*} packageId
* @param {*} entity
* @returns promise that resolve into an array of packageExtensions for a given entity
*/
async function selectPackageExtension(db, packageId, entity) {
let acc = []
return dbApi
.dbAll(
db,
`
SELECT
PE.ENTITY,
PE.PROPERTY,
PE.TYPE,
PE.CONFIGURABILITY,
PE.LABEL,
PE.GLOBAL_DEFAULT,
PED.ENTITY_CODE,
PED.ENTITY_QUALIFIER,
PED.PARENT_CODE,
PED.MANUFACTURER_CODE,
PED.VALUE
FROM
PACKAGE_EXTENSION AS PE
LEFT OUTER JOIN
PACKAGE_EXTENSION_DEFAULT AS PED
ON
PE.PACKAGE_EXTENSION_ID = PED.PACKAGE_EXTENSION_REF
WHERE
PE.PACKAGE_REF = ?
AND PE.ENTITY = ?
ORDER BY
PE.PROPERTY,
PED.PARENT_CODE,
PED.ENTITY_CODE`,
[packageId, entity]
)
.then((rows) =>
rows.reduce((a, x) => {
let newPropRequired
if (a.length == 0 || a[a.length - 1].property != x.PROPERTY) {
newPropRequired = true
} else {
newPropRequired = false
}
let prop
if (newPropRequired) {
prop = dbMapping.map.packageExtension(x)
prop.defaults = []
a.push(prop)
} else {
prop = a[a.length - 1]
}
prop.defaults.push(dbMapping.map.packageExtensionDefault(x))
return a
}, acc)
)
}
/**
* Takes defaults key values from package, and inserts them into the session
* as initial values.
*
* @param {*} db
* @param {*} sessionId
* @returns the list of packages from getSessionPackages()
*/
async function insertSessionKeyValuesFromPackageDefaults(db, sessionId) {
let packages = await getSessionPackages(db, sessionId)
let p = packages.map(async (pkg) => {
let optionDefaultsArray = await selectAllDefaultOptions(db, pkg.packageRef)
let promises = optionDefaultsArray.map(async (optionDefault) => {
let option = await selectOptionValueByOptionDefaultId(
db,
optionDefault.optionRef
)
return querySession.insertSessionKeyValue(
db,
sessionId,
option.optionCategory,
option.optionCode
)
})
return Promise.all(promises)
})
await Promise.all(p)
return packages
}
// exports
exports.getAttributeAccessInterface = getAttributeAccessInterface
exports.getPackageRefByAttributeId = getPackageRefByAttributeId
exports.getPackageByPathAndParent = getPackageByPathAndParent
exports.getPackageByPackageId = getPackageByPackageId
exports.getPackagesByType = getPackagesByType
exports.getPackageByParent = getPackageByParent
exports.getPackageIdByPathAndTypeAndVersion =
getPackageIdByPathAndTypeAndVersion
exports.getPackageSessionPackagePairBySessionId =
getPackageSessionPackagePairBySessionId
exports.getPathCrc = getPathCrc
exports.getZclPropertiesPackage = getZclPropertiesPackage
exports.insertPathCrc = insertPathCrc
exports.updatePathCrc = updatePathCrc
exports.registerTopLevelPackage = registerTopLevelPackage
exports.updateVersion = updateVersion
exports.insertSessionPackage = insertSessionPackage
exports.getSessionPackages = getSessionPackages
exports.getSessionPackagesWithTypes = getSessionPackagesWithTypes
exports.insertOptionsKeyValues = insertOptionsKeyValues
exports.selectAllOptionsValues = selectAllOptionsValues
exports.selectSpecificOptionValue = selectSpecificOptionValue
exports.insertDefaultOptionValue = insertDefaultOptionValue
exports.getSessionPackagesByType = getSessionPackagesByType
exports.getSessionGenTemplates = getSessionGenTemplates
exports.selectAllDefaultOptions = selectAllDefaultOptions
exports.selectOptionValueByOptionDefaultId = selectOptionValueByOptionDefaultId
exports.getPackagesByParentAndType = getPackagesByParentAndType
exports.getSessionZclPackages = getSessionZclPackages
exports.getSessionZclPackageIds = getSessionZclPackageIds
exports.getAllPackages = getAllPackages
exports.deleteAllSessionPackages = deleteAllSessionPackages
exports.insertPackageExtension = insertPackageExtension
exports.selectPackageExtension = selectPackageExtension
exports.selectPackageExtensionByPropertyAndEntity =
selectPackageExtensionByPropertyAndEntity
exports.deleteSessionPackage = deleteSessionPackage
exports.selectAllUiOptions = selectAllUiOptions
exports.insertSessionKeyValuesFromPackageDefaults =
insertSessionKeyValuesFromPackageDefaults
exports.getPackagesByCategoryAndType = getPackagesByCategoryAndType
exports.getPackagesByPackageIds = getPackagesByPackageIds
exports.getPackageByPathAndType = getPackageByPathAndType
exports.updatePackageIsInSync = updatePackageIsInSync