Skip to content
This repository has been archived by the owner on Jul 24, 2021. It is now read-only.

Commit

Permalink
add "latest" flag to responses containing metadata for json_schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
karenetheridge committed Jan 8, 2021
1 parent d2eb4e0 commit a93c08e
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 1 deletion.
20 changes: 20 additions & 0 deletions docs/json-schema/response.json
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,13 @@
"JSONSchemaDescriptions" : {
"items" : {
"additionalProperties" : false,
"if" : {
"properties" : {
"deactivated" : {
"type" : "string"
}
}
},
"properties" : {
"$id" : {
"format" : "uri-reference",
Expand Down Expand Up @@ -1689,6 +1696,11 @@
"readOnly" : true,
"title" : "ID"
},
"latest" : {
"description" : "true when it is the latest of its type-name series",
"title" : "Latest in Type-Name Series?",
"type" : "boolean"
},
"name" : {
"$ref" : "common.json#/$defs/json_pointer_token",
"readOnly" : true,
Expand All @@ -1712,10 +1724,18 @@
"type",
"name",
"version",
"latest",
"created",
"created_user",
"deactivated"
],
"then" : {
"properties" : {
"latest" : {
"const" : false
}
}
},
"type" : "object"
},
"type" : "array",
Expand Down
10 changes: 10 additions & 0 deletions docs/modules/Conch::DB::ResultSet::JSONSchema.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ Chainable resultset that restricts the resultset to the single row that matches
the indicated resource. (Does **not** fetch the indicated resource content -- you would need a
`->column(...)` for that.)

### with\_latest\_flag

Chainable resultset that adds the `latest` boolean flag to each result, indicating whether
that row is the latest of its type-name series (that is, whether it can be referenced as
`/json_schema/type/name/latest`).

The query will be closed off as a subselect (that additional chaining will SELECT FROM),
so it makes a difference whether you add things to the resultset before or after calling this
method.

## LICENSING

Copyright Joyent, Inc.
Expand Down
13 changes: 13 additions & 0 deletions json-schema/response.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1918,6 +1918,7 @@ $defs:
- type
- name
- version
- latest
- created
- created_user
- deactivated
Expand Down Expand Up @@ -1947,6 +1948,10 @@ $defs:
title: Version
readOnly: true
$ref: common.yaml#/$defs/positive_integer
latest:
title: Latest in Type-Name Series?
description: true when it is the latest of its type-name series
type: boolean
created:
title: Created
readOnly: true
Expand All @@ -1960,5 +1965,13 @@ $defs:
title: Deactivated
type: [ 'null', string ]
format: date-time
if:
properties:
deactivated:
type: string
then:
properties:
latest:
const: false

# vim: set sts=2 sw=2 et :
3 changes: 2 additions & 1 deletion lib/Conch/Controller/JSONSchema.pm
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,11 @@ sub get_metadata ($c) {
}

$rs = $rs
->with_latest_flag # closes off the resultset as a subquery!
->with_description
->with_created_user
->remove_columns([ 'body' ])
->order_by([ qw(json_schema.name version) ]);
->order_by([ qw(json_schema.name json_schema.version) ]);

$c->status(200, [ $rs->all ]);
}
Expand Down
3 changes: 3 additions & 0 deletions lib/Conch/DB/Result/JSONSchema.pm
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@ sub TO_JSON ($self) {
$data->{'$id'} = '/json_schema/'.join('/', $data->@{qw(type name version)});
$data->{description} = $self->get_column('description') if $self->has_column_loaded('description');

# Mojo::JSON renders \0, \1 as json booleans
$data->{latest} = \$self->get_column('latest');

if (my $user_cache = $self->related_resultset('created_user')->get_cache) {
$data->{created_user} = +{ map +($_ => $user_cache->[0]->$_), qw(id name email) };
}
Expand Down
36 changes: 36 additions & 0 deletions lib/Conch/DB/ResultSet/JSONSchema.pm
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,42 @@ sub resource ($self, $type, $name, $version_or_latest) {
return $rs;
}

=head2 with_latest_flag
Chainable resultset that adds the C<latest> boolean flag to each result, indicating whether
that row is the latest of its type-name series (that is, whether it can be referenced as
C</json_schema/type/name/latest>).
The query will be closed off as a subselect (that additional chaining will SELECT FROM),
so it makes a difference whether you add things to the resultset before or after calling this
method.
=cut

sub with_latest_flag ($self) {
my $me = $self->current_source_alias;

# "Note that first_value, last_value, and nth_value consider only the rows within the
# “window frame”, which by default contains the rows from the start of the partition
# through the last peer of the current row."
# therefore we sort in reverse, so latest comes first and is visible to all rows in the
# window. see https://www.postgresql.org/docs/10/functions-window.html
my $rs = $self
->add_columns([qw(id type name version deactivated)]) # make sure these columns are available
->search(undef, {
'+select' => [{
'' => \"first_value($me.id) over (partition by $me.type, $me.name order by $me.deactivated asc nulls first, version desc)",
-as => 'last_row_id',
}],
})
->as_subselect_rs;

# RT#132276: do not select columns that aren't there
$rs = $rs->columns($self->{attrs}{columns}) if exists $self->{attrs}{columns};

return $rs->add_columns({ latest => \"$me.id = last_row_id and $me.deactivated is null" });
}

1;
__END__
Expand Down
8 changes: 8 additions & 0 deletions t/integration/json_schema-authed.t
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ $t_ro->get_ok('/json_schema/foo')
type => 'foo',
name => 'alpha',
version => 1,
latest => JSON::PP::true,
created => ignore,
created_user => { map +($_ => $ro_user->$_), qw(id name email) },
deactivated => undef,
Expand All @@ -325,6 +326,7 @@ $t_ro->get_ok('/json_schema/foo')
type => 'foo',
name => 'bar',
version => 1,
latest => JSON::PP::false,
created => Conch::Time->new($db_rows[0]->{created})->to_string,
created_user => { map +($_ => $ro_user->$_), qw(id name email) },
deactivated => undef,
Expand All @@ -336,11 +338,13 @@ $t_ro->get_ok('/json_schema/foo')
type => 'foo',
name => 'bar',
version => 2,
latest => JSON::PP::true,
created => Conch::Time->new($db_rows[1]->{created})->to_string,
created_user => { map +($_ => $ro_user->$_), qw(id name email) },
deactivated => undef,
},
]);

my $metadata = $t_ro->tx->res->json;

$t_other->get_ok('/json_schema/foo')
Expand Down Expand Up @@ -372,7 +376,10 @@ $t_other->delete_ok($_)
$t_ro->delete_ok('/json_schema/foo/bar/2')
->status_is(204)
->log_debug_is('Deactivated JSON Schema id '.$schema2_id.' (/json_schema/foo/bar/2); latest of this type and name is now /json_schema/foo/bar/1');

$metadata->[1]->{latest} = JSON::PP::true;
$metadata->[2]->{deactivated} = re(qr/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3,9}Z$/);
$metadata->[2]->{latest} = JSON::PP::false;

$t_ro->delete_ok('/json_schema/'.$schema2_id)
->status_is(410);
Expand Down Expand Up @@ -403,6 +410,7 @@ $t_ro->delete_ok('/json_schema/foo/bar/1')
->status_is(204)
->log_debug_is('Deactivated JSON Schema id '.$schema1_id.' (/json_schema/foo/bar/1); no schemas of this type and name remain');
$metadata->[1]->{deactivated} = re(qr/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3,9}Z$/);
$metadata->[1]->{latest} = JSON::PP::false;

$t_ro->get_ok('/json_schema/foo/bar/1')
->status_is(200)
Expand Down

0 comments on commit a93c08e

Please sign in to comment.