import {query, transaction, HEX2uuid } from '@commtool/sql-query'
import { errorLoggerUpdate } from '../../utils/requestLogger.js';
import { loadVisibleScopes, publishVisibilityDiff, resolveVisibilityOrganization } from '../visibilityEvents.js'
import {filterObjects } from '@commtool/object-filter';
// these two functions will match an object or an array of objects against the supplied sources, respectively there filters and will add to the respective targets
// so you add a person as a new member to a group so you will check all filters belonging to this group, if it matches
// a filter and will then execute the respective filter
// if andRemove= true, we will as well remove all (!) visibilities which do not match the filters of the sources
/**
* Setzt Sichtbarkeit fuer die Filter, die an `sources` haengen, und **meldet**
* die Zeilen, die dabei entstehen oder verschwinden.
*
* @param {Buffer|Buffer[]} ObjectUID - das Objekt (oder die Objekte), deren Sichtbarkeit gesetzt wird
* @param {Buffer[]} sources - die Quellen, deren `visible`/`changeable`-Filter greifen sollen
* @param {{andRemove?: boolean, organization?: string|Buffer|null}} [paras]
* `andRemove` loescht zuerst alle Zeilen der Objekte (Vererbung neu rechnen),
* `organization` geht in das Event-Payload (ohne sie greift der Rueckfall
* in `resolveVisibilityOrganization`).
* @returns {Promise<void>}
*/
export const addVisibility=async (ObjectUID,sources,paras={andRemove:false})=>
{
try {
if(!sources || sources.length===0)
return
const {andRemove,organization=null}=paras
// get all filters with Type 'visible' and 'changeable'
const includers=await query(`SELECT DISTINCT ObjectBase.UID,ObjectBase.Data,IF(jobs.Type='job',jobs.UIDBelongsTo,jobs.UID) AS userUID, ObjectBase.Type,
jobs.Type AS TargetType FROM ObjectBase
INNER JOIN Links ON (Links.UID=ObjectBase.UID AND ObjectBase.Type IN ('visible','changeable' ) AND Links.Type='list')
INNER JOIN ObjectBase AS jobs ON (Links.UIDTarget=jobs.UID)
WHERE ObjectBase.UIDBelongsTo IN (?)
GROUP BY UID
`,[sources],{cast:['json']})
// return if we do not have filters
if(!includers.length)
return
let myObjects
// get Object Data
if(Array.isArray(ObjectUID))
{
myObjects=await query( `SELECT ObjectBase.UID,Member.UID AS UIDmember,ObjectBase.Type,
Member.Data AS Data,
ObjectBase.Data AS ExtraData,
MainBase.Data AS MainBaseData, ObjectBase.dindex, ObjectBase.Title,Main.Display
FROM ObjectBase
INNER JOIN ObjectBase AS Main ON (Main.UID=ObjectBase.UIDBelongsTo)
INNER JOIN ObjectBase AS MainBase ON (MainBase.UID=ObjectBase.UIDBelongsTo AND MainBase.Type IN ('person','extern','group','ggroup'))
INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo) WHERE ObjectBase.UID IN(?)`,[ObjectUID],{cast:['json']} )
}
else
{
myObjects=await query( `SELECT ObjectBase.UID, Member.UID AS UIDmember, ObjectBase.Type,
Member.Data AS Data,
ObjectBase.Data AS ExtraData,
MainBase.Data AS MainBaseData, ObjectBase.dindex, ObjectBase.Title,Main.Display
FROM ObjectBase
INNER JOIN ObjectBase AS Main ON (Main.UID=ObjectBase.UIDBelongsTo)
INNER JOIN ObjectBase AS MainBase ON (MainBase.UID=ObjectBase.UIDBelongsTo AND MainBase.Type IN ('person','extern','group','ggroup'))
INNER JOIN Member ON (Member.UID=ObjectBase.UIDBelongsTo) WHERE ObjectBase.UID =? `,[ObjectUID] ,{cast:['json']})
}
const DObjects=myObjects.filter(o=>o.Type!=='filter')
// go through all includers and add the filtered objects, Filters with 0 results will be eliminated
let toBeAdded=[]
includers.map(include=>
{
const filter=include.Data
if(Object.keys(include.Data).length>0)
{
if(include.TargetType==='job' || include.TargetType==='extern') // super admin target is extern type!
toBeAdded=[...toBeAdded,...filterObjects(DObjects,filter).map(o=>([o.UID,include.Type,include.userUID,include.TargetType]))]
else
{
// we have now a list,dlist , email , event or eventT so we have three visibility states: visible,changeable, admin
const myType = include.Type==='visible' ? 'visible' : filter.admin ? 'admin' : 'changeable'
const add=filterObjects(DObjects,filter).map(o=>([include.userUID,myType,o.UIDmember,include.TargetType]))
toBeAdded=[...toBeAdded,...add]
/* if(include.TargetType !=='job' && add.length>0)
console.log(add.map(a=>({target:HEX2uuid(a[0]),type:a[1],'user':HEX2uuid(a[2]), targetType: include.TargetType})))*/
}
}
else
{
if(include.TargetType==='job' || include.TargetType==='extern') // super admin target is extern type!
{
toBeAdded=[...toBeAdded, ...DObjects.map(o=>([o.UID,include.Type,include.userUID,include.TargetType]))]
}
else
{
toBeAdded=[...toBeAdded, ...DObjects.map(o=>([include.userUID,include.Type,o.UIDmember,include.TargetType]))]
}
}
})
// now, if we have remaining includers with filtered data, we are going to add visiblity for all jobs
if(toBeAdded.length)
{
// Der Vorher-Stand: nur die Zeilen, die dieser Lauf ueberhaupt
// anfassen kann. `andRemove` loescht alles zu `DObjects`, sonst wird
// ausschliesslich ueber die Paare aus `toBeAdded` geschrieben — mehr
// zu lesen waere teurer, nicht genauer.
const affected=[...DObjects.map(o=>o.UID),...toBeAdded.map(el=>el[0])]
const before=await loadVisibleScopes(affected)
let oldVisibility
if(andRemove && toBeAdded.length>0)
{
// we are performing this in a transaction to not have conflict with other queries
await transaction(async(connection)=>
{
/* oldVisibility=await connection.query(`SELECT Visible.UID, Visible.Type, Visible.UIDUser, ObjectBase.Type AS ObjectType
FROM Visible INNER JOIN ObjectBase ON (ObjectBase.UID=Visible.UID)
WHERE Visible.UID IN(?) AND ObjectBase.Type IN ('person','guest','extern','job')
`,[DObjects.map(o=>o.UID)])*/
await connection.query(`DELETE FROM Visible WHERE UID IN (?)`,DObjects.map(o=>o.UID))
await connection.query(`INSERT INTO Visible (UID,Type,UIDUser) VALUES (?,?,?)
ON DUPLICATE KEY UPDATE Type=IF(VALUE(Type)='admin','admin',
IF(Type<>'admin' AND VALUE(Type)='changeable','changeable',Type))`,
toBeAdded.map(el=>([el[0],el[1],el[2],])) ,
{batch:true})
},
{
// avoid dead locks in Visible Table
beforeTransaction:async (connection)=> await connection.query(`SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;`)
})
}
else if(toBeAdded.length>0)
{
await transaction(async(connection)=>
{
await connection.query(`INSERT INTO Visible (UID,Type,UIDUser) VALUES (?,?,?)
ON DUPLICATE KEY UPDATE Type=IF(VALUE(Type)='admin','admin',IF(Type<>'admin' AND VALUE(Type)='changeable','changeable',Type))`,
toBeAdded.map(el=>([el[0],el[1],el[2]])),{batch:true})
},
{
beforeTransaction:async (connection)=> await connection.query(`SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;`)
})
}
// Bis hierher war dieser Pfad stumm: die Zeile entstand, das Event
// fehlte. Genau daran hing die Projektion auf dem toten Ast — eine
// Rechte-Aenderung im Betrieb kam nie an ([057 §16]).
const after=await loadVisibleScopes(affected)
publishVisibilityDiff(before,after,organization ?? await resolveVisibilityOrganization(affected))
}
}
catch(e)
{
errorLoggerUpdate(e)
}
}