class GoodData::Project

Constants

DEFAULT_ENVIRONMENT
DEFAULT_INVITE_MESSAGE
EMPTY_OBJECT
PROJECTS_PATH
PROJECT_PATH
SLIS_PATH
USERSPROJECTS_PATH
USER_ACCOUNT_PATH

Attributes

connection[RW]
json[RW]

Public Class Methods

[](id, opts = { client: GoodData.connection }) click to toggle source

Returns a Project object identified by given string The following identifiers are accepted

- /gdc/md/<id>
- /gdc/projects/<id>
- <id>
# File lib/gooddata/models/project.rb, line 83
def [](id, opts = { client: GoodData.connection })
  return id if id.instance_of?(GoodData::Project) || id.respond_to?(:project?) && id.project?

  if id == :all
    Project.all({ client: GoodData.connection }.merge(opts))
  else
    fail(ArgumentError, 'wrong type of argument. Should be either project ID or path') unless project_id_or_path?(id)

    id = id.match(/[a-zA-Z\d]+$/)[0] if id =~ %r{/}

    c = GoodData.get_client(opts)
    response = c.get(PROJECT_PATH % id)
    c.factory.create(Project, response)
  end
end
add_user_groups_to_dashboard(group_dashboards, dashboard, common_group_names, target_user_groups) click to toggle source
# File lib/gooddata/models/project.rb, line 503
def add_user_groups_to_dashboard(group_dashboards, dashboard, common_group_names, target_user_groups)
  group_dashboards.each do |group_dashboard|
    group_name = group_dashboard[:name]
    next if common_group_names && common_group_names[group_name]

    target_user_group = target_user_groups.select { |group| group.name == group_name }.first
    next unless target_user_group

    dashboard.grant(:member => target_user_group, :permission => group_dashboard[:permission])
  end
end
all(opts = { client: GoodData.connection }, limit = nil, offset = nil) click to toggle source

Returns an array of all projects accessible by current user

# File lib/gooddata/models/project.rb, line 72
def all(opts = { client: GoodData.connection }, limit = nil, offset = nil)
  c = GoodData.get_client(opts)
  c.user.projects(limit, offset)
end
clone_with_etl(project, options = {}) click to toggle source

Clones project along with etl and schedules

@param project [Project] Project to be cloned from @param [options] Options that are passed into project.clone @return [GoodData::Project] New cloned project

# File lib/gooddata/models/project.rb, line 108
def clone_with_etl(project, options = {})
  a_clone = project.clone(options)
  GoodData::Project.transfer_etl(project.client, project, a_clone)
  a_clone
end
collect_process_aliases(process_data, client, aliases) click to toggle source
# File lib/gooddata/models/project.rb, line 366
def collect_process_aliases(process_data, client, aliases)
  data_sources = process_data.dig('process', 'dataSources')
  unless data_sources.blank?
    data_sources.map do |data_source|
      get_data_source_alias(data_source['id'], client, aliases)
    end
  end
  component = process_data.dig('process', 'component')
  get_data_source_alias(component['configLocation']['dataSourceConfig']['id'], client, aliases) if component&.dig('configLocation', 'dataSourceConfig')
  aliases
end
create(opts = { client: GoodData.connection }, &block) click to toggle source

Create a project from a given attributes Expected keys:

  • :title (mandatory)

  • :summary

  • :template (default /projects/blank)

# File lib/gooddata/models/project.rb, line 137
def create(opts = { client: GoodData.connection }, &block)
  GoodData.logger.info "Creating project #{opts[:title]}"

  auth_token = opts[:auth_token] || opts[:token]
  if auth_token.nil? || auth_token.empty?
    opts = { auth_token: Helpers::AuthHelper.read_token }.merge(opts)
    auth_token = opts[:auth_token]
  end
  fail ArgumentError, 'You have to provide your token for creating projects as :auth_token or :token parameter' if auth_token.nil? || auth_token.empty?

  project = create_object(opts)
  project.save
  # until it is enabled or deleted, recur. This should still end if there
  # is a exception thrown out from RESTClient. This sometimes happens from
  # WebApp when request is too long
  while project.state.to_s != 'enabled'
    if project.deleted?
      # if project is switched to deleted state, fail. This is usually problem of creating a template which is invalid.
      fail 'Project was marked as deleted during creation. This usually means you were trying to create from template and it failed.'
    end
    sleep(3)
    project.reload!
  end

  if block
    GoodData.with_project(project) do |p|
      block.call(p)
    end
  end
  sleep 3
  project
end
create_from_blueprint(blueprint, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 178
def create_from_blueprint(blueprint, options = {})
  GoodData::Model::ProjectCreator.migrate(options.merge(spec: blueprint, client: client))
end
create_object(data = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 114
def create_object(data = {})
  c = GoodData.get_client(data)
  new_data = GoodData::Helpers.deep_dup(EMPTY_OBJECT).tap do |d|
    d['project']['meta']['title'] = data[:title]
    d['project']['meta']['summary'] = data[:summary] if data[:summary]
    d['project']['meta']['projectTemplate'] = data[:template] if data[:template]
    d['project']['content']['guidedNavigation'] = data[:guided_navigation] if data[:guided_navigation]

    token = data[:auth_token] || data[:token]

    d['project']['content']['authorizationToken'] = token if token
    d['project']['content']['driver'] = data[:driver] if data[:driver]
    d['project']['content']['environment'] = data[:environment] if data[:environment]
  end
  c.create(Project, new_data)
end
dashboard_user_groups(user_groups, dashboard) click to toggle source
# File lib/gooddata/models/project.rb, line 475
def dashboard_user_groups(user_groups, dashboard)
  group_dashboards = []
  dashboard_grantees = dashboard.grantees['granteeURIs']['items'].select { |item| item['aclEntryURI']['grantee'].include?('/usergroups/') }

  dashboard_grantees.each do |dashboard_grantee|
    permission = dashboard_grantee['aclEntryURI']['permission']
    group_id = dashboard_grantee['aclEntryURI']['grantee'].split('/').last
    user_group = user_groups.select { |group| group.links['self'].split('/').last == group_id }.first
    next unless user_group

    group_dashboards << {
      name: user_group.name,
      user_group: user_group,
      permission: permission
    }
  end
  group_dashboards
end
find(opts = { client: GoodData.connection }) click to toggle source
# File lib/gooddata/models/project.rb, line 170
def find(opts = { client: GoodData.connection })
  c = GoodData.get_client(opts)
  user = c.user
  user.projects['projects'].map do |project|
    c.create(GoodData::Project, project)
  end
end
get_data_source_alias(data_source_id, client, aliases) click to toggle source
# File lib/gooddata/models/project.rb, line 378
def get_data_source_alias(data_source_id, client, aliases)
  unless aliases[data_source_id]
    data_source = GoodData::DataSource.from_id(data_source_id, client: client)
    if data_source&.alias
      aliases[data_source_id] = {
        :type => data_source.type,
        :alias => data_source.alias
      }
    end
  end
  aliases[data_source_id]
end
get_dataset_mapping(from_project) click to toggle source
# File lib/gooddata/models/project.rb, line 260
def get_dataset_mapping(from_project)
  GoodData::DatasetMapping.get(:client => from_project.client, :project => from_project)
end
get_ldm_layout(from_project) click to toggle source
# File lib/gooddata/models/project.rb, line 276
def get_ldm_layout(from_project)
  GoodData::LdmLayout.get(:client => from_project.client, :project => from_project)
rescue StandardError => e
  GoodData.logger.warn "An unexpected error when get ldm layout. Error: #{e.message}"
  GoodData::LdmLayout::DEFAULT_EMPTY_LDM_LAYOUT
end
new(json) click to toggle source

Initializes object instance from raw wire JSON

@param json Json used for initialization

Calls superclass method
# File lib/gooddata/models/project.rb, line 1271
def initialize(json)
  super
  @json = json
  @log_formatter = GoodData::ProjectLogFormatter.new(self)
end
project_id_or_path?(id) click to toggle source
# File lib/gooddata/models/project.rb, line 99
def project_id_or_path?(id)
  id.to_s =~ %r{^(\/gdc\/(projects|md)\/)?[a-zA-Z\d]+$}
end
remove_user_groups_from_dashboard(group_dashboards, dashboard, common_group_names) click to toggle source
# File lib/gooddata/models/project.rb, line 494
def remove_user_groups_from_dashboard(group_dashboards, dashboard, common_group_names)
  group_dashboards.each do |group_dashboard|
    group_name = group_dashboard[:name]
    next if common_group_names && common_group_names[group_name]

    dashboard.revoke(:member => group_dashboard[:user_group], :permission => group_dashboard[:permission])
  end
end
replace_data_source_ids(data_sources, client, aliases) click to toggle source
# File lib/gooddata/models/project.rb, line 401
def replace_data_source_ids(data_sources, client, aliases)
  array_data_sources = []
  if data_sources && !data_sources.empty?
    data_sources.map do |data_source|
      new_id = verify_data_source_alias(aliases[data_source[:id]], client)
      array_data_sources.push(:id => new_id)
    end
  end
  array_data_sources
end
replace_process_data_source_ids(process_data, client, aliases) click to toggle source
# File lib/gooddata/models/project.rb, line 391
def replace_process_data_source_ids(process_data, client, aliases)
  component = process_data.dig(:process, :component)
  if component&.dig(:configLocation, :dataSourceConfig)
    the_alias = aliases[component[:configLocation][:dataSourceConfig][:id]]
    process_data[:process][:component][:configLocation][:dataSourceConfig][:id] = verify_data_source_alias(the_alias, client)
  end
  process_data[:process][:dataSources] = replace_data_source_ids(process_data[:process][:dataSources], client, aliases)
  process_data
end
save_ldm_layout(ldm_layout_json, to_project) click to toggle source
# File lib/gooddata/models/project.rb, line 283
def save_ldm_layout(ldm_layout_json, to_project)
  ldm_layout = GoodData::LdmLayout.new(ldm_layout_json)
  begin
    ldm_layout.save(:client => to_project.client, :project => to_project)
    status = "OK"
  rescue StandardError => e
    GoodData.logger.warn "An unexpected error when save ldm layout. Error: #{e.message}"
    status = "Failed"
  end

  {
    to: to_project.pid,
    status: status
  }
end
transfer_color_palette(from_project, to_project) click to toggle source
# File lib/gooddata/models/project.rb, line 726
def transfer_color_palette(from_project, to_project)
  colors = from_project.current_color_palette
  to_project.create_custom_color_palette(colors) unless colors.empty?
end
transfer_dashboard_permission(from_project, to_project, source_dashboards, target_dashboards) click to toggle source
# File lib/gooddata/models/project.rb, line 453
def transfer_dashboard_permission(from_project, to_project, source_dashboards, target_dashboards)
  source_user_groups = from_project.user_groups
  target_user_groups = to_project.user_groups

  source_dashboards.each do |source_dashboard|
    target_dashboard = target_dashboards.select { |dash| dash.title == source_dashboard.title }.first
    next unless target_dashboard

    begin
      source_group_dashboards = dashboard_user_groups(source_user_groups, source_dashboard)
      target_group_dashboards = dashboard_user_groups(target_user_groups, target_dashboard)

      common_group_names = source_group_dashboards.flat_map { |s| target_group_dashboards.select { |t| t[:name] == s[:name] } }.map { |x| [x[:name], true] }.to_h

      remove_user_groups_from_dashboard(target_group_dashboards, target_dashboard, common_group_names)
      add_user_groups_to_dashboard(source_group_dashboards, target_dashboard, common_group_names, target_user_groups)
    rescue StandardError => e
      GoodData.logger.warn "Failed to synchronize dashboard permission from project: '#{from_project.title}', PID: '#{from_project.pid}' to project: '#{to_project.title}', PID: '#{to_project.pid}', dashboard: '#{target_dashboard.title}', dashboard uri: '#{target_dashboard.uri}' . Error: #{e.message} - #{e}" # rubocop:disable Metrics/LineLength
    end
  end
end
transfer_etl(client, from_project, to_project) click to toggle source

Clones project along with etl and schedules.

@param client [GoodData::Rest::Client] GoodData client to be used for connection @param from_project [GoodData::Project | GoodData::Segment | GoodData:Client | String] Object to be cloned from. Can be either segment in which case we take the master, client in which case we take its project, string in which case we treat is as an project object or directly project @param to_project [GoodData::Project | GoodData::Segment | GoodData:Client | String]

# File lib/gooddata/models/project.rb, line 238
def transfer_etl(client, from_project, to_project)
  from_project = case from_project
                 when GoodData::Client
                   from_project.project
                 when GoodData::Segment
                   from_project.master_project
                 else
                   client.projects(from_project)
                 end

  to_project = case to_project
               when GoodData::Client
                 to_project.project
               when GoodData::Segment
                 to_project.master_project
               else
                 client.projects(to_project)
               end
  transfer_processes(from_project, to_project)
  transfer_schedules(from_project, to_project)
end
transfer_output_stage(from_project, to_project, options) click to toggle source
# File lib/gooddata/models/project.rb, line 199
def transfer_output_stage(from_project, to_project, options)
  from_prj_output_stage = from_project.add.output_stage
  output_stage_prefix = options[:ads_output_stage_prefix] || from_prj_output_stage.output_stage_prefix
  output_stage_uri = options[:ads_output_stage_uri] || from_prj_output_stage.schema
  if from_project.processes.any? { |p| p.type == :dataload }
    if to_project.processes.any? { |p| p.type == :dataload }
      to_project.add.output_stage.schema = output_stage_uri
      to_project.add.output_stage.output_stage_prefix = output_stage_prefix
      to_project.add.output_stage.save
    else
      from_server = from_project.client.connection.server.url
      to_server = to_project.client.connection.server.url
      if from_server != to_server && options[:ads_output_stage_uri].nil?
        raise "Cannot transfer output stage from #{from_server} to #{to_server}. " \
              'It is not possible to transfer output stages between ' \
              'different domains. Please specify an address of an output ' \
              'stage that is in the same domain as the target project ' \
              'using the "ads_output_stage_uri" parameter.'
      end

      to_project.add.output_stage = GoodData::AdsOutputStage.create(
        client: to_project.client,
        ads: output_stage_uri,
        client_id: from_prj_output_stage.client_id,
        output_stage_prefix: output_stage_prefix,
        project: to_project
      )
    end
  end
end
transfer_processes(from_project, to_project, options = {}) click to toggle source

@param from_project The source project @param to_project The target project @param options Optional parameters @option ads_output_stage_uri Uri of the source output stage. It must be in the same domain as the target project.

# File lib/gooddata/models/project.rb, line 303
def transfer_processes(from_project, to_project, options = {})
  options = GoodData::Helpers.symbolize_keys(options)
  aliases = {}
  to_project_processes = to_project.processes
  additional_hidden_params = options[:additional_hidden_params] || {}
  result = from_project.processes.uniq(&:name).map do |process|
    fail "The process name #{process.name} must be unique in transferred project #{to_project}" if to_project_processes.count { |p| p.name == process.name } > 1
    next if process.type == :dataload || process.add_v2_component?
    collect_process_aliases(process.data, from_project.client, aliases)

    to_process = to_project_processes.find { |p| p.name == process.name }

    data_sources = GoodData::Helpers.symbolize_keys_recursively!(process.data_sources)
    data_sources = replace_data_source_ids(data_sources, to_project.client, aliases)
    to_process = if process.path
                   to_process.delete if to_process
                   Process.deploy_from_appstore(process.path, name: process.name, client: to_project.client, project: to_project, data_sources: data_sources)
                 elsif process.component
                   to_process.delete if to_process
                   process_hash = GoodData::Helpers::DeepMergeableHash[GoodData::Helpers.symbolize_keys(process.to_hash)].deep_merge(additional_hidden_params)
                   process_hash = replace_process_data_source_ids(process_hash, to_project.client, aliases)
                   Process.deploy_component(process_hash, project: to_project, client: to_project.client)
                 else
                   Dir.mktmpdir('etl_transfer') do |dir|
                     dir = Pathname(dir)
                     filename = dir + 'process.zip'
                     File.open(filename, 'w') do |f|
                       f << process.download
                     end
                     if to_process
                       to_process.deploy(filename, type: process.type, name: process.name, data_sources: data_sources)
                     else
                       to_project.deploy_process(filename, type: process.type, name: process.name, data_sources: data_sources)
                     end
                   end
                 end

    {
      from: from_project.pid,
      to: to_project.pid,
      name: process.name,
      status: to_process ? 'successful' : 'failed'
    }
  end

  transfer_output_stage(from_project, to_project, options)
  result << {
    from: from_project.pid,
    to: to_project.pid,
    name: 'Automated Data Distribution',
    status: 'successful'
  }

  res = (from_project.processes + to_project.processes).map { |p| [p, p.name, p.type] }
  res.group_by { |x| [x[1], x[2]] }
    .select { |_, procs| procs.length == 1 && procs[2] != :dataload }
    .reject { |_, procs| procs.first.first.add_v2_component? }
    .flat_map { |_, procs| procs.select { |p| p[0].project.pid == to_project.pid }.map { |p| p[0] } }
    .peach(&:delete)

  result.compact
end
transfer_schedules(from_project, to_project, has_cycle_trigger = false) click to toggle source

Clones project along with etl and schedules.

@param client [GoodData::Rest::Client] GoodData client to be used for connection @param from_project [GoodData::Project | GoodData::Segment | GoodData:Client | String] Object to be cloned from. Can be either segment in which case we take the master, client in which case we take its project, string in which case we treat is as an project object or directly project.

# File lib/gooddata/models/project.rb, line 522
def transfer_schedules(from_project, to_project, has_cycle_trigger = false)
  to_project_processes = to_project.processes.sort_by(&:name)
  from_project_processes = from_project.processes.sort_by(&:name)
  from_project_processes.reject!(&:add_v2_component?)

  GoodData.logger.debug("Processes in from project #{from_project.pid}: #{from_project_processes.map(&:name).join(', ')}")
  GoodData.logger.debug("Processes in to project #{to_project.pid}: #{to_project_processes.map(&:name).join(', ')}")

  cache = to_project_processes
            .zip(from_project_processes)
            .flat_map do |remote, local|
              local.schedules.map do |schedule|
                [remote, local, schedule]
              end
            end

  remote_schedules = to_project.schedules
  remote_stuff = remote_schedules.map do |s|
    v = s.to_hash
    after_schedule = remote_schedules.find { |s2| s.trigger_id == s2.obj_id }
    v[:after] = s.trigger_id && after_schedule && after_schedule.name
    v[:remote_schedule] = s
    v[:params] = v[:params].except("EXECUTABLE", "PROCESS_ID")
    v.compact
  end

  local_schedules = from_project.schedules
  local_stuff = local_schedules.map do |s|
    v = s.to_hash
    after_schedule = local_schedules.find { |s2| s.trigger_id == s2.obj_id }
    after_process_schedule = from_project_processes.find { |p| after_schedule && p.obj_id == after_schedule.process_id }
    v[:after] = s.trigger_id && after_process_schedule && after_schedule && after_schedule.name
    v[:trigger_execution_status] = s.trigger_execution_status
    v[:remote_schedule] = s
    v[:params] = v[:params].except("EXECUTABLE", "PROCESS_ID")
    v.compact
  end

  diff = GoodData::Helpers.diff(remote_stuff, local_stuff, key: :name, fields: [:name, :cron, :after, :params, :hidden_params, :reschedule, :state])
  stack = diff[:added].map do |x|
    [:added, x]
  end

  stack += diff[:changed].map do |x|
    [:changed, x]
  end

  schedule_cache = remote_schedules.reduce({}) do |a, e|
    a[e.name] = e
    a
  end

  results = []
  update_trigger_schedules = []
  loop do # rubocop:disable Metrics/BlockLength
    break if stack.empty?
    state, changed_schedule = stack.shift
    lazy_update_trigger_info = false
    if state == :added
      schedule_spec = changed_schedule
      if schedule_spec[:after] && !schedule_cache[schedule_spec[:after]]
        if has_cycle_trigger
          # The schedule is triggered by another schedule
          lazy_update_trigger_info = true
        else
          stack << [state, schedule_spec]
          next
        end
      end

      remote_process, process_spec = cache.find do |_remote, local, schedule|
        (schedule_spec[:process_id] == local.process_id) && (schedule.name == schedule_spec[:name])
      end

      next unless remote_process || process_spec

      GoodData.logger.info("Creating schedule #{schedule_spec[:name]} for process #{remote_process.name}")

      executable = nil
      if process_spec.type != :dataload
        executable = schedule_spec[:executable] || (process_spec.type == :ruby ? 'main.rb' : 'main.grf')
      end

      params = schedule_parameters(schedule_spec)

      if lazy_update_trigger_info
        # Temporary update nil for trigger info. The trigger info will be update late after transfer all schedules
        created_schedule = remote_process.create_schedule(nil, executable, params)
        update_trigger_schedules << {
          state: :added,
          schedule: created_schedule,
          after: schedule_spec[:after]
        }
      else
        created_schedule = remote_process.create_schedule(schedule_spec[:cron] || schedule_cache[schedule_spec[:after]], executable, params)
      end

      schedule_cache[created_schedule.name] = created_schedule

      results << {
        state: :added,
        process: remote_process,
        schedule: created_schedule
      }
    else
      schedule_spec = changed_schedule[:new_obj]
      if schedule_spec[:after] && !schedule_cache[schedule_spec[:after]]
        if has_cycle_trigger
          # The schedule is triggered by another schedule
          lazy_update_trigger_info = true
        else
          stack << [state, schedule_spec]
          next
        end
      end

      remote_process, process_spec = cache.find do |i|
        i[2].name == schedule_spec[:name]
      end

      schedule = changed_schedule[:old_obj][:remote_schedule]

      GoodData.logger.info("Updating schedule #{schedule_spec[:name]} for process #{remote_process.name}")

      schedule.params = (schedule_spec[:params] || {})
      schedule.cron = schedule_spec[:cron] if schedule_spec[:cron]

      unless lazy_update_trigger_info
        schedule.after = schedule_cache[schedule_spec[:after]] if schedule_spec[:after]
        schedule.trigger_execution_status = schedule_cache[schedule_spec[:trigger_execution_status]] if schedule_spec[:after]
      end

      schedule.hidden_params = schedule_spec[:hidden_params] || {}
      if process_spec.type != :dataload
        schedule.executable = schedule_spec[:executable] || (process_spec.type == :ruby ? 'main.rb' : 'main.grf')
      end

      schedule.reschedule = schedule_spec[:reschedule]
      schedule.name = schedule_spec[:name]
      schedule.state = schedule_spec[:state]
      schedule.save
      schedule_cache[schedule.name] = schedule

      if lazy_update_trigger_info
        update_trigger_schedules << {
          state: :changed,
          schedule: schedule,
          after: schedule_spec[:after],
          trigger_execution_status: schedule_spec[:trigger_execution_status]
        }
      end

      results << {
        state: :changed,
        process: remote_process,
        schedule: schedule
      }
    end
  end

  if has_cycle_trigger
    update_trigger_schedules.each do |update_trigger_schedule|
      working_schedule = update_trigger_schedule[:schedule]
      working_schedule.after = schedule_cache[update_trigger_schedule[:after]]
      working_schedule.trigger_execution_status = schedule_cache[update_trigger_schedule[:trigger_execution_status]] if update_trigger_schedule[:state] == :changed

      # Update trigger info
      working_schedule.save

      # Update transfer result
      results.each do |transfer_result|
        transfer_result[:schedule] = working_schedule if transfer_result[:schedule].obj_id == working_schedule.obj_id
      end
    end
  end

  diff[:removed].each do |removed_schedule|
    GoodData.logger.info("Removing schedule #{removed_schedule[:name]}")

    removed_schedule[:remote_schedule].delete

    results << {
      state: :removed,
      process: removed_schedule.process,
      schedule: removed_schedule
    }
  end

  results
end
transfer_tagged_stuff(from_project, to_project, tag) click to toggle source
# File lib/gooddata/models/project.rb, line 713
def transfer_tagged_stuff(from_project, to_project, tag)
  GoodData.logger.info("Transferring tagged stuff - #{tag}")

  objects = from_project.find_by_tag(tag)

  if objects.any?
    GoodData.logger.info("\n#{JSON.pretty_generate(objects)}")
    from_project.partial_md_export(objects, project: to_project)
  else
    GoodData.logger.info('No tagged objects to transfer')
  end
end
transfer_user_groups(from_project, to_project) click to toggle source
# File lib/gooddata/models/project.rb, line 434
def transfer_user_groups(from_project, to_project)
  from_project.user_groups.map do |ug|
    # migrate groups
    new_group = to_project.user_groups.select { |group| group.name == ug.name }.first
    new_group_status = new_group ? 'modified' : 'created'
    new_group ||= UserGroup.create(:name => ug.name, :description => ug.description, :project => to_project)
    new_group.project = to_project
    new_group.description = ug.description
    new_group.save

    {
      from: from_project.pid,
      to: to_project.pid,
      user_group: new_group.name,
      status: new_group_status
    }
  end
end
update_dataset_mapping(model_mapping_json, to_project) click to toggle source
# File lib/gooddata/models/project.rb, line 264
def update_dataset_mapping(model_mapping_json, to_project)
  dataset_mapping = GoodData::DatasetMapping.new(model_mapping_json)
  res = dataset_mapping.save(:client => to_project.client, :project => to_project)
  status = res&.dig('datasetMappings', 'items').nil? ? "Failed" : "OK"
  count = "OK".eql?(status) ? res['datasetMappings']['items'].length : 0
  {
    to: to_project.pid,
    count: count,
    status: status
  }
end
user_csv_import(row) click to toggle source

Takes one CSV line and creates hash from data extracted

@param row CSV row

# File lib/gooddata/models/project.rb, line 185
def user_csv_import(row)
  {
    'user' => {
      'content' => {
        'email' => row[0],
        'login' => row[1],
        'firstname' => row[2],
        'lastname' => row[3]
      },
      'meta' => {}
    }
  }
end
verify_data_source_alias(ds_alias, client) click to toggle source

Verify whether the data source exists in the domain using its alias

@param [String] ds_alias The data source’s alias @param [Object] client The Rest Client object @return [String] Id of the data source or failed with the reason

# File lib/gooddata/models/project.rb, line 417
def verify_data_source_alias(ds_alias, client)
  domain = client.connection.server.url
  fail "The data source alias is empty, check your data source configuration." unless ds_alias

  uri = "/gdc/dataload/dataSources/internal/availableAlias?alias=#{ds_alias[:alias]}"
  res = client.get(uri)
  fail "Unable to get information about the Data Source '#{ds_alias[:alias]}' in the domain '#{domain}'" unless res
  fail "Unable to find the #{ds_alias[:type]} Data Source '#{ds_alias[:alias]}' in the domain '#{domain}'" if res['availableAlias']['available']

  ds_type = res['availableAlias']['existingDataSource']['type']
  if ds_type && ds_type != ds_alias[:type]
    fail "Wrong Data Source type - the '#{ds_type}' type is expected but the Data Source '#{ds_alias[:alias]}' in the domain '#{domain}' has the '#{ds_alias[:type]}' type"
  else
    res['availableAlias']['existingDataSource']['id']
  end
end

Private Class Methods

schedule_parameters(schedule_spec) click to toggle source
# File lib/gooddata/models/project.rb, line 733
def schedule_parameters(schedule_spec)
  {
    params: schedule_spec[:params],
    hidden_params: schedule_spec[:hidden_params],
    name: schedule_spec[:name],
    reschedule: schedule_spec[:reschedule],
    state: schedule_spec[:state],
    trigger_execution_status: schedule_spec[:trigger_execution_status]
  }
end

Public Instance Methods

add() click to toggle source
# File lib/gooddata/models/project.rb, line 2200
def add
  @add ||= GoodData::AutomatedDataDistribution.new(self)
  @add
end
add_dashboard(dashboard) click to toggle source
# File lib/gooddata/models/project.rb, line 745
def add_dashboard(dashboard)
  GoodData::Dashboard.create(dashboard, :client => client, :project => self)
end
Also aliased as: create_dashboard
add_data_permissions(filters, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 2116
def add_data_permissions(filters, options = {})
  GoodData.logger.info("Synchronizing #{filters.count} filters in project #{pid}")
  GoodData::UserFilterBuilder.execute_mufs(filters, { client: client, project: self }.merge(options))
end
add_measure(metric, options = {})
Alias for: add_metric
add_metric(metric, options = {}) click to toggle source

Creates a metric in a project

@param [options] Optional report options @return [GoodData::Report] Instance of new report

# File lib/gooddata/models/project.rb, line 767
def add_metric(metric, options = {})
  default = { client: client, project: self }
  if metric.is_a?(String)
    GoodData::Metric.xcreate(metric, options.merge(default))
  else
    GoodData::Metric.xcreate(options[:expression], metric.merge(options.merge(default)))
  end
end
add_report(options = {}) click to toggle source

Creates new instance of report in context of project

@param [options] Optional report options @return [GoodData::Report] Instance of new report

# File lib/gooddata/models/project.rb, line 785
def add_report(options = {})
  report = GoodData::Report.create(options.merge(client: client, project: self))
  report.save
end
Also aliased as: create_report
add_report_definition(json) click to toggle source

Creates new instance of report definition in context of project This report definition can be used for creating of GoodData::Report

@param [json] Raw report definition json @return [GoodData::ReportDefinition] Instance of new report definition

# File lib/gooddata/models/project.rb, line 797
def add_report_definition(json)
  rd = GoodData::ReportDefinition.new(json)
  rd.client = client
  rd.project = self
  rd.save
end
Also aliased as: create_report_definition
add_user(login, desired_roles, options = {})
Alias for: set_user_roles
add_user_group(data) click to toggle source
# File lib/gooddata/models/project.rb, line 751
def add_user_group(data)
  g = GoodData::UserGroup.create(data.merge(project: self))

  begin
    g.save
  rescue RestClient::Conflict
    user_groups(data[:name])
  end
end
Also aliased as: create_group
add_users(list, options = {})
Alias for: set_users_roles
add_variable_permissions(filters, var, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 2121
def add_variable_permissions(filters, var, options = {})
  GoodData::UserFilterBuilder.execute_variables(filters, var, { client: client, project: self }.merge(options))
end
am_i_admin?() click to toggle source

Returns an indication whether current user is admin in this project

@return [Boolean] True if user has admin role in the project, false otherwise.

# File lib/gooddata/models/project.rb, line 809
def am_i_admin?
  user_has_role?(client.user, 'admin')
end
analytical_dashboards(id = :all) click to toggle source

Helper for getting analytical dashboards (KD dashboards) of a project

@param id [String | Number | Object] Anything that you can pass to GoodData::Dashboard @return [GoodData::AnalyticalDashboard | Array<GoodData::AnalyticalDashboard>] dashboard instance or list

# File lib/gooddata/models/project.rb, line 1009
def analytical_dashboards(id = :all)
  GoodData::AnalyticalDashboard[id, project: self, client: client]
end
attribute_by_identifier(identifier) click to toggle source
# File lib/gooddata/models/project.rb, line 830
def attribute_by_identifier(identifier)
  GoodData::Attribute.find_first_by_identifier(identifier, project: self, client: client)
end
attribute_by_title(title) click to toggle source
# File lib/gooddata/models/project.rb, line 838
def attribute_by_title(title)
  GoodData::Attribute.find_first_by_title(title, project: self, client: client)
end
attributes(id = :all) click to toggle source

Helper for getting attributes of a project

@param [String | Number | Object] Anything that you can pass to GoodData::Attribute @return [GoodData::Attribute | Array<GoodData::Attribute>] fact instance or list

# File lib/gooddata/models/project.rb, line 817
def attributes(id = :all)
  GoodData::Attribute[id, project: self, client: client]
end
attributes_by_identifier(identifier) click to toggle source
# File lib/gooddata/models/project.rb, line 834
def attributes_by_identifier(identifier)
  GoodData::Attribute.find_by_identifier(identifier, project: self, client: client)
end
attributes_by_title(title) click to toggle source
# File lib/gooddata/models/project.rb, line 842
def attributes_by_title(title)
  GoodData::Attribute.find_by_title(title, project: self, client: client)
end
blueprint(options = {}) click to toggle source

Gets project blueprint from the server

@return [GoodData::ProjectRole] Project role if found

# File lib/gooddata/models/project.rb, line 849
def blueprint(options = {})
  options = { include_ca: true }.merge(options)
  result = client.get("/gdc/projects/#{pid}/model/view", params: { includeDeprecated: true, includeGrain: true, includeCA: options[:include_ca] })

  polling_url = result['asyncTask']['link']['poll']
  model = client.poll_on_code(polling_url, options)
  bp = GoodData::Model::FromWire.from_wire(model, options)
  bp.title = title
  bp
end
browser_uri(options = {}) click to toggle source

Returns web interface URI of project

@return [String] Project URL

# File lib/gooddata/models/project.rb, line 863
def browser_uri(options = {})
  grey = options[:grey]
  server = client.connection.server_url
  if grey
    "#{server}#{uri}"
  else
    "#{server}/#s=#{uri}"
  end
end
check_groups(specified_groups, user_groups_cache = nil, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 2026
def check_groups(specified_groups, user_groups_cache = nil, options = {})
  current_user_groups = user_groups if user_groups_cache.nil? || user_groups_cache.empty?
  groups = current_user_groups.map(&:name)
  missing_groups = []
  change_groups = {}
  specified_groups.each do |group|
    found_group = groups.find { |name| name.casecmp(group).zero? }
    if found_group.nil?
      missing_groups << group
    else
      # Change groups when they have similar group name with difference of case sensitivity
      if found_group != group
        change_groups[group] = found_group
        GoodData.logger.warn("Group with name #{group} is existed in project with name #{found_group}.")
      end
    end
  end
  if options[:create_non_existing_user_groups]
    missing_groups.each do |g|
      GoodData.logger.info("Creating group #{g}")
      create_group(name: g, description: g)
    end
  else
    unless missing_groups.empty?
      fail 'All groups have to be specified before you try to import ' \
        'users. Groups that are currently in project are ' \
        "#{groups.join(',')} and you asked for #{missing_groups.join(',')}"
    end
  end
  [current_user_groups, change_groups]
end
clone(options = {}) click to toggle source

Clones project

@param options [Hash] Export options @option options [Boolean] :data Clone project with data @option options [Boolean] :users Clone project with users @option options [Boolean] :exclude_schedules Specifies whether to include scheduled emails @return [GoodData::Project] Newly created project

# File lib/gooddata/models/project.rb, line 880
def clone(options = {})
  a_title = options[:title] || "Clone of #{title}"

  begin
    # Create the project first so we know that it is passing.
    # What most likely is wrong is the token and the export actaully takes majority of the time
    new_project = GoodData::Project.create(options.merge(:title => a_title, :client => client, :driver => content[:driver]))
    export_token = export_clone(options)
    new_project.import_clone(export_token)
  rescue
    new_project.delete if new_project
    raise
  end
end
compute_measure(expression)
Alias for: compute_metric
compute_metric(expression) click to toggle source
# File lib/gooddata/models/project.rb, line 982
def compute_metric(expression)
  GoodData::Metric.xexecute(expression, client: client, project: self)
end
Also aliased as: compute_measure
compute_report(spec = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 978
def compute_report(spec = {})
  GoodData::ReportDefinition.execute(spec.merge(client: client, project: self))
end
computed_attributes(id = :all) click to toggle source
# File lib/gooddata/models/project.rb, line 821
def computed_attributes(id = :all)
  attrs = attributes(id)
  if attrs.is_a?(GoodData::Attribute)
    attrs.computed_attribute? ? attrs : nil
  else
    attrs.select(&:computed_attribute?)
  end
end
create_custom_color_palette(colors) click to toggle source
# File lib/gooddata/models/project.rb, line 2249
def create_custom_color_palette(colors)
  GoodData::StyleSetting.create(colors, client: client, project: self)
end
create_dashboard(dashboard)
Alias for: add_dashboard
create_group(data)
Alias for: add_user_group
create_measure(metric, options = {})
Alias for: add_metric
create_metric(metric, options = {})
Alias for: add_metric
create_output_stage(ads, opts = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 2237
def create_output_stage(ads, opts = {})
  add.create_output_stage(ads, opts)
end
create_report(options = {})
Alias for: add_report
create_report_definition(json)
create_schedule(process, date, executable, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 988
def create_schedule(process, date, executable, options = {})
  s = GoodData::Schedule.create(process, date, executable, options.merge(client: client, project: self))
  s.save
end
create_users(list, options = {})
Alias for: set_users_roles
create_variable(data) click to toggle source
# File lib/gooddata/models/project.rb, line 993
def create_variable(data)
  GoodData::Variable.create(data, client: client, project: self)
end
current_color_palette() click to toggle source
# File lib/gooddata/models/project.rb, line 2245
def current_color_palette
  GoodData::StyleSetting.current(client: client, project: self)
end
dashboards(id = :all) click to toggle source

Helper for getting dashboards of a project

@param id [String | Number | Object] Anything that you can pass to GoodData::Dashboard @return [GoodData::Dashboard | Array<GoodData::Dashboard>] dashboard instance or list

# File lib/gooddata/models/project.rb, line 1001
def dashboards(id = :all)
  GoodData::Dashboard[id, project: self, client: client]
end
data_permissions(id = :all) click to toggle source
# File lib/gooddata/models/project.rb, line 1013
def data_permissions(id = :all)
  GoodData::MandatoryUserFilter[id, client: client, project: self]
end
Also aliased as: user_filters
dataset_mapping() click to toggle source
# File lib/gooddata/models/project.rb, line 2209
def dataset_mapping
  GoodData::Project.get_dataset_mapping(self)
end
datasets(id = :all) click to toggle source

Gives you list of datasets. These are not blueprint datasets but model datasets coming from meta data server.

@param id [Symbol | String | GoodData::MdObject] Export options @return [Array<GoodData::Dataset> | GoodData::Dataset] Dataset or list of datasets in the project

# File lib/gooddata/models/project.rb, line 900
def datasets(id = :all)
  GoodData::Dataset[id, project: self, client: client]
end
delete() click to toggle source

Deletes project

# File lib/gooddata/models/project.rb, line 1019
def delete
  fail "Project '#{title}' with id #{uri} is already deleted" if deleted?
  client.delete(uri)
end
delete_all_data(options = {}) click to toggle source

Helper for getting rid of all data in the project

@option options [Boolean] :force has to be added otherwise the operation is not performed @return [Array] Result of executing MAQLs

# File lib/gooddata/models/project.rb, line 1035
def delete_all_data(options = {})
  return false unless options[:force]
  begin
    datasets.reject(&:date_dimension?).pmap(&:delete_data)
  rescue MaqlExecutionError => e
    # This is here so that we do not throw out exceptions on synchornizing date dimensions
    # Currently there is no reliable way how to tell it is a date dimension
    fail e unless GoodData::Helpers.interpolate_error_messages(e.data['wTaskStatus']['messages']) == ["Internal error [handle_exception, hide_internal]."]
  end
end
delete_dashboards() click to toggle source

Deletes dashboards for project

# File lib/gooddata/models/project.rb, line 1047
def delete_dashboards
  Dashboard.all.map { |data| Dashboard[data['link']] }.each(&:delete)
end
deleted?() click to toggle source

Returns true if project is in deleted state

@return [Boolean] Returns true if object deleted. False otherwise.

# File lib/gooddata/models/project.rb, line 1027
def deleted?
  state == :deleted
end
deploy_process(path, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 1051
def deploy_process(path, options = {})
  GoodData::Process.deploy(path, options.merge(client: client, project: self))
end
dimensions(id = :all) click to toggle source
# File lib/gooddata/models/project.rb, line 904
def dimensions(id = :all)
  GoodData::Dimension[id, client: client, project: self]
end
disable_users(list, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 1997
def disable_users(list, options = {})
  list = list.map(&:to_hash)
  url = "#{uri}/users"
  payloads = list.map do |u|
    uri, = resolve_roles(u, [], options)
    generate_user_payload(uri, 'DISABLED')
  end

  payloads.each_slice(100).mapcat do |payload|
    result = client.post(url, 'users' => payload)
    result['projectUsersUpdateResult'].mapcat { |k, v| v.map { |x| { type: k.to_sym, user: x } } }
  end
end
download_file(file, where) click to toggle source
# File lib/gooddata/models/project.rb, line 1194
def download_file(file, where)
  GoodData.download_from_project_webdav(file, where, project: self)
end
driver() click to toggle source
# File lib/gooddata/models/project.rb, line 1198
def driver
  content['driver']
end
environment() click to toggle source
# File lib/gooddata/models/project.rb, line 1202
def environment
  content['environment']
end
execute_dml(dml, options = {}) click to toggle source

Executes DML expression. See (developer.gooddata.com/article/deleting-records-from-datasets) for some examples and explanations

@param dml [String] DML expression @return [Hash] Result of executing DML

# File lib/gooddata/models/project.rb, line 1060
def execute_dml(dml, options = {})
  uri = "/gdc/md/#{pid}/dml/manage"
  result = client.post(uri, manage: { maql: dml })
  polling_uri = result['uri']

  client.poll_on_response(polling_uri, options) do |body|
    body && body['taskState'] && body['taskState']['status'] == 'WAIT'
  end
end
execute_maql(maql, options = {}) click to toggle source

Executes MAQL expression and waits for it to be finished.

@param maql [String] MAQL expression @return [Hash] Result of executing MAQL

# File lib/gooddata/models/project.rb, line 1074
def execute_maql(maql, options = {})
  ldm_links = client.get(md[GoodData::Model::LDM_CTG])
  ldm_uri = Links.new(ldm_links)[GoodData::Model::LDM_MANAGE_CTG]
  response = client.post(ldm_uri, manage: { maql: maql })
  polling_uri = response['entries'].first['link']

  result = client.poll_on_response(polling_uri, options) do |body|
    body && body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
  end
  if result['wTaskStatus']['status'] == 'ERROR'
    fail MaqlExecutionError.new("Executionof MAQL '#{maql}' failed in project '#{pid}'", result)
  end
  result
end
export_clone(options = {}) click to toggle source

Export a clone from a project to be later imported. If you do not want to do anything special and you do not need fine grained controle use clone method which does all the heavy lifting for you.

@param options [Hash] Export options @option options [Boolean] :data Clone project with data @option options [Boolean] :users Clone project with users @option options [String] :authorized_users Comma separated logins of authorized users. Users that can use the export @option options [Boolean] :exclude_schedules Specifies whether to include scheduled notifications in the export @option options [Boolean] :cross_data_center_export Specifies whether export can be used in any data center @return [String] token of the export

# File lib/gooddata/models/project.rb, line 919
def export_clone(options = {})
  with_data = options[:data].nil? ? true : options[:data]
  with_users = options[:users].nil? ? false : options[:users]

  export = {
    :exportProject => {
      :exportUsers => with_users ? 1 : 0,
      :exportData => with_data ? 1 : 0
    }
  }
  export[:exportProject][:authorizedUsers] = options[:authorized_users] if options[:authorized_users]
  if options[:exclude_schedules]
    exclude_notifications = options[:exclude_schedules] ? 1 : 0
    export[:exportProject][:excludeSchedules] = exclude_notifications
  end
  if options[:cross_data_center_export]
    cross_data_center = options[:cross_data_center_export] ? 1 : 0
    export[:exportProject][:crossDataCenterExport] = cross_data_center
  end

  result = client.post("/gdc/md/#{obj_id}/maintenance/export", export)
  status_url = result['exportArtifact']['status']['uri']
  polling_result = client.poll_on_response(status_url) do |body|
    body['taskState']['status'] == 'RUNNING'
  end

  ensure_clone_task_ok(polling_result, GoodData::ExportCloneError)
  result['exportArtifact']['token']
end
fact_by_title(title) click to toggle source
# File lib/gooddata/models/project.rb, line 1097
def fact_by_title(title)
  GoodData::Fact.find_first_by_title(title, project: self, client: client)
end
facts(id = :all) click to toggle source

Helper for getting facts of a project

@param [String | Number | Object] Anything that you can pass to GoodData::Fact @return [GoodData::Fact | Array<GoodData::Fact>] fact instance or list

# File lib/gooddata/models/project.rb, line 1093
def facts(id = :all)
  GoodData::Fact[id, project: self, client: client]
end
facts_by_title(title) click to toggle source
# File lib/gooddata/models/project.rb, line 1101
def facts_by_title(title)
  GoodData::Fact.find_by_title(title, project: self, client: client)
end
find_attribute_element_value(uri) click to toggle source
# File lib/gooddata/models/project.rb, line 1105
def find_attribute_element_value(uri)
  GoodData::Attribute.find_element_value(uri, client: client, project: self)
end
find_by_tag(tags) click to toggle source
# File lib/gooddata/models/project.rb, line 1217
def find_by_tag(tags)
  tags = tags.split(',').map(&:strip) unless tags.is_a?(Array)

  objects = tags.map do |tag|
    url = "/gdc/md/#{pid}/tags/#{tag}"
    res = client.get(url)

    ((res || {})['entries'] || []).map do |entry|
      entry['link']
    end
  end

  objects.flatten!

  objects.uniq!

  objects
end
folders(id = :all) click to toggle source
# File lib/gooddata/models/project.rb, line 949
def folders(id = :all)
  GoodData::Folder[id, project: self, client: client]
end
fuzzy_get_user(name, user_list = users) click to toggle source

Gets user by its email, full_name, login or uri.

@param [String] name Name to look for @param [Array<GoodData::User>]user_list Optional cached list of users used for look-ups @return [GoodDta::Membership] User

# File lib/gooddata/models/project.rb, line 1241
def fuzzy_get_user(name, user_list = users)
  return name if name.instance_of?(GoodData::Membership)
  return member(name) if name.instance_of?(GoodData::Profile)
  name = name.is_a?(Hash) ? name[:login] || name[:uri] : name
  return nil unless name
  name.downcase!
  user_list.select do |user|
    user.uri.downcase == name ||
      user.login.downcase == name ||
      user.email.downcase == name
  end
  nil
end
get_role(role_name, role_list = roles) click to toggle source

Gets project role

@param [String] role_title Title of role to look for @return [GoodData::ProjectRole] Project role if found

# File lib/gooddata/models/project.rb, line 1154
def get_role(role_name, role_list = roles)
  return role_name if role_name.is_a? GoodData::ProjectRole

  role_name.downcase!
  role_list.each do |role|
    return role if role.uri == role_name ||
                   role.identifier.downcase == role_name ||
                   role.identifier.downcase.gsub(/role$/, '') == role_name ||
                   role.title.downcase == role_name ||
                   role.summary.downcase == role_name
  end
  nil
end
get_role_by_identifier(role_name, role_list = roles) click to toggle source

Gets project role by its identifier

@param [String] role_name Title of role to look for @return [GoodData::ProjectRole] Project role if found

# File lib/gooddata/models/project.rb, line 1119
def get_role_by_identifier(role_name, role_list = roles)
  role_name = role_name.downcase.gsub(/role$/, '')
  role_list.each do |role|
    tmp_role_name = role.identifier.downcase.gsub(/role$/, '')
    return role if tmp_role_name == role_name
  end
  nil
end
get_role_by_summary(role_summary, role_list = roles) click to toggle source

Gets project role byt its summary

@param [String] role_summary Summary of role to look for @return [GoodData::ProjectRole] Project role if found

# File lib/gooddata/models/project.rb, line 1132
def get_role_by_summary(role_summary, role_list = roles)
  role_list.each do |role|
    return role if role.summary.downcase == role_summary.downcase
  end
  nil
end
get_role_by_title(role_title, role_list = roles) click to toggle source

Gets project role by its name

@param [String] role_title Title of role to look for @return [GoodData::ProjectRole] Project role if found

# File lib/gooddata/models/project.rb, line 1143
def get_role_by_title(role_title, role_list = roles)
  role_list.each do |role|
    return role if role.title.downcase == role_title.downcase
  end
  nil
end
get_user(slug, user_list = users) click to toggle source

Gets user by its login or uri in various shapes It does not find by other information because that is not unique. If you want to search by name or email please use fuzzy_get_user.

@param [String] name Name to look for @param [Array<GoodData::User>]user_list Optional cached list of users used for look-ups @return [GoodDta::Membership] User

# File lib/gooddata/models/project.rb, line 1175
def get_user(slug, user_list = users)
  search_crit = if slug.respond_to?(:login)
                  slug.login || slug.uri
                elsif slug.is_a?(Hash)
                  slug[:login] || slug[:uri]
                else
                  slug
                end
  return nil unless search_crit
  user_list.find do |user|
    user.uri == search_crit.downcase ||
      user.login.downcase == search_crit.downcase
  end
end
Also aliased as: member
import_clone(export_token, options = {}) click to toggle source

Imports a clone into current project. The project has to be freshly created.

@param export_token [String] Export token of the package to be imported @return [Project] current project

# File lib/gooddata/models/project.rb, line 962
def import_clone(export_token, options = {})
  import = {
    :importProject => {
      :token => export_token
    }
  }

  result = client.post("/gdc/md/#{obj_id}/maintenance/import", import)
  status_url = result['uri']
  polling_result = client.poll_on_response(status_url, options) do |body|
    body['taskState']['status'] == 'RUNNING'
  end
  ensure_clone_task_ok(polling_result, GoodData::ImportCloneError)
  self
end
import_users(new_users, options = {}) click to toggle source

Imports users

# File lib/gooddata/models/project.rb, line 1839
def import_users(new_users, options = {})
  role_list = roles
  users_list = users

  GoodData.logger.warn("Importing users to project (#{pid})")
  new_users = new_users.map { |x| ((x.is_a?(Hash) && x[:user] && x[:user].to_hash.merge(role: x[:role])) || x.to_hash).tap { |u| u[:login].downcase! } }
  # First check that if groups are provided we have them set up
  user_groups_cache, change_groups = check_groups(new_users.map(&:to_hash).flat_map { |u| u[:user_group] || [] }.uniq, options[:user_groups_cache], options)

  unless change_groups.empty?
    new_users.each do |user|
      user[:user_group].map! { |e| change_groups[e].nil? ? e : change_groups[e] }
    end
  end

  whitelisted_new_users, whitelisted_users = whitelist_users(new_users.map(&:to_hash), users_list, options[:whitelists])

  # conform the role on list of new users so we can diff them with the users coming from the project
  diffable_new_with_default_role = whitelisted_new_users.map do |u|
    u[:role] = Array(u[:role] || u[:roles] || 'readOnlyUser')
    u
  end

  intermediate_new = diffable_new_with_default_role.map do |u|
    u[:role] = u[:role].map do |r|
      role = get_role(r, role_list)
      role ? role.uri : r
    end

    u[:role_title] = u[:role].map do |r|
      role = get_role(r, role_list)
      role ? role.title : r
    end

    if u[:role].all?(&:nil?)
      u[:type] = :error
      u[:reason] = 'Invalid role(s) specified'
    else
      u[:type] = :ok
    end

    u[:status] = 'ENABLED'
    u
  end

  intermediate_new_by_type = intermediate_new.group_by { |i| i[:type] }
  diffable_new = intermediate_new_by_type[:ok] || []

  # Diff users. Only login and role is important for the diff
  diff = GoodData::Helpers.diff(whitelisted_users, diffable_new, key: :login, fields: [:login, :role, :status])
  diff_results = diff.flat_map do |operation, users|
    if operation == :changed
      users.map { |u| u[:new_obj].merge(operation: operation) }
    else
      users.map { |u| u.merge(operation: operation) }
    end
  end
  diff_results = diff_results.map do |u|
    u[:login_uri] = USER_ACCOUNT_PATH + u[:login]
    u
  end
  return diff_results if options[:dry_run]

  # Create new users
  results = []
  GoodData.logger.warn("Creating #{diff[:added].count} users in project (#{pid})")
  to_create = diff[:added].map { |x| { user: x, role: x[:role] } }
  created_users_result = create_users(to_create, roles: role_list, project_users: whitelisted_users)
  @log_formatter.log_created_users(created_users_result, diff[:added])
  results.concat(created_users_result)
  send_mail_to_new_users(diff[:added], options[:email_options]) if options[:email_options] && !options[:email_options].empty? && !diff[:added].empty?

  # # Update existing users
  GoodData.logger.warn("Updating #{diff[:changed].count} users in project (#{pid})")
  to_update = diff[:changed].map { |x| { user: x[:new_obj], role: x[:new_obj][:role] || x[:new_obj][:roles] } }
  updated_users_result = set_users_roles(to_update, roles: role_list, project_users: whitelisted_users)
  @log_formatter.log_updated_users(updated_users_result, diff[:changed], role_list)
  results.concat(updated_users_result)

  unless options[:do_not_touch_users_that_are_not_mentioned]
    # Remove old users
    to_disable = diff[:removed].reject { |user| user[:status] == 'DISABLED' || user[:status] == :disabled }
    GoodData.logger.warn("Disabling #{to_disable.count} users from project (#{pid})")
    disabled_users_result = disable_users(to_disable, roles: role_list, project_users: whitelisted_users)
    @log_formatter.log_disabled_users(disabled_users_result)
    results.concat(disabled_users_result)

    # Remove old users completely
    if options[:remove_users_from_project]
      to_remove = (to_disable + users(disabled: true).to_a).map(&:to_hash).uniq do |user|
        user[:uri]
      end
      GoodData.logger.warn("Removing #{to_remove.count} users from project (#{pid})")
      removed_users_result = remove_users(to_remove)
      @log_formatter.log_removed_users(removed_users_result)
      results.concat(removed_users_result)
    end
  end

  # reassign to groups
  removal_user_group_members = []
  mappings = new_users.map(&:to_hash).flat_map do |user|
    removal_user_group_members << user[:login] if user[:user_group]&.empty?
    groups = user[:user_group] || []
    groups.map { |g| [user[:login], g] }
  end

  unless mappings.empty?
    users_lookup = login_users
    mappings.group_by { |_, g| g }.each do |g, mapping|
      remote_users = mapping.map { |user, _| user }.map { |login| users_lookup[login] && users_lookup[login].uri }.reject(&:nil?)
      GoodData.logger.info("Assigning users #{remote_users} to group #{g}")
      next if remote_users.empty?
      existing_group = user_groups(g)
      if existing_group.nil?
        GoodData.logger.warn("Group #{g} not found!!!")
      else
        existing_group.set_members(remote_users)
      end
    end
    mentioned_groups = mappings.map(&:last).uniq
    groups_to_cleanup = user_groups_cache.reject { |g| mentioned_groups.include?(g.name) }

    # clean all groups not mentioned with exception of whitelisted users
    groups_to_cleanup.each do |g|
      g.set_members(whitelist_users(g.members.map(&:to_hash), [], options[:whitelists], :include).first.map { |x| x[:uri] })
    end
  end

  remove_member_from_group(users_lookup, removal_user_group_members, user_groups_cache)
  GoodData::Helpers.join(results, diff_results, [:user], [:login_uri])
end
info() click to toggle source
# File lib/gooddata/models/project.rb, line 1539
def info
  results = blueprint.datasets.pmap do |ds|
    [ds, ds.count(self)]
  end
  GoodData.logger.info(title)
  GoodData.logger.info(GoodData::Helpers.underline(title))
  GoodData.logger.info("\nDatasets - #{results.count}\n")
  results.each do |x|
    dataset, count = x
    dataset.title.tap do |t|
      GoodData.logger.info(t)
      GoodData.logger.info(GoodData::Helpers.underline(t))
      GoodData.logger.info("Size - #{count} rows")
      GoodData.logger.info("#{dataset.attributes_and_anchors.count} attributes, #{dataset.facts.count} facts, #{dataset.references.count} references\n")
    end
  end
  nil
end
invitations() click to toggle source

Returns invitations to project

@return [Array<GoodData::Invitation>] List of invitations

# File lib/gooddata/models/project.rb, line 1318
def invitations
  invitations = client.get @json['project']['links']['invitations']
  invitations['invitations'].pmap do |invitation|
    client.create GoodData::Invitation, invitation
  end
end
invite(email, role, msg = DEFAULT_INVITE_MESSAGE) click to toggle source

Invites new user to project

@param email [String] User to be invited @param role [String] Role URL or Role ID to be used @param msg [String] Optional invite message

TODO: Return invite object

# File lib/gooddata/models/project.rb, line 1284
def invite(email, role, msg = DEFAULT_INVITE_MESSAGE)
  GoodData.logger.info("Inviting #{email}, role: #{role}")

  role_url = nil
  if role.index('/gdc/').nil?
    tmp = get_role(role)
    role_url = tmp.uri if tmp
  else
    role_url = role if role_url.nil?
  end

  data = {
    :invitations => [
      {
        :invitation => {
          :content => {
            :email => email,
            :role => role_url,
            :action => {
              :setMessage => msg
            }
          }
        }
      }
    ]
  }

  url = "/gdc/projects/#{pid}/invitations"
  client.post(url, data)
end
labels(id = :all, opts = {}) click to toggle source

Helper for getting labels of a project

@param [String | Number | Object] Anything that you can pass to GoodData::Label + it supports :all as welll @return [GoodData::Fact | Array<GoodData::Fact>] fact instance or list

# File lib/gooddata/models/project.rb, line 1337
def labels(id = :all, opts = {})
  if id == :all
    attributes.pmapcat(&:labels).uniq
  else
    GoodData::Label[id, opts.merge(project: self, client: client)]
  end
end
ldm_layout() click to toggle source
# File lib/gooddata/models/project.rb, line 2217
def ldm_layout
  GoodData::Project.get_ldm_layout(self)
end
login_users() click to toggle source
# File lib/gooddata/models/project.rb, line 1990
def login_users
  users.reduce({}) do |a, e|
    a[e.login] = e
    a
  end
end
maql_diff(options = {}) click to toggle source

get maql diff from another project or blueprint to current project

@param options [Hash] options @option options [GoodData::Project] :project source project @option options [GoodData::Model::ProjectBlueprint] :blueprint blueprint of source project @option options [Array] :params additional parameters for diff api @return [Hash] project model diff

# File lib/gooddata/models/project.rb, line 2264
def maql_diff(options = {})
  fail "No :project or :blueprint specified" unless options[:blueprint] || options[:project]
  bp = options[:blueprint] || options[:project].blueprint
  uri = "/gdc/projects/#{pid}/model/diff"
  params = Hash[(options[:params] || []).map { |i| [i, true] }]
  result = client.post(uri, bp.to_wire, params: params)
  client.poll_on_code(result['asyncTask']['link']['poll'])
end
md() click to toggle source
# File lib/gooddata/models/project.rb, line 1345
def md
  @md ||= client.create(Links, client.get(data['links']['metadata']))
end
measure_by_title(title)
Alias for: metric_by_title
measures(id = :all, opts = { :full => true })
Alias for: metrics
measures_by_title(title)
Alias for: metrics_by_title
member(slug, user_list = users)

Gets user by its email, full_name, login or uri

Alias for: get_user
member?(profile, list = members) click to toggle source

Checks if the profile is member of project

@param [GoodData::Profile] profile - Profile to be checked @param [Array<GoodData::Membership>] list Optional list of members to check against @return [Boolean] true if is member else false

# File lib/gooddata/models/project.rb, line 1391
def member?(profile, list = members)
  !member(profile, list).nil?
end
members(opts = {})
Alias for: users
members?(profiles, list = members) click to toggle source
# File lib/gooddata/models/project.rb, line 1395
def members?(profiles, list = members)
  profiles.map { |p| member?(p, list) }
end
metadata(key = :all) click to toggle source

Get data from project specific metadata storage

@param [Symbol | String] :all or nothing for all keys or a string for value of specific key @return [Hash] key Hash of stored data

# File lib/gooddata/models/project.rb, line 1353
def metadata(key = :all)
  GoodData::ProjectMetadata[key, client: client, project: self]
end
metric_by_title(title) click to toggle source
# File lib/gooddata/models/project.rb, line 1374
def metric_by_title(title)
  GoodData::Metric.find_first_by_title(title, project: self, client: client)
end
Also aliased as: measure_by_title
metrics(id = :all, opts = { :full => true }) click to toggle source

Helper for getting metrics of a project

@return [Array<GoodData::Metric>] matric instance or list

# File lib/gooddata/models/project.rb, line 1368
def metrics(id = :all, opts = { :full => true })
  GoodData::Metric[id, opts.merge(project: self, client: client)]
end
Also aliased as: measures
metrics_by_title(title) click to toggle source
# File lib/gooddata/models/project.rb, line 1380
def metrics_by_title(title)
  GoodData::Metric.find_by_title(title, project: self, client: client)
end
Also aliased as: measures_by_title
obj_id() click to toggle source

Gets raw resource ID

@return [String] Raw resource ID

# File lib/gooddata/models/project.rb, line 1402
def obj_id
  uri.split('/').last
end
Also aliased as: pid
objects(id, opts = {}) click to toggle source

Helper for getting objects of a project

@return [Array<GoodData::MdObject>] object instance or list

# File lib/gooddata/models/project.rb, line 1411
def objects(id, opts = {})
  GoodData::MdObject[id, opts.merge(project: self, client: client)]
end
objects_export(objs, options = {}) click to toggle source

Transfer objects from one project to another

@param [Array<GoodData::MdObject | String>, String, GoodData::MdObject] objs Any representation of the object or a list of those @param [Hash] options The options to migration. @option options [Number] :time_limit Time in seconds before the blocking call will fail. See GoodData::Rest::Client.poll_on_response for additional details @option options [Number] :sleep_interval Interval between polls on the status of the migration. @return [String] Returns token that you can use as input for object_import

# File lib/gooddata/models/project.rb, line 1422
def objects_export(objs, options = {})
  fail 'Nothing to migrate. You have to pass list of objects, ids or uris that you would like to migrate' if objs.nil?
  objs = Array(objs).map { |o| o.respond_to?(:uri) ? o.uri : o }
  if objs.empty?
    GoodData.logger.warn 'Nothing to migrate.'
    return
  end

  export_payload = {
    :partialMDExport => {
      :uris => objs,
      :exportAttributeProperties => '1',
      :crossDataCenterExport => '1'
    }
  }
  result = client.post("#{md['maintenance']}/partialmdexport", export_payload)
  polling_url = result['partialMDArtifact']['status']['uri']
  token = result['partialMDArtifact']['token']

  polling_result = client.poll_on_response(polling_url, options) do |body|
    body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
  end
  if polling_result['wTaskStatus'] && polling_result['wTaskStatus']['status'] == 'ERROR'
    messages = GoodData::Helpers.interpolate_error_messages(polling_result['wTaskStatus']['messages']).join(' ')
    fail ObjectsExportError, "Exporting objects failed with messages. #{messages}"
  end
  token
end
objects_import(token, options = {}) click to toggle source

Import objects from import token. If you do not need specifically this method what you are probably looking for is transfer_objects. This is a lower level method.

@param [String] token Migration token ID @param [Hash] options The options to migration. @option options [Number] :time_limit Time in seconds before the blocking call will fail. See GoodData::Rest::Client.poll_on_response for additional details @option options [Number] :sleep_interval Interval between polls on the status of the migration. @return [Boolean] Returns true if it succeeds or throws exceoption

# File lib/gooddata/models/project.rb, line 1458
def objects_import(token, options = {})
  fail 'You need to provide a token for object import' if token.blank?

  import_payload = {
    :partialMDImport => {
      :token => token,
      :overwriteNewer => '1',
      :updateLDMObjects => '1',
      :importAttributeProperties => '1'
    }
  }

  result = client.post("#{md['maintenance']}/partialmdimport", import_payload)
  polling_url = result['uri']

  polling_result = client.poll_on_response(polling_url, options) do |body|
    body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
  end

  if polling_result['wTaskStatus']['status'] == 'ERROR'
    messages = GoodData::Helpers.interpolate_error_messages(polling_result['wTaskStatus']['messages']).join(' ')
    fail ObjectsImportError, "Importing objects failed with messages. #{messages}"
  end
  true
end
partial_md_export(objects, options = {}) click to toggle source

Transfer objects from one project to another

@param [Array<GoodData::MdObject | String>, String, GoodData::MdObject] objects Any representation of the object or a list of those @param [Hash] options The options to migration. @option options [GoodData::Project | String | Array<String> | Array<GoodData::Project>] :project Project(s) to migrate to @option options [Number] :batch_size Number of projects that are migrated at the same time. Default is 10

@return [Boolean | Array<Hash>] Return either true or throws exception if you passed only one project. If you provided an array returns list of hashes signifying sucees or failure. Take note that in case of list of projects it does not throw exception.

# File lib/gooddata/models/project.rb, line 1495
def partial_md_export(objects, options = {})
  projects = options[:project]
  batch_size = options[:batch_size] || 10
  token = objects_export(objects)
  return if token.nil?

  if projects.is_a?(Array)
    projects.each_slice(batch_size).flat_map do |batch|
      batch.pmap do |proj|
        target_project = client.projects(proj)
        target_project.objects_import(token, options)
        {
          project: target_project,
          result: true
        }
      end
    end
  else
    target_project = client.projects(projects)
    target_project.objects_import(token, options)
    [{
      project: target_project,
      result: true
    }]
  end
end
Also aliased as: transfer_objects
pid()
Alias for: obj_id
processes(id = :all) click to toggle source

Helper for getting processes of a project

@param [String | Number | Object] Anything that you can pass to GoodData::Report @return [GoodData::Report | Array<GoodData::Report>] report instance or list

# File lib/gooddata/models/project.rb, line 1528
def processes(id = :all)
  GoodData::Process[id, project: self, client: client]
end
project?() click to toggle source

Checks if this object instance is project

@return [Boolean] Return true for all instances

# File lib/gooddata/models/project.rb, line 1535
def project?
  true
end
project_webdav_path() click to toggle source

Get WebDav directory for project data @return [String]

# File lib/gooddata/models/project.rb, line 1111
def project_webdav_path
  client.project_webdav_path(:project => self)
end
public?() click to toggle source
# File lib/gooddata/models/project.rb, line 1206
def public?
  content['isPublic']
end
reload!() click to toggle source

Forces project to reload

# File lib/gooddata/models/project.rb, line 1559
def reload!
  if saved?
    response = client.get(uri)
    @json = response
  end
  self
end
remove_member_from_group(users_lookup, removal_user_group_members, user_groups_cache) click to toggle source
# File lib/gooddata/models/project.rb, line 1972
def remove_member_from_group(users_lookup, removal_user_group_members, user_groups_cache)
  unless removal_user_group_members.empty?
    users_lookup ||= login_users
    current_user_groups = user_groups_cache || user_groups
    removal_user_group_members.uniq.each do |login|
      user_uri = users_lookup[login]&.uri

      # remove user from group if exists as group member
      current_user_groups.each do |user_group|
        if user_group.member?(user_uri)
          GoodData.logger.info("Removing #{user_uri} user from group #{user_group.name}")
          user_group.remove_members(user_uri)
        end
      end
    end
  end
end
remove_users(list) click to toggle source
# File lib/gooddata/models/project.rb, line 2011
def remove_users(list)
  list = list.map(&:to_hash)

  list.pmapcat do |u|
    u_id = GoodData::Helpers.last_uri_part(u[:uri])
    url = "#{uri}/users/#{u_id}"
    begin
      client.delete(url)
      [{ type: :successful, operation: :user_deleted_from_project, user: u }]
    rescue => e
      [{ type: :failed, message: e.message, user: u }]
    end
  end
end
replace_from_mapping(mapping, opts = {}) click to toggle source

Method used for walking through objects in project and trying to replace all occurences of some object for another object. This is typically used as a means for exchanging Date dimensions.

@param mapping [Array<Array>] Mapping specifying what should be exchanged for what. As mapping should be used output of GoodData::Helpers.prepare_mapping.

# File lib/gooddata/models/project.rb, line 1572
def replace_from_mapping(mapping, opts = {})
  default = {
    :purge => false,
    :dry_run => false
  }
  opts = default.merge(opts)
  dry_run = opts[:dry_run]

  if opts[:purge]
    GoodData.logger.info 'Purging old project definitions'
    reports.peach(&:purge_report_of_unused_definitions!)
  end

  fail ArgumentError, 'No mapping specified' if mapping.blank?
  rds = report_definitions

  {
    # data_permissions: data_permissions,
    variables: variables,
    dashboards: dashboards,
    metrics: metrics,
    report_definitions: rds
  }.each do |key, collection|
    GoodData.logger.info("Replacing #{key}")
    collection.peach do |item|
      new_item = item.replace(mapping)
      if new_item.json != item.json
        if dry_run
          GoodData.logger.info "Would save #{new_item.uri}. Running in dry run mode"
        else
          GoodData.logger.info "Saving #{new_item.uri}"
          new_item.save
        end
      else
        GoodData.logger.info "Ignore #{item.uri}"
      end
    end
  end

  GoodData.logger.info 'Replacing hidden metrics'
  local_metrics = mapping.map { |a, _| a }.pmapcat { |a| a.usedby('metric') }.select { |m| m['deprecated'] == '1' }.map { |m| m['link'] }.uniq
  GoodData.logger.info("Found #{local_metrics.count} metrics")
  local_metrics.pmap { |m| metrics(m) }.peach do |item|
    new_item = item.replace(mapping)
    if new_item.json != item.json
      if dry_run
        GoodData.logger.info "Would save #{new_item.uri}. Running in dry run mode"
      else
        GoodData.logger.info "Saving #{new_item.uri}"
        new_item.save
      end
    else
      GoodData.logger.info "Ignore #{item.uri}"
    end
  end

  GoodData.logger.info 'Replacing dashboard saved views'
  contexts = mapping.map { |a, _| a }.pmapcat { |a| a.usedby('executionContext') }.map { |a| GoodData::MdObject[a['link'], client: client, project: self] }
  GoodData.logger.info("Found #{contexts.count} dashboard saved views")
  contexts.peach do |item|
    new_item = GoodData::MdObject.replace_quoted(item, mapping)
    if new_item.json != item.json
      if dry_run
        GoodData.logger.info "Would save #{new_item.uri}. Running in dry run mode"
      else
        GoodData.logger.info "Saving #{new_item.uri}"
        new_item.save
      end
    else
      GoodData.logger.info "Ignore #{item.uri}"
    end
  end

  GoodData.logger.info 'Replacing variable values'
  variables.each do |var|
    var.values.peach do |val|
      val.replace(mapping).save unless dry_run
    end
  end

  {
    visualizations: MdObject.query('visualizationObject', MdObject, client: client, project: self),
    visualization_widgets: MdObject.query('visualizationWidget', MdObject, client: client, project: self),
    kpis: MdObject.query('kpi', MdObject, client: client, project: self)
  }.each do |key, collection|
    GoodData.logger.info "Replacing #{key}"
    collection.each do |item|
      new_item = MdObject.replace_quoted(item, mapping)
      if new_item.json != item.json
        if dry_run
          GoodData.logger.info "Would save #{new_item.uri}. Running in dry run mode"
        else
          GoodData.logger.info "Saving #{new_item.uri}"
          new_item.save
        end
      else
        GoodData.logger.info "Ignore #{item.uri}"
      end
    end
  end
  nil
end
report_definitions(id = :all, options = {}) click to toggle source

Helper for getting report definitions of a project

@param [String | Number | Object] Anything that you can pass to GoodData::ReportDefinition @return [GoodData::ReportDefinition | Array<GoodData::ReportDefinition>] report definition instance or list

# File lib/gooddata/models/project.rb, line 1687
def report_definitions(id = :all, options = {})
  GoodData::ReportDefinition[id, options.merge(project: self, client: client)]
end
reports(id = :all) click to toggle source

Helper for getting reports of a project

@param [String | Number | Object] Anything that you can pass to GoodData::Report @return [GoodData::Report | Array<GoodData::Report>] report instance or list

# File lib/gooddata/models/project.rb, line 1679
def reports(id = :all)
  GoodData::Report[id, project: self, client: client]
end
reset_color_palette() click to toggle source
# File lib/gooddata/models/project.rb, line 2253
def reset_color_palette
  GoodData::StyleSetting.reset(client: client, project: self)
end
resolve_roles(login, desired_roles, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 2159
def resolve_roles(login, desired_roles, options = {})
  user = if login.is_a?(String) && login.include?('@')
           USER_ACCOUNT_PATH + login
         elsif login.is_a?(String)
           login
         elsif login.is_a?(Hash) && login[:login]
           USER_ACCOUNT_PATH + login[:login]
         elsif login.is_a?(Hash) && login[:uri]
           login[:uri]
         elsif login.respond_to?(:uri) && login.uri
           login.uri
         elsif login.respond_to?(:login) && login.login
           USER_ACCOUNT_PATH + login.login
         else
           fail "Unsupported user specification #{login}"
         end

  role_list = options[:roles] || roles
  desired_roles = Array(desired_roles)
  roles = desired_roles.map do |role_name|
    role = get_role(role_name, role_list)
    fail ArgumentError, "Invalid role '#{role_name}' specified for user '#{GoodData::Helpers.last_uri_part(user)}'" if role.nil?
    role.uri
  end
  [user, roles]
end
roles() click to toggle source

Gets the list or project roles

@return [Array<GoodData::ProjectRole>] List of roles

# File lib/gooddata/models/project.rb, line 1694
def roles
  url = "/gdc/internal/projects/#{pid}/roles"
  res = client.get url
  res['internalProjectRoles']['roles'].map do |r|
    client.create(GoodData::ProjectRole, r, project: self)
  end
end
save() click to toggle source

Saves project

# File lib/gooddata/models/project.rb, line 1703
def save
  data_to_send = GoodData::Helpers.deep_dup(raw_data)
  data_to_send['project']['content'].delete('cluster')
  data_to_send['project']['content'].delete('isPublic')
  data_to_send['project']['content'].delete('state')
  response = if uri
               client.post(PROJECT_PATH % pid, data_to_send)
               client.get uri
             else
               result = client.post(PROJECTS_PATH, data_to_send)
               client.get result['uri']
             end
  @json = response
  self
end
save_ldm_layout(ldm_layout_json) click to toggle source
# File lib/gooddata/models/project.rb, line 2221
def save_ldm_layout(ldm_layout_json)
  GoodData::Project.save_ldm_layout(ldm_layout_json, self)
end
schedule_mail(options = GoodData::ScheduledMail::DEFAULT_OPTS) click to toggle source

Schedules an email with dashboard or report content

# File lib/gooddata/models/project.rb, line 1720
def schedule_mail(options = GoodData::ScheduledMail::DEFAULT_OPTS)
  GoodData::ScheduledMail.create(options.merge(client: client, project: self))
end
scheduled_mails(options = { :full => false }) click to toggle source
# File lib/gooddata/models/project.rb, line 1724
def scheduled_mails(options = { :full => false })
  GoodData::ScheduledMail[:all, options.merge(project: self, client: client)]
end
schedules(id = :all) click to toggle source

@param [String | Number | Object] Anything that you can pass to GoodData::Schedule @return [GoodData::Schedule | Array<GoodData::Schedule>] schedule instance or list

# File lib/gooddata/models/project.rb, line 1730
def schedules(id = :all)
  GoodData::Schedule[id, project: self, client: client]
end
set_metadata(key, val) click to toggle source

Set data for specific key in project specific metadata storage

@param [String] key key of the value to be stored @return [String] val value to be stored

# File lib/gooddata/models/project.rb, line 1361
def set_metadata(key, val)
  GoodData::ProjectMetadata.[]=(key, { client: client, project: self }, val)
end
set_user_roles(login, desired_roles, options = {}) click to toggle source

Update user

@param user User to be updated @param desired_roles Roles to be assigned to user @param role_list Optional cached list of roles used for lookups

# File lib/gooddata/models/project.rb, line 2063
def set_user_roles(login, desired_roles, options = {})
  user_uri, roles = resolve_roles(login, desired_roles, options)
  url = "#{uri}/users"
  payload = generate_user_payload(user_uri, 'ENABLED', roles)
  res = client.post(url, payload)
  failure = GoodData::Helpers.get_path(res, %w(projectUsersUpdateResult failed))
  fail ArgumentError, "User #{user_uri} could not be added. #{failure.first['message']}" unless failure.blank?

  res
end
Also aliased as: add_user
set_users_roles(list, options = {}) click to toggle source

Update list of users

@param list List of users to be updated @param role_list Optional list of cached roles to prevent unnecessary server round-trips

# File lib/gooddata/models/project.rb, line 2079
def set_users_roles(list, options = {})
  return [] if list.empty?
  role_list = options[:roles] || roles
  project_users = options[:project_users] || users

  intermediate_users = list.flat_map do |user_hash|
    user = user_hash[:user] || user_hash[:login]
    desired_roles = user_hash[:role] || user_hash[:roles] || 'readOnlyUser'
    begin
      login, roles = resolve_roles(user, desired_roles, options.merge(project_users: project_users, roles: role_list))
      [{ :type => :successful, user: login, roles: roles }]
    rescue => e
      [{ :type => :failed, :reason => e.message, user: user, roles: desired_roles }]
    end
  end

  # User can fail pre sending to API during resolving roles. We add only users that passed that step.
  users_by_type = intermediate_users.group_by { |u| u[:type] }
  users_to_add = users_by_type[:successful] || []

  payloads = users_to_add.map { |u| generate_user_payload(u[:user], 'ENABLED', u[:roles]) }
  results = payloads.each_slice(100).map do |payload|
    client.post("#{uri}/users", 'users' => payload)
  end
  # this ugly line turns the hash of errors into list of errors with types so we can process them easily
  typed_results = results.flat_map do |x|
    x['projectUsersUpdateResult'].flat_map do |k, v|
      v.map { |v2| v2.is_a?(String) ? { type: k.to_sym, user: v2 } : GoodData::Helpers.symbolize_keys(v2).merge(type: k.to_sym) }
    end
  end
  # we have to concat errors from role resolution and API result
  typed_results + (users_by_type[:failed] || [])
end
Also aliased as: add_users, create_users
slis() click to toggle source

Gets SLIs data

@return [GoodData::Metadata] SLI Metadata

# File lib/gooddata/models/project.rb, line 1737
def slis
  link = "#{data['links']['metadata']}#{SLIS_PATH}"

  # FIXME: Review what to do with passed extra argument
  Metadata.new client.get(link)
end
state() click to toggle source

Gets project state

@return [String] Project state

# File lib/gooddata/models/project.rb, line 1747
def state
  data['content']['state'].downcase.to_sym if data['content'] && data['content']['state']
end
title=(a_title) click to toggle source

Gets project title

@return [String] Project title

# File lib/gooddata/models/project.rb, line 1756
def title=(a_title)
  data['meta']['title'] = a_title if data['meta']
end
token() click to toggle source
# File lib/gooddata/models/project.rb, line 1210
def token
  content['authorizationToken']
end
transfer_color_palette(target) click to toggle source
# File lib/gooddata/models/project.rb, line 2241
def transfer_color_palette(target)
  GoodData::Project.transfer_color_palette(self, target)
end
transfer_etl(target) click to toggle source
# File lib/gooddata/models/project.rb, line 2205
def transfer_etl(target)
  GoodData::Project.transfer_etl(client, self, target)
end
transfer_objects(objects, options = {})
Alias for: partial_md_export
transfer_processes(target) click to toggle source
# File lib/gooddata/models/project.rb, line 2225
def transfer_processes(target)
  GoodData::Project.transfer_processes(self, target)
end
transfer_schedules(target) click to toggle source
# File lib/gooddata/models/project.rb, line 2229
def transfer_schedules(target)
  GoodData::Project.transfer_schedules(self, target)
end
transfer_tagged_stuff(target, tag) click to toggle source
# File lib/gooddata/models/project.rb, line 2233
def transfer_tagged_stuff(target, tag)
  GoodData::Project.transfer_tagged_stuff(self, target, tag)
end
update_dataset_mapping(model_mapping_json) click to toggle source
# File lib/gooddata/models/project.rb, line 2213
def update_dataset_mapping(model_mapping_json)
  GoodData::Project.update_dataset_mapping(model_mapping_json, self)
end
update_from_blueprint(blueprint, options = {}) click to toggle source

Applies blueprint to the project.

@param [Hash] blueprint Blueprint to apply to the project. @option options [Hash] :update_preference (cascade_drops: false, preserve_data: true) Specifies how dropping LDM and data should be treated. @example Update with custom update preference.

GoodData.project.update_from_blueprint(
  blueprint,
  update_preference: {
    cascade_drops: false, preserve_data: false
  }
)
# File lib/gooddata/models/project.rb, line 2155
def update_from_blueprint(blueprint, options = {})
  GoodData::Model::ProjectCreator.migrate(options.merge(spec: blueprint, token: options[:auth_token], client: client, project: self))
end
upgrade_custom_v2(message, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 2186
def upgrade_custom_v2(message, options = {})
  uri = "/gdc/md/#{pid}/datedimension/upgrade"
  poll_result = client&.post(uri, message)

  return poll_result['wTaskStatus']['status'] if poll_result['wTaskStatus'] && poll_result['wTaskStatus']['status']

  polling_uri = poll_result['asyncTask']['link']['poll']
  result = client&.poll_on_response(polling_uri, options) do |body|
    body && body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
  end

  result['wTaskStatus']['status'] == 'OK' ? 'OK' : 'FAIL'
end
upload(data, blueprint, dataset_name, options = {}) click to toggle source

Uploads file to project

@param file File to be uploaded @param schema Schema to be used

# File lib/gooddata/models/project.rb, line 1764
def upload(data, blueprint, dataset_name, options = {})
  GoodData::Model.upload_data(data, blueprint, dataset_name, options.merge(client: client, project: self))
end
upload_file(file, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 1190
def upload_file(file, options = {})
  GoodData.upload_to_project_webdav(file, options.merge(project: self))
end
upload_multiple(data, blueprint, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 1768
def upload_multiple(data, blueprint, options = {})
  GoodData::Model.upload_multiple_data(data, blueprint, options.merge(client: client, project: self))
end
uri() click to toggle source
# File lib/gooddata/models/project.rb, line 1772
def uri
  data['links']['self'] if data && data['links'] && data['links']['self']
end
user_filters(id = :all)
Alias for: data_permissions
user_groups(id = :all, options = {}) click to toggle source
# File lib/gooddata/models/project.rb, line 953
def user_groups(id = :all, options = {})
  GoodData::UserGroup[id, options.merge(project: self)]
end
user_has_role?(user, role_name) click to toggle source

Checks whether user has particular role in given proejct

@param user [GoodData::Profile | GoodData::Membership | String] User in question. Can be passed by login (String), profile or membershi objects @param role_name [String || GoodData::ProjectRole] Project role cna be given by either string or GoodData::ProjectRole object @return [Boolean] Tru if user has role_name

# File lib/gooddata/models/project.rb, line 1260
def user_has_role?(user, role_name)
  member = get_user(user)
  role = get_role(role_name)
  member.roles.include?(role)
rescue
  false
end
users(opts = {}) click to toggle source

List of users in project

@return [Array<GoodData::User>] List of users

# File lib/gooddata/models/project.rb, line 1780
def users(opts = {})
  client = client(opts)
  all_users = []
  offset = opts[:offset] || 0
  limit = opts[:limit] || 1_000
  loop do
    tmp = client.get("/gdc/projects/#{pid}/users", params: { offset: offset, limit: limit })
    tmp['users'].each do |user_data|
      user = client.create(GoodData::Membership, user_data, project: self)

      if opts[:all]
        all_users << user
      elsif opts[:disabled]
        all_users << user if user&.disabled?
      else
        all_users << user if user&.enabled?
      end
    end
    break if tmp['users'].count < limit

    offset += limit
  end

  all_users
end
Also aliased as: members
validate(filters = %w(ldm pdm metric_filter invalid_objects), options = {}) click to toggle source

Run validation on project Valid settins for validation are (default all): ldm - Checks the consistency of LDM objects. pdm Checks LDM to PDM mapping consistency, also checks PDM reference integrity. metric_filter - Checks metadata for inconsistent metric filters. invalid_objects - Checks metadata for invalid/corrupted objects. asyncTask response

# File lib/gooddata/models/project.rb, line 2132
def validate(filters = %w(ldm pdm metric_filter invalid_objects), options = {})
  response = client.post "#{md['validate-project']}", 'validateProject' => filters
  polling_link = response['asyncTask']['link']['poll']
  client.poll_on_response(polling_link, options) do |body|
    body['wTaskStatus'] && body['wTaskStatus']['status'] == 'RUNNING'
  end
end
variables(id = :all, options = { client: client, project: self }) click to toggle source
# File lib/gooddata/models/project.rb, line 2140
def variables(id = :all, options = { client: client, project: self })
  GoodData::Variable[id, options]
end
whitelist_users(new_users, users_list, whitelist, mode = :exclude) click to toggle source
# File lib/gooddata/models/project.rb, line 1808
def whitelist_users(new_users, users_list, whitelist, mode = :exclude)
  return [new_users, users_list] unless whitelist

  new_whitelist_proc = proc do |user|
    whitelist.any? do |wl|
      if wl.is_a?(Regexp)
        user[:login] =~ wl
      else
        user[:login] && user[:login] == wl
      end
    end
  end

  whitelist_proc = proc do |user|
    whitelist.any? do |wl|
      if wl.is_a?(Regexp)
        user.login =~ wl
      else
        user.login && user.login == wl
      end
    end
  end

  if mode == :include
    [new_users.select(&new_whitelist_proc), users_list.select(&whitelist_proc)]
  elsif mode == :exclude
    [new_users.reject(&new_whitelist_proc), users_list.reject(&whitelist_proc)]
  end
end

Private Instance Methods

ensure_clone_task_ok(response, clone_task_error) click to toggle source

Checks state of an export/import task. @param response [Hash] Response from API @param clone_task_error [Error] Error to raise when state is not OK

# File lib/gooddata/models/project.rb, line 2346
def ensure_clone_task_ok(response, clone_task_error)
  if response['taskState'].nil?
    fail clone_task_error, "Clone task failed with unknown response: #{response}"
  elsif response['taskState']['status'] != 'OK'
    messages = response['taskState']['messages'] || []
    interpolated_messages = GoodData::Helpers.interpolate_error_messages(messages).join(' ')
    fail clone_task_error, "Clone task failed. #{interpolated_messages}"
  end
end
generate_user_payload(user_uri, status = 'ENABLED', roles_uri = nil) click to toggle source
# File lib/gooddata/models/project.rb, line 2328
def generate_user_payload(user_uri, status = 'ENABLED', roles_uri = nil)
  payload = {
    'user' => {
      'content' => {
        'status' => status
      },
      'links' => {
        'self' => user_uri
      }
    }
  }
  payload['user']['content']['userRoles'] = roles_uri if roles_uri
  payload
end
get_email_body(template, user) click to toggle source
# File lib/gooddata/models/project.rb, line 2321
def get_email_body(template, user)
  template.gsub('${name}', "#{user[:first_name]} #{user[:last_name]}")
          .gsub('${role}', user[:role_title].count == 1 ? user[:role_title].first : user[:role_title].to_s)
          .gsub('${user_group}', user[:user_group].count == 1 ? user[:user_group].first : user[:user_group].to_s)
          .gsub('${project}', Project[user[:pid]].title)
end
get_email_template(options) click to toggle source
# File lib/gooddata/models/project.rb, line 2289
def get_email_template(options)
  bucket = options[:email_template_bucket]
  path = options[:email_template_path]
  access_key = options[:email_template_access_key]
  secret_key = options[:email_template_secret_key]
  raise "Unable to connect to AWS. Parameter \"email_template_bucket\" seems to be empty" unless bucket
  raise "Unable to connect to AWS. Parameter \"email_template_path\" is missing" unless path
  raise "Unable to connect to AWS. Parameter \"email_template_access_key\" is missing" unless access_key
  raise "Unable to connect to AWS. Parameter \"email_template_secret_key\" is missing" unless secret_key
  args = {
    access_key_id: access_key,
    secret_access_key: secret_key,
    max_retries: 15,
    http_read_timeout: 120,
    http_open_timeout: 120
  }

  server_side_encryption = options['email_server_side_encryption'] || false
  args['s3_server_side_encryption'] = :aes256 if server_side_encryption

  s3 = Aws::S3::Resource.new(args)
  bucket = s3.bucket(bucket)
  process_email_template(bucket, path)
end
process_email_template(bucket, path) click to toggle source
# File lib/gooddata/models/project.rb, line 2314
def process_email_template(bucket, path)
  type = path.split('/').last.include?('.html') ? 'html' : 'txt'
  body = bucket.object(path).read
  body.prepend("MIME-Version: 1.0\nContent-type: text/html\n") if type == 'html'
  body
end
send_mail_to_new_users(users, email_options) click to toggle source
# File lib/gooddata/models/project.rb, line 2275
def send_mail_to_new_users(users, email_options)
  password = email_options[:email_password]
  from = email_options[:email_from]
  raise 'Missing sender email, please specify parameter "email_from"' unless from
  raise 'Missing authentication password, please specify parameter "email_password"' unless password
  template = get_email_template(email_options)
  smtp = Net::SMTP.new('relay1.na.intgdc.com', 25)
  smtp.enable_starttls OpenSSL::SSL::SSLContext.new("TLSv1_2_client")
  smtp.start('notifications.gooddata.com', 'gdc', password, :plain)
  users.each do |user|
    smtp.send_mail(get_email_body(template, user), from, user[:login])
  end
end