Source: Router/achievement/select.js

// @ts-check
/**
 * @import {ExpressRequestAuthorized, ExpressResponse} from '../../types.js'
 */
import  {query,UUID2hex,HEX2uuid} from '@commtool/sql-query'
import { parseTimestampToSeconds, parseTimestampToSecondsOrDefault } from '../../utils/parseTimestamp.js'
import { errorLoggerRead } from '../../utils/requestLogger.js';
import { authorizeUser } from './utilities.js';



/**
 * Handles GET request for a specific achievement.
 * 
 * @async
 * @function getAchievement
 * @param {Object} req - Express request object
 * @param {Object} req.params - Request parameters
 * @param {string} req.params.UID - UID of the achievement to retrieve
 * @param {Object} req.query - Query parameters
 * @param {string} [req.query.timestamp] - Optional timestamp for point-in-time queries
 * @param {Object} req.session - Session information
 * @param {string} req.session.user - Current user's UID
 * @param {Object} res - Express response object
 * @returns {Promise<void>} - Sends JSON response with achievement data or error message
 * @description Retrieves an achievement by UID, checking user permissions.
 *              If timestamp is provided, retrieves the achievement as it was at that time.
 *              Returns achievement details including template data and authorization status.
 */
export const getAchievement=async (req,res)=>
{
    try {
        const timestamp = parseTimestampToSeconds(req.query.timestamp ? String(req.query.timestamp) : undefined)
        const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''

        const UID=UUID2hex(req.params.UID)
        const result=await query(`SELECT ObjectBase.UID,ObjectBase.UIDBelongsTo,ObjectBase.Title,ObjectBase.Display,Member.Display AS DisplayMember,
                                Template.Display AS DisplayTemplate,
                                Member.Data AS MemberData,ObjectBase.Data AS BaseData , ALink.UIDTarget AS UIDTemplate, Template.Data AS TemplateData,
                                ObjectBase.validFrom
                                FROM ObjectBase ${asOf}
                                INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo) 
                                INNER JOIN Visible ON(Visible.UID=ObjectBase.UIDBelongsTo)
                                INNER JOIN Links AS ALink ON (ALink.UID=ObjectBase.UID AND ALink.Type='achievement')
                                INNER JOIN ObjectBase AS Template ON (Template.UID=ALink.UIDTarget  AND Template.Type='achievementT')
                                WHERE ObjectBase.UID=? AND Visible.UIDUser=?`,
                                [UID,UUID2hex(req.session.user)],
                                {cast:['UUID','json'],log:false}
                            )
                                    
        //console.log(sql)
        if(result.length===0)
        {
            res.json({success:false,message: `achievement does not exist or is not accessible for this user`})
            return
        }  
        const achievement=result[0]
        const filter= achievement.TemplateData.authorized
        achievement.editAuthorized = await authorizeUser(req.session.user,filter)
        res.json({success:true, result:achievement})
    }
    catch(e)
    {
        errorLoggerRead(e)
    }
}

/**
 * Retrieves a list of achievements for a person.
 * 
 * @async
 * @function listAchievementsPerson
 * @param {Object} req - Express request object.
 * @param {Object} req.query - Request query parameters.
 * @param {number} [req.query.timestamp] - Optional timestamp for point-in-time query.
 * @param {boolean} [req.query.unique] - If true, returns only the latest version of each achievement.
 * @param {Object} req.params - Request path parameters.
 * @param {string} req.params.UID - UID of the person whose achievements to list.
 * @param {Object} req.session - User session object.
 * @param {string} req.session.user - UID of the current user.
 * @param {Object} res - Express response object.
 * @returns {Promise<void>} - Returns a JSON response with success status and result array.
 * @description Fetches achievements associated with a person, joining with template data.
 *              Can return achievements as of a specific point in time.
 *              Can filter to return only unique (latest) achievements.
 * @throws {Error} - Logs any errors encountered during database operations.
 */
export const listAchievementsPerson =async (req,res)=>
    {
        try {
            const timestamp = parseTimestampToSecondsOrDefault(req.query.timestamp, Date.now()/1000)
            const asOf=timestamp ? `FOR SYSTEM_TIME AS OF FROM_UNIXTIME(${timestamp})` : ''
            const result= await query(`SELECT
                ObjectBase.UID ,template.UID AS UIDTemplate, ObjectBase.Title , 
                UNIX_TIMESTAMP(ObjectBase.ValidFrom) *1000 AS date, UNIX_TIMESTAMP(ObjectBase.ValidUntil)*1000 AS ValidUntil,
                IF(ObjectBase.dindex,(${timestamp}-UNIX_TIMESTAMP(ObjectBase.ValidFrom))/24/3600-JSON_VALUE(template.Data,'$.renewal'),NULL) AS expired,
                ObjectBase.SortName, template.Display AS template ,ObjectBase.Data
                FROM 
                    ObjectBase ${asOf} AS ObjectBase 
                    INNER JOIN Visible AS Visible ON (ObjectBase.UIDBelongsTo = Visible.UID )
                    INNER JOIN Links ${asOf} ON (Links.UID=ObjectBase.UID AND Links.Type='achievement')
                    INNER JOIN ObjectBase ${asOf} AS template ON (template.UID=Links.UIDTarget AND template.Type='achievementT')
                WHERE
                    ObjectBase.UIDBelongsTo = ? AND ObjectBase.Type ='achievement' AND Visible.UIDUser = ?
                
                ORDER BY
                    ObjectBase.SortName,JSON_VALUE(ObjectBase.Data,'$.date')`,
                    [UUID2hex(req.params.UID),UUID2hex(req.session.user)],
                    {cast:['UUID','json'], log:true})
    
            const rresult= req.query.unique ? result.reduce((myResult,current)=>
                {
                    const r=myResult.find(el=>(el.UID===current.UID && current.ValidUntil>el.ValidUntil))
                    if(r)
                    {
                        r.expired=current.expired 
                        r.date=current.date
                        r.ValidUntil=current.ValidUntil
                        r.Data=current.Data
                        return myResult
                    }
                    else
                    {
                        return [...myResult,current]
                    }
                },[]) 
            : result
    
            res.json({success:true,result:rresult})
        }
        catch(e)
        {
            errorLoggerRead(e)
        }
        
    }