Skip to content

Commit

Permalink
Issue #104: Add category_uid to base of compiled class definition (#105)
Browse files Browse the repository at this point in the history
* Fix edge case with profiles and attribute observable.

When "compiling" profile attributes, attributes with observable by dictionary attribute definitions were not being copied to the profile's attributes. This commit fixes that oversight.

* Issue #104: Add category_uid to base of compiled class definitions

* Fix comment

* Minor dependency updates
  • Loading branch information
rmouritzen-splunk authored Aug 20, 2024
1 parent d4f2b03 commit a7ee6e9
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 69 deletions.
122 changes: 69 additions & 53 deletions lib/schema/cache.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,30 +64,31 @@ defmodule Schema.Cache do
dictionary = Utils.update_dictionary(dictionary, base_event, classes, objects)
observable_type_id_map = observables_from_dictionary(dictionary, observable_type_id_map)

attributes = dictionary[:attributes]
dictionary_attributes = dictionary[:attributes]

profiles = JsonReader.read_profiles() |> update_profiles(attributes)
profiles = JsonReader.read_profiles() |> update_profiles(dictionary_attributes)

# clean up the cached files
JsonReader.cleanup()

# Apply profiles to objects and classes
{objects, profiles} = Profiles.sanity_check(:object, objects, profiles)
# Check profiles used in objects, adding objects to profile's _links
profiles = Profiles.sanity_check(:object, objects, profiles)

objects =
objects
|> Utils.update_objects(attributes)
|> Utils.update_objects(dictionary_attributes)
|> update_observable(observable_type_id_map)
|> update_objects()
|> final_check(attributes)
|> final_check(dictionary_attributes)

{classes, profiles} = Profiles.sanity_check(:class, classes, profiles)
# Check profiles used in classes, adding classes to profile's _links
profiles = Profiles.sanity_check(:class, classes, profiles)

classes =
update_classes(classes, objects)
|> final_check(attributes)
|> final_check(dictionary_attributes)

base_event = final_check(:base_event, base_event, attributes)
base_event = final_check(:base_event, base_event, dictionary_attributes)

no_req_set = MapSet.new()
{profiles, no_req_set} = fix_entities(profiles, no_req_set, "profile")
Expand Down Expand Up @@ -781,8 +782,14 @@ defmodule Schema.Cache do
defp update_class_uid(class, categories) do
{key, category} = Utils.find_entity(categories, class, class[:category])

class = Map.put(class, :category, Atom.to_string(key))
class = Map.put(class, :category_name, category[:caption])
class =
if category != nil do
class
|> Map.put(:category, Atom.to_string(key))
|> Map.put(:category_name, category[:caption])
else
class
end

cat_uid = category[:uid] || 0
class_uid = class[:uid] || 0
Expand Down Expand Up @@ -881,36 +888,43 @@ defmodule Schema.Cache do
end

defp add_category_uid(class, name, categories) do
case class[:category] do
nil ->
Logger.warning("class '#{class[:name]}' has no category")
class
category_name = class[:category]

"other" ->
class
{_key, category} = Utils.find_entity(categories, class, class[:category])

cat_name ->
{_key, category} = Utils.find_entity(categories, class, cat_name)

if category == nil do
Logger.warning("class '#{class[:name]}' has an invalid category: #{cat_name}")
class
else
update_in(
class,
[:attributes, :category_uid, :enum],
fn _enum ->
id = Integer.to_string(category[:uid]) |> String.to_atom()
%{id => category}
end
)
if category == nil do
case category_name do
"other" ->
Logger.info("Class \"#{class[:name]}\" uses special undefined category \"other\"")

nil ->
Logger.warning("Class \"#{class[:name]}\" has no category")

undefined ->
Logger.warning("Class \"#{class[:name]}\" has undefined category: #{undefined}")
end

# Match update_class_uid and use 0 for undefined categories
Map.put(class, :category_uid, 0)
else
category_uid = category[:uid]

class
|> Map.put(:category_uid, category_uid)
|> update_in(
[:attributes, :category_uid, :enum],
fn _enum ->
id = Integer.to_string(category_uid) |> String.to_atom()
%{id => category}
end
|> put_in([:attributes, :category_uid, :_source], name)
|> put_in(
[:attributes, :category_name, :description],
"The event category name, as defined by category_uid value: <code>#{category[:caption]}</code>."
)
)
|> put_in(
[:attributes, :category_name, :description],
"The event category name, as defined by category_uid value:" <>
" <code>#{category[:caption]}</code>."
)
end
|> put_in([:attributes, :category_uid, :_source], name)
end

defp attribute_source({item_key, item}) do
Expand Down Expand Up @@ -1285,6 +1299,7 @@ defmodule Schema.Cache do
end)
end

# Flesh out profile attributes from dictionary attributes
defp update_profiles(profiles, dictionary_attributes) do
Enum.into(profiles, %{}, fn {name, profile} ->
{name,
Expand All @@ -1294,34 +1309,35 @@ defmodule Schema.Cache do
end)
end

defp update_profile(profile, attributes, dictionary_attributes) do
Enum.into(attributes, %{}, fn {name, attribute} ->
defp update_profile(profile, profile_attributes, dictionary_attributes) do
Enum.into(profile_attributes, %{}, fn {name, attribute} ->
{name,
case find_attribute(dictionary_attributes, name, String.to_atom(profile)) do
nil ->
Logger.warning("profile #{profile} uses #{name} that is not defined in the dictionary")
attribute

attr ->
copy(attribute, attr)
dictionary_attribute ->
update_profile_attribute(attribute, dictionary_attribute)
end
|> Map.delete(:profile)}
end)
end

defp copy(to, from) do
defp update_profile_attribute(to, from) do
to
|> copy(from, :caption)
|> copy(from, :description)
|> copy(from, :is_array)
|> copy(from, :enum)
|> copy(from, :type)
|> copy(from, :type_name)
|> copy(from, :object_name)
|> copy(from, :object_type)
end

defp copy(to, from, key) do
|> copy_new(from, :caption)
|> copy_new(from, :description)
|> copy_new(from, :is_array)
|> copy_new(from, :enum)
|> copy_new(from, :type)
|> copy_new(from, :type_name)
|> copy_new(from, :object_name)
|> copy_new(from, :object_type)
|> copy_new(from, :observable)
end

defp copy_new(to, from, key) do
case from[key] do
nil -> to
val -> Map.put_new(to, key, val)
Expand Down
23 changes: 10 additions & 13 deletions lib/schema/profiles.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,35 +46,32 @@ defmodule Schema.Profiles do
end

@doc """
Checks classes or objects if all profile attributes are defined.
Checks items (classes or objects), ensuring that each all profiles defined in each item are
defined in profiles. Adds each properly define profile to profile's _links.
"""
def sanity_check(group, maps, profiles) do
profiles =
Enum.reduce(maps, profiles, fn {name, map}, acc ->
check_profiles(group, name, map, map[:profiles], acc)
end)

{maps, profiles}
def sanity_check(group, items, profiles) do
Enum.reduce(items, profiles, fn {item_name, item}, acc ->
check_profiles(group, item_name, item, item[:profiles], acc)
end)
end

# Checks if all profile attributes are defined in the given attribute set.
defp check_profiles(_group, _name, _map, nil, all_profiles) do
all_profiles
end

defp check_profiles(group, name, map, profiles, all_profiles) do
Enum.reduce(profiles, all_profiles, fn p, acc ->
defp check_profiles(group, item_name, item, item_profiles, all_profiles) do
Enum.reduce(item_profiles, all_profiles, fn p, acc ->
case acc[p] do
nil ->
Logger.warning("#{name} uses undefined profile: #{p}")
Logger.warning("#{item_name} uses undefined profile: #{p}")
acc

profile ->
link = %{group: group, type: Atom.to_string(name), caption: map[:caption]}
link = %{group: group, type: Atom.to_string(item_name), caption: item[:caption]}
profile = Map.update(profile, :_links, [link], fn links -> [link | links] end)
Map.put(acc, p, profile)
end
end)
end

end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
defmodule Schema.MixProject do
use Mix.Project

@version "2.72.0"
@version "2.73.0"

def project do
build = System.get_env("GITHUB_RUN_NUMBER") || "SNAPSHOT"
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
"elixir_uuid": {:hex, :uuid_utils, "1.6.5", "bafd6ffcbec895513a7c10855df3954f29909fb5d05ee52681e30e84297b1a80", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "36aaeee10740eae4d357231f48571a2687cb541730f94f47cbd3f186dc07899c"},
"ex_json_schema": {:hex, :ex_json_schema, "0.7.4", "09eb5b0c8184e5702bc89625a9d0c05c7a0a845d382e9f6f406a0fc1c9a8cc3f", [:mix], [], "hexpm", "45c67fa840f0d719a2b5578126dc29bcdc1f92499c0f61bcb8a3bcb5935f9684"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
Expand All @@ -29,5 +29,5 @@
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.6", "0437fe56e093fd4ac422de33bf8fc89f7bc1416a3f2d732d8b2c8fd54792fe60", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "e04378d26b0af627817ae84c92083b7e97aca3121196679b73c73b99d0d133ea"},
"websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"},
}

0 comments on commit a7ee6e9

Please sign in to comment.