Skip to content

Commit

Permalink
Add support for maintaining ignore patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Aug 22, 2024
1 parent e0ad105 commit 5632b7c
Show file tree
Hide file tree
Showing 14 changed files with 352 additions and 39 deletions.
2 changes: 1 addition & 1 deletion assets/legacy/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function createLicense() {

export function ignoreLine(link) {
const hash = link.data('hash');
$.post(`/reviews/add_ignore?hash=${hash}&package=${link.data('packname')}`);
$.post(`/ignored-matches?hash=${hash}&package=${link.data('packname')}`);
$(`.hash-${hash}`).removeClass('risk-9');
const cs = $(`.hash-${hash} .fa-fire`);
if (link.hasClass('current-selector')) {
Expand Down
5 changes: 5 additions & 0 deletions assets/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {createLicense, ignoreLine, snippetNonLicense} from './legacy/util.js';
import ClassifySnippets from './vue/ClassifySnippets.vue';
import EditSnippet from './vue/EditSnippet.vue';
import IgnoredFiles from './vue/IgnoredFiles.vue';
import IgnoredMatches from './vue/IgnoredMatches.vue';
import KnownLicenses from './vue/KnownLicenses.vue';
import KnownProducts from './vue/KnownProducts.vue';
import OpenReviews from './vue/OpenReviews.vue';
Expand Down Expand Up @@ -56,6 +57,10 @@ window.cavil = {
app.mount('#edit-snippet');
},

setupIgnoredMatches() {
createApp(IgnoredMatches).mount('#ignored-matches');
},

setupIgnoredFiles() {
createApp(IgnoredFiles).mount('#ignored-files');
},
Expand Down
199 changes: 199 additions & 0 deletions assets/vue/IgnoredMatches.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<template>
<div>
<div class="row">
<div class="col-12 alert alert-primary" role="alert">
These ignored matches are used to decide which snippets the indexer should ignore when scanning for new keyword
matches.
</div>
</div>
<div>
<form>
<div class="row g-4">
<div class="col-lg-2">
<div class="form-floating">
<select v-model="params.limit" @change="gotoPage(1)" class="form-control">
<option>10</option>
<option>25</option>
<option>50</option>
<option>100</option>
</select>
<label class="form-label">Matches per Page</label>
</div>
</div>
<div class="col"></div>
<div id="cavil-pkg-filter" class="col-lg-3">
<form @submit.prevent="filterNow">
<div class="form-floating">
<input v-model="filter" type="text" class="form-control" placeholder="Filter" />
<label class="form-label">Filter</label>
</div>
</form>
</div>
</div>
</form>
<div class="row">
<div class="col-12">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>Ignored Match</th>
<th>Package</th>
<th>Created</th>
<th>Contributor</th>
<th>Owner</th>
<th></th>
</tr>
</thead>
<tbody v-if="matches === null">
<tr>
<td id="all-done" colspan="6"><i class="fas fa-sync fa-spin"></i> Loading ignored matches...</td>
</tr>
</tbody>
<tbody v-else-if="matches.length > 0">
<tr v-for="match in matches" :key="match.id">
<td v-if="match.snippetUrl !== null">
<a :href="match.snippetUrl">{{ match.hash }}</a>
</td>
<td v-else>{{ match.hash }}</td>
<td>{{ match.packname }}</td>
<td>{{ match.created }}</td>
<td>{{ match.contributor_login }}</td>
<td>{{ match.owner_login }}</td>
<td class="text-center">
<span class="cavil-action text-center">
<a @click="deleteMatch(match)" href="#"><i class="fas fa-trash"></i></a>
</span>
</td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td id="all-done" colspan="6">No ignored matches found.</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-6 mb-4">
<ShownEntries :end.sync="end" :start.sync="start" :total.sync="total" />
</div>
<div class="col-6 mb-4" id="cavil-pagination">
<PaginationLinks
@goto-page="gotoPage"
:end.sync="end"
:start.sync="start"
:total.sync="total"
:current-page.sync="currentPage"
:total-pages.sync="totalPages"
/>
</div>
</div>
</div>
</div>
</template>

<script>
import PaginationLinks from './components/PaginationLinks.vue';
import ShownEntries from './components/ShownEntries.vue';
import {genParamWatchers, getParams, setParam} from './helpers/params.js';
import Refresh from './mixins/refresh.js';
import UserAgent from '@mojojs/user-agent';
import moment from 'moment';
export default {
name: 'IgnoredMatches',
mixins: [Refresh],
components: {PaginationLinks, ShownEntries},
data() {
const params = getParams({
limit: 10,
offset: 0,
filter: ''
});
return {
end: 0,
matches: null,
params,
refreshUrl: '/pagination/matches/ignored',
filter: params.filter,
start: 0,
total: 0
};
},
computed: {
totalPages() {
return Math.ceil(this.total / this.params.limit);
},
currentPage() {
return Math.ceil(this.end / this.params.limit);
}
},
methods: {
async deleteMatch(match) {
const ua = new UserAgent({baseURL: window.location.href});
await ua.post(match.removeUrl, {query: {_method: 'DELETE'}});
this.doApiRefresh();
},
gotoPage(num) {
this.cancelApiRefresh();
const limit = this.params.limit;
this.params.offset = num * limit - limit;
this.matches = null;
this.doApiRefresh();
},
refreshData(data) {
this.start = data.start;
this.end = data.end;
this.total = data.total;
const matches = [];
for (const match of data.page) {
matches.push({
id: match.id,
hash: match.hash,
packname: match.packname,
created: moment(match.created_epoch * 1000).fromNow(),
contributor_login: match.contributor_login,
owner_login: match.owner_login,
removeUrl: `/ignored-matches/${match.id}`,
snippetUrl: match.snippet === null ? null : `/snippet/edit/${match.snippet.id}`
});
}
this.matches = matches;
},
filterNow() {
this.cancelApiRefresh();
this.matches = null;
this.doApiRefresh();
}
},
watch: {
...genParamWatchers('limit', 'offset'),
filter: function (val) {
this.params.filter = val;
this.params.offset = 0;
setParam('filter', val);
}
}
};
</script>

<style>
.table {
margin-top: 1rem;
}
.cavil-action a {
color: #212529;
text-decoration: none;
}
#cavil-pkg-filter form {
margin: 2px 0;
white-space: nowrap;
justify-content: flex-end;
}
#all-done {
text-align: center;
}
</style>
2 changes: 1 addition & 1 deletion assets/vue/ProposedPatterns.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export default {
change.editUrl = `/snippet/edit/${change.data.snippet}`;
change.removeUrl = `/licenses/proposed/remove/${change.token_hexsum}`;
change.createUrl = `/snippet/decision/${change.data.snippet}`;
change.ignoreUrl = '/reviews/add_ignore';
change.ignoreUrl = '/ignored-matches';
if (change.package !== null) change.package.pkgUrl = `/reviews/details/${change.package.id}`;
if (change.closest !== null) change.closest.licenseUrl = `/licenses/edit_pattern/${change.closest.id}`;
Expand Down
8 changes: 6 additions & 2 deletions lib/Cavil.pm
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ sub startup ($self) {
->to('Report#source', format => 'html');
$admin->post('/reviews/review_package/<id:num>')->to('Reviewer#review_package')->name('review_package');
$manager->post('/reviews/fasttrack_package/<id:num>')->to('Reviewer#fasttrack_package')->name('fasttrack_package');
$admin->post('/reviews/add_ignore')->to('Reviewer#add_ignore');
$admin->post('/reviews/reindex/<id:num>')->to('Reviewer#reindex_package')->name('reindex_package');
$public->get('/pagination/reviews/open')->to('Pagination#open_reviews')->name('pagination_open_reviews');
$public->get('/pagination/reviews/recent')->to('Pagination#recent_reviews')->name('pagination_recent_reviews');
Expand All @@ -229,9 +228,14 @@ sub startup ($self) {
$logged_in->get('/licenses/recent')->to('License#recent')->name('recent_patterns');
$logged_in->get('/licenses/recent/meta')->to('License#recent_meta')->name('recent_patterns_meta');

$admin->get('/ignored-matches')->to('Ignore#list_matches')->name('list_ignored_matches');
$admin->post('/ignored-matches')->to('Ignore#add_match');
$admin->delete('/ignored-matches/<id:num>')->to('Ignore#remove_match')->name('remove_ignored_match');
$admin->get('/pagination/matches/ignored')->to('Pagination#ignored_matches')->name('pagination_ignored_matches');

$admin->get('/ignored-files')->to('Ignore#list_globs')->name('list_globs');
$admin->post('/ignored-files')->to('Ignore#add_glob')->name('add_ignore');
$admin->delete('/ignored-files/<id:num>')->to('Ignore#remove_glob')->name('remove_ignore');
$admin->delete('/ignored-files/<id:num>')->to('Ignore#remove_glob')->name('remove_ignored_file');
$admin->get('/pagination/files/ignored')->to('Pagination#ignored_files')->name('pagination_ignored_files');

# Public because of fine grained access controls (owner of proposal may remove it again)
Expand Down
38 changes: 38 additions & 0 deletions lib/Cavil/Controller/Ignore.pm
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,52 @@ sub add_glob ($self) {
return $self->render(json => 'ok');
}

sub add_match ($self) {
my $validation = $self->validation;
$validation->required('hash')->like(qr/^[a-f0-9]{32}$/i);
$validation->required('package');
$validation->optional('delay')->num;
$validation->optional('contributor');
return $self->reply->json_validation_error if $validation->has_error;

my $owner_id = $self->users->id_for_login($self->current_user);
my $contributor = $validation->param('contributor');
my $contributor_id = $contributor ? $self->users->id_for_login($contributor) : undef;
my $delay = $validation->param('delay') // 0;

my $hash = $validation->param('hash');
$self->packages->ignore_line(
{
package => $validation->param('package'),
hash => $hash,
owner => $owner_id,
contributor => $contributor_id,
delay => $delay
}
);
$self->patterns->remove_proposal($hash);

return $self->render(json => 'ok');
}

sub list_globs ($self) {
$self->render('ignore/list_globs');
}

sub list_matches ($self) {
$self->render('ignore/list_matches');
}

sub remove_glob ($self) {
return $self->render(status => 400, json => {error => 'Glob does not exist'})
unless $self->ignored_files->remove($self->param('id'), $self->current_user);
return $self->render(json => 'ok');
}

sub remove_match ($self) {
return $self->render(status => 400, json => {error => 'Ignored match does not exist'})
unless $self->packages->remove_ignored_line($self->param('id'), $self->current_user);
return $self->render(json => 'ok');
}

1;
15 changes: 15 additions & 0 deletions lib/Cavil/Controller/Pagination.pm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@
package Cavil::Controller::Pagination;
use Mojo::Base 'Mojolicious::Controller', -signatures;

sub ignored_matches ($self) {
my $v = $self->validation;
$v->optional('limit')->num;
$v->optional('offset')->num;
$v->optional('filter');
return $self->reply->json_validation_error if $v->has_error;
my $limit = $v->param('limit') // 10;
my $offset = $v->param('offset') // 0;
my $search = $v->param('filter') // '';

my $page
= $self->helpers->patterns->paginate_ignored_matches({limit => $limit, offset => $offset, search => $search});
$self->render(json => $page);
}

sub ignored_files ($self) {
my $v = $self->validation;
$v->optional('limit')->num;
Expand Down
28 changes: 0 additions & 28 deletions lib/Cavil/Controller/Reviewer.pm
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,6 @@ my $SMALL_REPORT_RE = qr/
)$
/xi;

sub add_ignore ($self) {
my $validation = $self->validation;
$validation->required('hash')->like(qr/^[a-f0-9]{32}$/i);
$validation->required('package');
$validation->optional('delay')->num;
$validation->optional('contributor');
return $self->reply->json_validation_error if $validation->has_error;

my $owner_id = $self->users->id_for_login($self->current_user);
my $contributor = $validation->param('contributor');
my $contributor_id = $contributor ? $self->users->id_for_login($contributor) : undef;
my $delay = $validation->param('delay') // 0;

my $hash = $validation->param('hash');
$self->packages->ignore_line(
{
package => $validation->param('package'),
hash => $hash,
owner => $owner_id,
contributor => $contributor_id,
delay => $delay
}
);
$self->patterns->remove_proposal($hash);

return $self->render(json => 'ok');
}

sub details ($self) {
my $id = $self->stash('id');
my $pkgs = $self->packages;
Expand Down
Loading

0 comments on commit 5632b7c

Please sign in to comment.