Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Alloy] Add CLI tools for inspecting and interacting with the Alloy API #377

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions bin/alloy/inspect-designs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env perl

=head1 NAME

inspect-designs - Inspect and validate Alloy V2 API designs and configurations

=head1 SYNOPSIS

# Inspect a single design
bin/alloy/inspect-designs northumberland_alloy designs_FMSDefect_613f1ff6dcf79201590c5eeb

# Inspect all designs
bin/alloy/inspect-designs northumberland_alloy

=head1 DESCRIPTION

This tool allows inspection of Alloy API designs and related info.

It can be used to:
- Validate single design configurations
- List all available designs
- Check API connectivity
- Verify design structure and attributes

=head1 CONFIGURATION

Requires a valid Alloy V2 configuration file with api_key and api_url defined.

=cut

use strict;
use warnings;

BEGIN {
use File::Basename qw(dirname);
use File::Spec;
my $d = dirname(File::Spec->rel2abs($0));
require "$d/../../setenv.pl";
}

use Integrations::AlloyV2;
use JSON::MaybeXS;
use Try::Tiny;
use feature qw(say);

# Get config name from command line
my $config = shift;
die "Usage: $0 <config_filename> (e.g. 'northumberland_alloy')\n" unless $config;

# Initialize Alloy integration
my $alloy = Integrations::AlloyV2->new(config_filename => $config);
die "No API key found in config\n" unless $alloy->config->{api_key};

my @designs;

if ($ARGV[0]) {
# Single design mode
my $design = try {
$alloy->api_call(
call => "design/$ARGV[0]",
);
} catch {
die "Error fetching design $ARGV[0]: $_\n";
};

die "No design found with code $ARGV[0]\n" unless $design && $design->{design};
push @designs, $design;
} else {
# All designs mode
my $page = 1;
my $page_size = 100;

say STDERR "Fetching designs from Alloy API...";

while (1) {
my $designs = try {
$alloy->api_call(
call => "design",
params => {
page => $page,
pageSize => $page_size
}
);
} catch {
die "Error fetching designs: $_\n";
};

last unless $designs->{results} && @{$designs->{results}};

push @designs, @{$designs->{results}};

last if $page >= $designs->{totalPages};
$page++;
}

say "\nFound " . scalar(@designs) . " designs\n";
}

foreach my $design (@designs) {
my $design_info = $design->{design};

say "Design: " . $design_info->{name};
say "Code: " . $design_info->{code};
say "Context: " . $design_info->{context};

if ($design_info->{attributes} && @{$design_info->{attributes}}) {
say "\nAttributes:";
foreach my $attr (@{$design_info->{attributes}}) {
# Skip if no code/name
next unless $attr->{code} && $attr->{name};

say sprintf(" %-40s %-20s %-10s %s",
$attr->{code},
$attr->{name},
$attr->{type},
$attr->{required} ? "Required" : "Optional"
);
}
}

if ($design_info->{implements} && @{$design_info->{implements}}) {
say "\nImplements:";
foreach my $impl (@{$design_info->{implements}}) {
say " " . $impl->{code};
}
}

say "\n" . "-" x 80 . "\n";
}
88 changes: 88 additions & 0 deletions bin/alloy/invoke
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#!/usr/bin/env perl

=head1 NAME

invoke - Command line interface for making Alloy API calls

=head1 SYNOPSIS

bin/alloy/invoke <config_filename> <method_name> [parameters_json]

=head1 DESCRIPTION

Makes API calls to an Alloy instance with optional parameters. Parameters can be
passed as JSON (array/object) or as a simple string.

=head1 CONFIGURATION

Requires a valid Alloy V2 configuration file for the given config_filename.

=head1 EXAMPLES

Basic API call to get designs:

bin/alloy/invoke northumberland_alloy get_designs

Search with JSON parameters:

bin/alloy/invoke northumberland_alloy search '{"type":"Query","properties":{"designCode":"design_fms_1"}}'

Get valuetype mapping:

bin/alloy/invoke northumberland_alloy get_valuetype_mapping

Get parent attributes for a design:

bin/alloy/invoke northumberland_alloy get_parent_attributes "design_fms_1"

=cut

use strict;
use warnings;

BEGIN {
use File::Basename qw(dirname);
use File::Spec;
my $d = dirname(File::Spec->rel2abs($0));
require "$d/../../setenv.pl";
}

use Try::Tiny;
use JSON::MaybeXS;
use Integrations::AlloyV2;

my ($config, $method, $params_json) = @ARGV;
die "Usage: $0 <config_filename> <method_name> [parameters_json]\n" unless $config && $method;

my $alloy = Integrations::AlloyV2->new(config_filename => $config);
die "No API key found in config\n" unless $alloy->config->{api_key};

try {
my @params;
if ($params_json) {
# First try parsing as JSON
my $params_decoded = eval { decode_json($params_json) };
if ($@) {
# If JSON parsing fails, treat as plain parameter
@params = ($params_json);
} else {
@params = ref $params_decoded eq 'ARRAY' ? @$params_decoded : ($params_decoded);
}
}

die "Method '$method' does not exist in AlloyV2\n"
unless $alloy->can($method);

my $res = $alloy->$method(@params);

if ($res) {
print JSON->new->pretty->canonical->encode($res);
exit 0;
} else {
print "Method call '$method' returned no results ($config)\n";
exit 1;
}
} catch {
print "Error calling method '$method' ($config): $_\n";
exit 1;
};
Loading