Skip to content

Commit

Permalink
Correct best practices, add test for JSON output using -j/-json flags
Browse files Browse the repository at this point in the history
  • Loading branch information
suprith-hub committed Jul 9, 2024
1 parent d2bc412 commit ae2f7f5
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 93 deletions.
2 changes: 1 addition & 1 deletion docs/frame.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ reference:
jsonschema frame path/to/my/schema.json
```

### Frame a JSON Schema with output in the form of JSON
### Frame a JSON Schema and output result as a JSON document

```sh
jsonschema frame path/to/my/schema.json --json
Expand Down
187 changes: 96 additions & 91 deletions src/command_frame.cc
Original file line number Diff line number Diff line change
@@ -1,112 +1,117 @@
#include <sourcemeta/jsontoolkit/json.h>
#include <sourcemeta/jsontoolkit/jsonschema.h>

#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> // std::cout
#include <sstream> // std::ostringstream

#include "command.h"
#include "utils.h"

const char* enumToString(sourcemeta::jsontoolkit::ReferenceEntryType type) {
switch(type) {
case sourcemeta::jsontoolkit::ReferenceEntryType::Resource: return "Resource";
case sourcemeta::jsontoolkit::ReferenceEntryType::Anchor: return "Anchor";
case sourcemeta::jsontoolkit::ReferenceEntryType::Pointer: return "Pointer";
default: return "Unknown";
}
static auto enum_to_string(const sourcemeta::jsontoolkit::ReferenceEntryType type) -> std::string {
switch (type) {
case sourcemeta::jsontoolkit::ReferenceEntryType::Resource:
return "Resource";
case sourcemeta::jsontoolkit::ReferenceEntryType::Anchor:
return "Anchor";
case sourcemeta::jsontoolkit::ReferenceEntryType::Pointer:
return "Pointer";
default:
return "Unknown";
}
}

auto intelligence::jsonschema::cli::frame(const std::span<const std::string> &arguments) -> int {
const auto options{parse_options(arguments, {"json", "j"})};
CLI_ENSURE(!options.at("").empty(), "You must pass a JSON Schema as input")
const sourcemeta::jsontoolkit::JSON schema{
sourcemeta::jsontoolkit::from_file(options.at("").front())
};
const auto options{parse_options(arguments, {"json", "j"})};
CLI_ENSURE(!options.at("").empty(), "You must pass a JSON Schema as input")
const sourcemeta::jsontoolkit::JSON schema{
sourcemeta::jsontoolkit::from_file(options.at("").front())};

sourcemeta::jsontoolkit::ReferenceFrame frame;
sourcemeta::jsontoolkit::ReferenceMap references;
sourcemeta::jsontoolkit::frame(schema, frame, references,
sourcemeta::jsontoolkit::default_schema_walker,
resolver(options))
.wait();
sourcemeta::jsontoolkit::ReferenceFrame frame;
sourcemeta::jsontoolkit::ReferenceMap references;
sourcemeta::jsontoolkit::frame(schema, frame, references,
sourcemeta::jsontoolkit::default_schema_walker,
resolver(options))
.wait();

bool outputJson = options.contains("json") || options.contains("j");
if (outputJson) {
sourcemeta::jsontoolkit::JSON outputJson = sourcemeta::jsontoolkit::JSON::make_object();
auto frameJson = sourcemeta::jsontoolkit::JSON::make_object();
auto referencesJson = sourcemeta::jsontoolkit::JSON::make_object();
const auto output_json = options.contains("json") || options.contains("j");
if (output_json) {
auto output_json_object = sourcemeta::jsontoolkit::JSON::make_object();
auto frame_json = sourcemeta::jsontoolkit::JSON::make_object();
auto references_json = sourcemeta::jsontoolkit::JSON::make_object();

for (const auto &[key, entry] : frame) {
sourcemeta::jsontoolkit::JSON frameEntry = sourcemeta::jsontoolkit::JSON::make_object();
frameEntry.assign("schema", sourcemeta::jsontoolkit::JSON{entry.root.value_or("<ANONYMOUS>")});
std::ostringstream pointer_stream;
sourcemeta::jsontoolkit::stringify(entry.pointer, pointer_stream);
frameEntry.assign("pointer", sourcemeta::jsontoolkit::JSON{pointer_stream.str()});
frameEntry.assign("baseURI", sourcemeta::jsontoolkit::JSON{entry.base});
frameEntry.assign("type", sourcemeta::jsontoolkit::JSON{enumToString(entry.type)});
std::ostringstream reference_stream;
sourcemeta::jsontoolkit::stringify(entry.relative_pointer, reference_stream);
frameEntry.assign("relativePointer", sourcemeta::jsontoolkit::JSON{reference_stream.str()});
frameEntry.assign("dialect", sourcemeta::jsontoolkit::JSON{entry.dialect});
frameJson.assign(key.second, sourcemeta::jsontoolkit::JSON{frameEntry});
}
outputJson.assign("frames", sourcemeta::jsontoolkit::JSON{frameJson});
for (const auto &[key, entry] : frame) {
auto frame_entry = sourcemeta::jsontoolkit::JSON::make_object();
frame_entry.assign("schema", sourcemeta::jsontoolkit::JSON{entry.root.value_or("<ANONYMOUS>")});
std::ostringstream pointer_stream;
sourcemeta::jsontoolkit::stringify(entry.pointer, pointer_stream);
frame_entry.assign("pointer", sourcemeta::jsontoolkit::JSON{pointer_stream.str()});
frame_entry.assign("baseURI", sourcemeta::jsontoolkit::JSON{entry.base});
frame_entry.assign("type", sourcemeta::jsontoolkit::JSON{enum_to_string(entry.type)});
std::ostringstream reference_stream;
sourcemeta::jsontoolkit::stringify(entry.relative_pointer, reference_stream);
frame_entry.assign("relativePointer", sourcemeta::jsontoolkit::JSON{reference_stream.str()});
frame_entry.assign("dialect", sourcemeta::jsontoolkit::JSON{entry.dialect});
frame_json.assign(key.second, sourcemeta::jsontoolkit::JSON{frame_entry});
}
output_json_object.assign("frames", sourcemeta::jsontoolkit::JSON{frame_json});

for (const auto &[pointer, entry] : references) {
auto refEntry = sourcemeta::jsontoolkit::JSON::make_object();
refEntry.assign("type", sourcemeta::jsontoolkit::JSON{pointer.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic ? "Dynamic" : "Static"});
refEntry.assign("destination", sourcemeta::jsontoolkit::JSON{entry.destination});
if (entry.base.has_value()) {
refEntry.assign("fragmentBaseURI", sourcemeta::jsontoolkit::JSON{entry.base.value()});
}
if (entry.fragment.has_value()) {
refEntry.assign("fragment", sourcemeta::jsontoolkit::JSON{entry.fragment.value()});
}
std::ostringstream ref_entry_stream;
sourcemeta::jsontoolkit::stringify(pointer.second, ref_entry_stream);
referencesJson.assign(ref_entry_stream.str(), sourcemeta::jsontoolkit::JSON{refEntry});
}
outputJson.assign("references", sourcemeta::jsontoolkit::JSON{referencesJson});

std::ostringstream print_stream;
sourcemeta::jsontoolkit::prettify(outputJson, print_stream);
std::cout << print_stream.str() << std::endl;
} else {
for (const auto &[key, entry] : frame) {
std::cout << "(LOCATION) URI: " << key.second << "\n";
std::cout << " Schema : " << entry.root.value_or("<ANONYMOUS>") << "\n";
std::cout << " Pointer :";
if (!entry.pointer.empty()) {
std::cout << " ";
}
std::cout<<"\n";
sourcemeta::jsontoolkit::stringify(entry.pointer, std::cout);
std::cout << " Base URI : " << entry.base << "\n";
std::cout << " Relative Pointer :";
if (!entry.relative_pointer.empty()) {
std::cout << " ";
}
sourcemeta::jsontoolkit::stringify(entry.relative_pointer, std::cout);
std::cout << "\n";
std::cout << " Dialect : " << entry.dialect << "\n";
}
for (const auto &[pointer, entry] : references) {
auto ref_entry = sourcemeta::jsontoolkit::JSON::make_object();
ref_entry.assign("type",
sourcemeta::jsontoolkit::JSON{pointer.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic ? "Dynamic" : "Static"});
ref_entry.assign("destination", sourcemeta::jsontoolkit::JSON{entry.destination});
if (entry.base.has_value()) {
ref_entry.assign("fragmentBaseURI", sourcemeta::jsontoolkit::JSON{entry.base.value()});
}
if (entry.fragment.has_value()) {
ref_entry.assign("fragment", sourcemeta::jsontoolkit::JSON{entry.fragment.value()});
}
std::ostringstream ref_entry_stream;
sourcemeta::jsontoolkit::stringify(pointer.second, ref_entry_stream);
references_json.assign(ref_entry_stream.str(), sourcemeta::jsontoolkit::JSON{ref_entry});
}
output_json_object.assign("references", sourcemeta::jsontoolkit::JSON{references_json});

for (const auto &[pointer, entry] : references) {
std::cout << "(REFERENCE) URI: ";
sourcemeta::jsontoolkit::stringify(pointer.second, std::cout);
std::cout << "\n";
std::cout << " Type : " << (pointer.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic ? "Dynamic" : "Static") << "\n";
std::cout << " Destination : " << entry.destination << "\n";
std::ostringstream print_stream;
sourcemeta::jsontoolkit::prettify(output_json_object, print_stream);
std::cout << print_stream.str() << std::endl;
} else {
for (const auto &[key, entry] : frame) {
std::cout << "(LOCATION) URI: " << key.second << "\n";
std::cout << " Schema : " << entry.root.value_or("<ANONYMOUS>") << "\n";
std::cout << " Pointer :";
if (!entry.pointer.empty()) {
std::cout << " ";
}
sourcemeta::jsontoolkit::stringify(entry.pointer, std::cout);
std::cout << "\n";
std::cout << " Base URI : " << entry.base << "\n";
std::cout << " Relative Pointer :";
if (!entry.relative_pointer.empty()) {
std::cout << " ";
}
sourcemeta::jsontoolkit::stringify(entry.relative_pointer, std::cout);
std::cout << "\n";
std::cout << " Dialect : " << entry.dialect << "\n";
}

for (const auto &[pointer, entry] : references) {
std::cout << "(REFERENCE) URI: ";
sourcemeta::jsontoolkit::stringify(pointer.second, std::cout);
std::cout << "\n";
std::cout << " Type : " << (pointer.first == sourcemeta::jsontoolkit::ReferenceType::Dynamic ? "Dynamic" : "Static") << "\n";
std::cout << " Destination : " << entry.destination << "\n";

if (entry.base.has_value()) {
std::cout << " - (w/o fragment) : " << entry.base.value() << "\n";
}
if (entry.base.has_value()) {
std::cout << " - (w/o fragment) : " << entry.base.value() << "\n";
}

if (entry.fragment.has_value()) {
std::cout << " - (fragment) : " << entry.fragment.value() << "\n";
}
}
if (entry.fragment.has_value()) {
std::cout << " - (fragment) : " << entry.fragment.value() << "\n";
}
}
}

return EXIT_SUCCESS;
}
return EXIT_SUCCESS;
}
2 changes: 1 addition & 1 deletion test/frame.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ cat << 'EOF' > "$TMP/expected.txt"
- (fragment) : /$defs/string
EOF

diff "$TMP/result.txt" "$TMP/expected.txt"
diff "$TMP/result.txt" "$TMP/expected.txt"
98 changes: 98 additions & 0 deletions test/frame_json_output.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/bin/sh

set -o errexit
set -o nounset

TMP="$(mktemp -d)"
clean() { rm -rf "$TMP"; }
trap clean EXIT

cat << 'EOF' > "$TMP/schema.json"
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com",
"$ref": "#/$defs/string",
"$defs": {
"string": { "type": "string" }
}
}
EOF

# Test with --json flag
"$1" frame "$TMP/schema.json" --json > "$TMP/result_json.txt"

# Test with -j flag
"$1" frame "$TMP/schema.json" -j > "$TMP/result.json"

cat << 'EOF' > "$TMP/expected_json.txt"
{
"frames": {
"https://example.com": {
"baseURI": "https://example.com",
"dialect": "https://json-schema.org/draft/2020-12/schema",
"pointer": "",
"relativePointer": "",
"schema": "https://example.com",
"type": "Resource"
},
"https://example.com#/$defs": {
"baseURI": "https://example.com",
"dialect": "https://json-schema.org/draft/2020-12/schema",
"pointer": "/$defs",
"relativePointer": "/$defs",
"schema": "https://example.com",
"type": "Pointer"
},
"https://example.com#/$defs/string": {
"baseURI": "https://example.com",
"dialect": "https://json-schema.org/draft/2020-12/schema",
"pointer": "/$defs/string",
"relativePointer": "/$defs/string",
"schema": "https://example.com",
"type": "Pointer"
},
"https://example.com#/$defs/string/type": {
"baseURI": "https://example.com",
"dialect": "https://json-schema.org/draft/2020-12/schema",
"pointer": "/$defs/string/type",
"relativePointer": "/$defs/string/type",
"schema": "https://example.com",
"type": "Pointer"
},
"https://example.com#/$id": {
"baseURI": "https://example.com",
"dialect": "https://json-schema.org/draft/2020-12/schema",
"pointer": "/$id",
"relativePointer": "/$id",
"schema": "https://example.com",
"type": "Pointer"
},
"https://example.com#/$ref": {
"baseURI": "https://example.com",
"dialect": "https://json-schema.org/draft/2020-12/schema",
"pointer": "/$ref",
"relativePointer": "/$ref",
"schema": "https://example.com",
"type": "Pointer"
},
"https://example.com#/$schema": {
"baseURI": "https://example.com",
"dialect": "https://json-schema.org/draft/2020-12/schema",
"pointer": "/$schema",
"relativePointer": "/$schema",
"schema": "https://example.com",
"type": "Pointer"
}
},
"references": {
"/$ref": {
"destination": "https://example.com#/$defs/string",
"fragment": "/$defs/string",
"fragmentBaseURI": "https://example.com",
"type": "Static"
}
}
}
EOF

diff "$TMP/result_json.txt" "$TMP/expected_json.txt"

0 comments on commit ae2f7f5

Please sign in to comment.