#################### main pod documentation begin ################### =head1 NAME Zymonic::DefinitionSource::Superset - Zymonic Base for a Superset DefinitionSource =head1 SYNOPSIS Allows reading of system definitions sources from a database table =head1 DESCRIPTION Loads and returns the content of definitions within a database table along with their metadata. =head1 USAGE This should only be added from the ZymonicSuperset.xml =head1 BUGS NONE =head1 SUPPORT As in the license, Zymonic is provided without warranty or support unless purchased separately, however... If you email zymonic-support@zednax.com your issue will be noted and may receive a response. For security issues, please contact zymonic-security@zednax.com and someone will respond within 8 working hours. =head1 AUTHOR Alex Masidlover et al. CPAN ID: MODAUTHOR Zednax Limited alex.masidlover@zednax.com http://www.zednax.com =head1 COPYRIGHT This program is free software licensed under the... Zymonic Public License 1.0 The full text of the license can be found in the LICENSE file included with this module. Other licenses may be acceptable if including parts of Zymonic in larger projects, please contact Zednax for details. =head1 SEE ALSO Zymonic perl(1). =cut #################### main pod documentation end ################### package Zymonic::DefinitionSource::Superset; use strict; BEGIN { use Exporter (); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); $VERSION = '0.01'; @ISA = qw(Exporter); #Give a hoot don't pollute, do not export more than needed by default @EXPORT = qw(); @EXPORT_OK = qw(); %EXPORT_TAGS = (); } use base "Zymonic::DefinitionSource"; use XML::Simple; use Zymonic::Utils qw(debug rethrow_exception remove_duplicates); use Zymonic; use Exception::Class ( 'Zymonic::Exception::DefinitionSource::Superset' => { isa => 'Zymonic::Exception::DefinitionSource', fields => [], description => 'DB DefinitionSource related exceptions' }, ); # DB definition source is defined via XML my @REQUIRED_PARAMS = ('xmldef'); #################### subroutine header begin #################### =head2 init Usage : N/A Purpose : The init method is called by the object constructor to initialise the object fields. Returns : nothing Argument : nothing Throws : Zymonic::Exception::DefinitionSource::MissingParameters Zymonic::Exception::DefinitionSource::DB::InvalidDB Comment : See Also : =cut #################### subroutine header end #################### sub init { my $self = shift; # check for required params my @missing = grep { !$self->{$_} } @REQUIRED_PARAMS; Zymonic::Exception::DefinitionSource::MissingParameters->throw( module => 'DB', missing_parameters => join( ', ', @missing ) ) if @missing; # track znames of objects fetched by build time, so can update them $self->{objects_fetched_by_build_time} = {}; } #################### subroutine header begin #################### =head2 db Usage : $self->db()->run_query(...) Purpose : Returns a DB object as specified by the class options on this definition source. Returns : a Zymonic::DB object Argument : nothing Throws : Zymonic::Exception::DefinitionSource::DB::InvalidDB Zymonic::Exception::DefinitionSource::DB::NoDB Comment : See Also : =cut #################### subroutine header end #################### sub db { my $self = shift; return $self->{DB} || $self->{config}->{DB}; } #################### subroutine header begin #################### =head2 get_objects Usage : my @objects = $self->get_objects Purpose : Returns any Zymonic objects defined within the source. Returns : an array of hashes that define Zymonic Objects Argument : nothing Throws : Comment : See Also : =cut #################### subroutine header end #################### sub get_objects { my $self = shift; my $mtime = shift; my $buildtime = shift; my $where = shift; return () unless $self->db(); # lookup all sources from the DB newer than the incoming time my $db_definitions = []; eval { $db_definitions = $self->db()->run_query( { string => "SELECT chart_zname, chart_name, zz_chart_json, role_permission, " . " zz_superset_charts.zzluts, zz_process.initiating_user " . "FROM zz_superset_charts " . "LEFT JOIN zz_process ON zz_superset_charts.parent_process_id = zz_process.processid " . "WHERE zz_superset_charts.zzluts > ? " . ( ref($where) ? " AND " . $where->{clause} : "" ), params => [ $mtime, ( ref($where) ? @{ $where->{params} } : () ) ], } ); } or do { # could be the case that this table is checked on the first config build before # it is added to the system, therefore handle any SQL errors here my $exception = $@; if ( $exception and ( $exception->isa('Zymonic::Exception::Db::Query_Run') or $exception->isa('Zymonic::Exception::DefinitionSource::DB::NoDB') ) ) { # carry on with no defs $db_definitions = []; } else { rethrow_exception($exception); } }; # parse into list of objects my @objects = (); foreach my $db_definition ( @{$db_definitions} ) { # parse definition, don't KeepRoot here as root will be the type name # which we don't need for the def my $xmldef = { class => 'Superset', ZName => { content => $db_definition->{chart_zname} }, DisplayName => { content => $db_definition->{chart_name} }, }; # assemble metadata $xmldef->{ZZmetadata} = { sourcefile => 'zz_superset_charts', maintainer => $db_definition->{initiating_user} || '', timestamp => int( $db_definition->{zzluts} ), buildtime => $buildtime, }; # add to list in correct format push( @objects, { type => 'Filter', defn => $xmldef, } ); if ($buildtime) { # track that this object was fetched $self->{objects_fetched_by_build_time}->{$buildtime} = [] unless $self->{objects_fetched_by_build_time}->{$buildtime}; push( @{ $self->{objects_fetched_by_build_time}->{$buildtime} }, $db_definition->{chart_zname} ); } } return @objects; } #################### subroutine header begin #################### =head2 get_objects_by_type Usage : my @objects = $self->get_objects_by_type($type) Purpose : Returns specific type Zymonic objects defined within the source. Returns : an array of hashes that define Zymonic Objects Argument : type Throws : Comment : See Also : =cut #################### subroutine header end #################### sub get_objects_by_type { my $self = shift; my $type = shift; my @objects = (); if ( $type eq 'Filter' ) { $self->get_objects( 0, 0 ); } return @objects; } #################### subroutine header begin #################### =head2 most_recent_change Usage : my $epoch = $self->most_recent_change Purpose : Returns the epoch of the most recent change within this defintion source. Returns : see purpose Argument : nothing Throws : Comment : TODO See Also : =cut #################### subroutine header end #################### sub most_recent_change { my $self = shift; # max timestamp from the DB my $db_definitions = $self->db()->run_query( { string => "SELECT MAX(zzluts) AS most_recent_change FROM zz_superset_charts", params => [], } ); return $db_definitions->[0]->{most_recent_change}; } #################### subroutine header begin #################### =head2 source_ref Usage : my $source = $self->source_ref Purpose : Returns a unique reference to the DefinitonSource Returns : see purpose Argument : nothing Throws : Comment : TODO See Also : =cut #################### subroutine header end #################### sub source_ref { my $self = shift; return "zz_superset"; } #################### subroutine header begin #################### =head2 maintainer Usage : my $maintainer = $self->maintainer Purpose : Returns a maintainer (email address usually) Returns : see purpose Argument : nothing Throws : Comment : TODO See Also : =cut #################### subroutine header end #################### sub maintainer { my $self = shift; # maintainers from the DB my $db_definitions = $self->db()->run_query( { string => "SELECT DISTINCT initiating_user FROM zz_superset_charts " . "JOIN zz_process ON zz_process.processid = zz_superset_charts.parent_process_id", params => [], } ); return join( ', ', map { $_->{initiating_user} } @{$db_definitions} ); } #################### subroutine header begin #################### =head2 can_save_defs Usage : $self->can_save_defs Purpose : Returns whether you can save defs into this definition source Returns : 'true' or '' Argument : nothing Throws : Comment : See Also : =cut #################### subroutine header end #################### sub can_save_defs { my $self = shift; return ''; } #################### subroutine header begin #################### =head2 allowed_types Usage : my @types = $self->allowed_types Purpose : Returns a list of types allowed from this definition source Returns : see purpose Argument : nothing Throws : Comment : TODO See Also : =cut #################### subroutine header end #################### sub allowed_types { my $self = shift; return qw(Filter); } #################### subroutine header begin #################### =head2 set_build_time Usage : $self->set_build_time($time) Purpose : Sets the build time on the definition source Returns : see purpose Argument : build time Throws : Comment : See Also : =cut #################### subroutine header end #################### sub set_build_time { my $self = shift; my $buildtime = shift; # udpate db records for the incomig build time if ( $self->{objects_fetched_by_build_time}->{$buildtime} ) { my @znames = @{ $self->{objects_fetched_by_build_time}->{$buildtime} }; # may need to do this in chunks as if there are 1000s of znames the SQL below will fail my $db = $self->db(); my $chunk_size = 500; while (@znames) { my @znames_chunk = splice( @znames, 0, $chunk_size ); $db->run_statement( { string => "UPDATE zz_superset_chart SET build_time = ? " . "WHERE chart_zname IN (" . join( ', ', map { '?' } @znames_chunk ) . ')', params => [ $buildtime, @znames_chunk ], } ); } } } #################### subroutine header begin #################### =head2 get_def Usage : my $xml_def = $zconfig->get_def('Table', 'test'); Purpose : Returns a reference to the object definition - will be used by the rest of the Zymonic system for quick access to objects. Returns : a reference to the table definition. Argument : a scalar containing the name of the table required. Throws : Comment : See Also : =cut #################### subroutine header end #################### sub get_def { my $self = shift; my $type = shift; my $zname = shift; my @objects = $self->get_objects( 0, 0, { clause => 'chart_zname = ?', params => [$zname] } ); return map { $_->{defn} } @objects; } 1;