Skip to content

Commit

Permalink
Add config_topic and spec
Browse files Browse the repository at this point in the history
  • Loading branch information
jmthomas committed Jun 10, 2022
1 parent 119e2d1 commit e0fdde6
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
68 changes: 68 additions & 0 deletions cosmos/lib/cosmos/topics/config_topic.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# encoding: ascii-8bit

# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# This program may also be used under the terms of a commercial or
# enterprise edition license of COSMOS if purchased from the
# copyright holder

require 'cosmos/topics/topic'

module Cosmos
class ConfigTopic < Topic
PRIMARY_KEY = "__CONFIG"

# Helper method to initialize the stream and ensure a consistent key
def self.initialize_stream(scope)
self.initialize_streams(["#{scope}#{PRIMARY_KEY}"])
end

# Write a configuration change to the topic
# @param config [Hash] Hash with required keys 'kind', 'name', 'type'
def self.write(config, scope:)
unless config.keys.include?(:kind)
raise "ConfigTopic error, required key kind: not given"
end
unless ['created', 'deleted'].include?(config[:kind])
raise "ConfigTopic error unknown kind: #{config[:kind]}"
end
unless config.keys.include?(:name)
raise "ConfigTopic error, required key name: not given"
end
unless config.keys.include?(:type)
raise "ConfigTopic error, required key type: not given"
end
# Limit the configuration topics to 1000 entries
Topic.write_topic("#{scope}#{PRIMARY_KEY}", config, '*', 1000)
end

def self.read(offset = nil, count: 100, scope:)
topic = "#{scope}#{PRIMARY_KEY}"
if offset
result = Topic.read_topics([topic], [offset], nil, count)
if result.empty?
[] # We want to return an empty array rather than an empty hash
else
# result is a hash with the topic key followed by an array of results
# This returns just the array of arrays [[offset, hash], [offset, hash], ...]
result[topic]
end
else
result = Topic.get_newest_message(topic)
return [result] if result
return []
end
end
end
end
76 changes: 76 additions & 0 deletions cosmos/spec/topics/config_topic_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# encoding: ascii-8bit

# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# This program may also be used under the terms of a commercial or
# enterprise edition license of COSMOS if purchased from the
# copyright holder

require 'spec_helper'
require 'cosmos/topics/config_topic'

module Cosmos
describe ConfigTopic do
before(:each) do
mock_redis()
end

describe "self.initialize_stream" do
it "initializes the stream with the scope" do
ConfigTopic.initialize_stream('DEFAULT')
expect(EphemeralStore.scan_each(type: 'stream', count: 100).to_a).to include("DEFAULT__CONFIG")
end
end

describe "self.write" do
it "requires kind, type, name keys" do
expect { ConfigTopic.write({ type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/)
expect { ConfigTopic.write({ kind: 'created', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/)
expect { ConfigTopic.write({ kind: 'created', type: 'target', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/)
end

it "requires kind to be 'created' or 'deleted'" do
expect { ConfigTopic.write({ kind: 'unknown', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT') }.to raise_error(/ConfigTopic error/)
end
end

describe "self.read" do
it "reads from an offset" do
ConfigTopic.initialize_stream('DEFAULT')
ConfigTopic.write({ kind: 'created', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT')
ConfigTopic.write({ kind: 'deleted', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT')
config = ConfigTopic.read(0, scope: 'DEFAULT') # read all
expect(config[0][1]['kind']).to eql 'created'
expect(config[0][1]['type']).to eql 'target'
expect(config[0][1]['name']).to eql 'INST'
expect(config[0][1]['plugin']).to eql 'PLUGIN'
expect(config[1][1]['kind']).to eql 'deleted'
expect(config[1][1]['type']).to eql 'target'
expect(config[1][1]['name']).to eql 'INST'
expect(config[1][1]['plugin']).to eql 'PLUGIN'
end

it "reads the latest" do
ConfigTopic.initialize_stream('DEFAULT')
ConfigTopic.write({ kind: 'created', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT')
ConfigTopic.write({ kind: 'deleted', type: 'target', name: 'INST', plugin: 'PLUGIN'}, scope: 'DEFAULT')
config = ConfigTopic.read(scope: 'DEFAULT') # read latest
expect(config[0][1]['kind']).to eql 'deleted'
expect(config[0][1]['type']).to eql 'target'
expect(config[0][1]['name']).to eql 'INST'
expect(config[0][1]['plugin']).to eql 'PLUGIN'
end
end
end
end

0 comments on commit e0fdde6

Please sign in to comment.