Skip to content

Commit 259ff87

Browse files
committed
WIP: create inflator and subsequent code cleanup.
WIP: create inflator and subsequent code cleanup.
1 parent 0fab2c4 commit 259ff87

File tree

13 files changed

+150
-108
lines changed

13 files changed

+150
-108
lines changed

conf/permissions.dist.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,11 @@ db_permissions:
163163
allowed_roles: ['course_admin', 'instructor']
164164
Settings:
165165
getDefaultCourseSettings:
166-
allowed_roles: ['*']
166+
allowed_roles: ['course_admin', 'instructor','student']
167167
getCourseSettings:
168-
allowed_roles: ['*']
168+
allowed_roles: ['course_admin', 'instructor','student']
169+
getCourseSetting:
170+
allowed_roles: ['course_admin', 'instructor','student']
169171
updateCourseSetting:
170172
allowed_roles: ['course_admin', 'instructor']
171173
deleteCourseSetting:

lib/DB/Schema/Result/CourseSetting.pm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ C<value>: the value of the setting as a JSON so different types of data can be s
3333

3434
__PACKAGE__->table('course_setting');
3535

36-
__PACKAGE__->load_components('InflateColumn::Serializer', 'Core');
36+
__PACKAGE__->load_components(qw/InflateColumn::Serializer InflateColumn::JSONValue Core/);
3737

3838
__PACKAGE__->add_columns(
3939
course_setting_id => {
@@ -53,8 +53,8 @@ __PACKAGE__->add_columns(
5353
value => {
5454
data_type => 'text',
5555
default_value => '{}',
56-
serializer_class => 'JSON',
57-
serializer_options => { utf8 => 1 }
56+
retrieve_on_insert => 1,
57+
inflate_value => 1,
5858
},
5959
);
6060

lib/DB/Schema/Result/GlobalSetting.pm

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ C<subcategory>: the subcategory of the setting (may be null)
5353

5454
__PACKAGE__->table('global_setting');
5555

56-
__PACKAGE__->load_components('InflateColumn::Serializer', 'Core');
56+
__PACKAGE__->load_components(qw/InflateColumn::Serializer InflateColumn::JSONValue Core/);
5757

5858
__PACKAGE__->add_columns(
5959
setting_id => {
@@ -69,8 +69,7 @@ __PACKAGE__->add_columns(
6969
data_type => 'text',
7070
default_value => '{}',
7171
retrieve_on_insert => 1,
72-
serializer_class => 'JSON',
73-
serializer_options => { utf8 => 1 }
72+
inflate_value => 1,
7473
},
7574
description => {
7675
data_type => 'text',

lib/DB/Schema/ResultSet/Course.pm

Lines changed: 27 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,6 @@ sub getGlobalSettings ($self, %args) {
283283
my @settings = map {
284284
{ $_->get_inflated_columns };
285285
} @global_settings;
286-
for my $setting (@settings) {
287-
# The default_value is stored as a JSON and needs to be parsed.
288-
$setting->{default_value} = $setting->{default_value}->{value};
289-
}
290286
return \@settings;
291287
}
292288

@@ -323,7 +319,6 @@ sub getGlobalSetting ($self, %args) {
323319
unless $global_setting;
324320
return $global_setting if $args{as_result_set};
325321
my $setting_to_return = { $global_setting->get_inflated_columns };
326-
$setting_to_return->{default_value} = $setting_to_return->{default_value}->{value};
327322
return $setting_to_return;
328323
}
329324

@@ -356,20 +351,11 @@ sub getCourseSettings ($self, %args) {
356351
my @settings_from_db = $course->course_settings;
357352

358353
return \@settings_from_db if $args{as_result_set};
359-
my @settings_to_return = ($args{merged})
360-
? map {
361-
{ $_->get_inflated_columns, $_->global_setting->get_inflated_columns };
362-
} @settings_from_db
363-
: map {
364-
{ $_->get_inflated_columns };
365-
} @settings_from_db;
366-
367-
for my $setting (@settings_to_return) {
368-
# value and default_value are decoded from JSON as a hash. Return to a value.
369-
for my $key (qw/default_value value/) {
370-
$setting->{$key} = $setting->{$key}->{value} if defined($setting->{$key});
371-
}
372-
}
354+
my @settings_to_return = map {
355+
$args{merged}
356+
? { $_->get_inflated_columns, $_->global_setting->get_inflated_columns }
357+
: { $_->get_inflated_columns };
358+
} @settings_from_db;
373359
return \@settings_to_return;
374360
}
375361

@@ -400,25 +386,31 @@ A single course setting as either a hashref or a C<DBIx::Class::ResultSet::Cours
400386
=cut
401387

402388
sub getCourseSetting ($self, %args) {
403-
404389
my $global_setting = $self->getGlobalSetting(info => $args{info}, as_result_set => 1);
405390
DB::Exception::SettingNotFound->throw(
406-
message => "The setting with name: '" . $args{info}->{setting_name} . "' is not a defined info.")
391+
message => "The global setting with name: '" . $args{info}->{setting_name} . "' is not a defined info.")
407392
unless defined($global_setting);
408393

409394
my $course = $self->getCourse(info => getCourseInfo($args{info}), as_result_set => 1);
410395
my $setting = $course->course_settings->find({ setting_id => $global_setting->setting_id });
411396

397+
DB::Exception::SettingNotFound->throw(
398+
message => 'The course setting with '
399+
. (
400+
$args{info}->{setting_name} ? " name: '$args{info}->{setting_name}'"
401+
: "setting_id of $args{info}->{setting_id} is not a found in the course "
402+
)
403+
. (
404+
$args{info}->{course_name} ? ("with name '" . $args{info}->{course_name} . "'")
405+
: "with course_id of $args{info}->{course_id}"
406+
)
407+
) unless defined($setting);
408+
412409
return $setting if $args{as_result_set};
413410
my $setting_to_return =
414411
$args{merged}
415412
? { $setting->get_inflated_columns, $setting->global_setting->get_inflated_columns }
416413
: { $setting->get_inflated_columns };
417-
418-
# value and default_value are decoded from JSON as a hash. Return to a value.
419-
for my $key (qw/default_value value/) {
420-
$setting_to_return->{$key} = $setting_to_return->{$key}->{value} if defined($setting_to_return->{$key});
421-
}
422414
return $setting_to_return;
423415
}
424416

@@ -451,44 +443,30 @@ A single course setting as either a hashref or a C<DBIx::Class::ResultSet::Cours
451443
=cut
452444

453445
sub updateCourseSetting ($self, %args) {
454-
my $course = $self->getCourse(info => getCourseInfo($args{info}), as_result_set => 1);
455-
446+
my $course = $self->getCourse(info => getCourseInfo($args{info}), as_result_set => 1);
456447
my $global_setting = $self->getGlobalSetting(info => getSettingInfo($args{info}));
457448

458449
my $course_setting = $course->course_settings->find({
459450
setting_id => $global_setting->{setting_id}
460451
});
461452

462-
# Check that the setting is valid.
463-
464453
my $params = {
465454
course_id => $course->course_id,
466455
setting_id => $global_setting->{setting_id},
467-
value => { value => $args{params}{value} }
456+
value => $args{params}{value}
468457
};
469458

470-
# remove the following fields before checking for valid settings:
471-
delete $global_setting->{$_} for (qw/setting_id course_id/);
459+
isValidSetting($global_setting, $params->{value});
472460

473-
isValidSetting($global_setting, $params->{value}{value});
474-
475-
# The course_id must be deleted to ensure it is written to the database correctly.
476-
delete $params->{course_id} if defined($params->{course_id});
477-
478-
my $updated_course_setting =
461+
my $up_setting =
479462
defined($course_setting) ? $course_setting->update($params) : $course->add_to_course_settings($params);
480463

481-
return $updated_course_setting if $args{as_result_set};
464+
return $up_setting if $args{as_result_set};
482465
my $setting_to_return =
483466
($args{merged})
484-
? { $updated_course_setting->get_inflated_columns,
485-
$updated_course_setting->global_setting->get_inflated_columns }
486-
: { $updated_course_setting->get_inflated_columns };
467+
? { $up_setting->get_inflated_columns, $up_setting->global_setting->get_inflated_columns }
468+
: { $up_setting->get_inflated_columns };
487469

488-
# value and default_value are decoded from JSON as a hash. Return to a value.
489-
for my $key (qw/default_value value/) {
490-
$setting_to_return->{$key} = $setting_to_return->{$key}->{value} if defined($setting_to_return->{$key});
491-
}
492470
return $setting_to_return;
493471
}
494472

@@ -516,21 +494,8 @@ A single course setting as either a hashref or a C<DBIx::Class::ResultSet::Cours
516494
=cut
517495

518496
sub deleteCourseSetting ($self, %args) {
519-
my $setting = $self->getCourseSetting(info => $args{info}, as_result_set => 1);
520-
my $deleted_setting = $setting->delete;
521-
522-
return $deleted_setting if $args{as_result_set};
523-
524-
my $setting_to_return =
525-
($args{merged})
526-
? { $deleted_setting->get_inflated_columns, $deleted_setting->global_setting->get_inflated_columns }
527-
: { $deleted_setting->get_inflated_columns };
528-
529-
# value and default_value are decoded from JSON as a hash. Return to a value.
530-
for my $key (qw/default_value value/) {
531-
$setting_to_return->{$key} = $setting_to_return->{$key}->{value} if defined($setting_to_return->{$key});
532-
}
533-
return $setting_to_return;
497+
$self->getCourseSetting(info => $args{info}, as_result_set => 1)->delete;
498+
return;
534499
}
535500

536501
1;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package DBIx::Class::InflateColumn::JSONValue;
2+
3+
=pod
4+
5+
DBIx::Class::InflateColumn::JSONValue - encodes any value (string, number, arrray, ...)
6+
in the form value => {}, then JSON encoded.
7+
8+
=cut
9+
10+
use strict;
11+
use warnings;
12+
13+
use base qw/DBIx::Class/;
14+
use Mojo::JSON qw/encode_json decode_json/;
15+
use Try::Tiny;
16+
17+
# This is a simplified version of DBIx::Class::InflateColumn::DateTime
18+
19+
sub register_column {
20+
my ($self, $column, $info, @rest) = @_;
21+
22+
$self->next::method($column, $info, @rest);
23+
return unless $info->{inflate_value};
24+
25+
$self->inflate_column(
26+
$column => {
27+
inflate => sub {
28+
my $str = shift;
29+
# This is a bit of a hack. It appears that sometimes the deflate isn't called on the values
30+
# of type string and number so they don't need to be decoded.
31+
try {
32+
my $hash = decode_json($str);
33+
return $hash->{value};
34+
} catch {
35+
# If the value in $str is a number, return the numerical value.
36+
return $str =~ /^-?(0|([1-9][0-9]*))(\.[0-9]+)?([eE][-+]?[0-9]+)?$/ ? 0 + $str : $str;
37+
};
38+
},
39+
deflate => sub { return encode_json({ value => shift }); }
40+
}
41+
);
42+
return;
43+
}
44+
45+
1;

lib/WeBWorK3.pm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ sub settingsRoutes ($app, $course_routes) {
222222
$global_settings->get('/:setting_id')->to('Settings#getGlobalSetting');
223223
$global_settings->post('/check-timezone')->to('Settings#checkTimeZone');
224224
$course_routes->get('/settings')->to('Settings#getCourseSettings');
225+
$course_routes->get('/settings/:setting_id')->to('Settings#getCourseSetting');
225226
$course_routes->put('/settings/:setting_id')->to('Settings#updateCourseSetting');
226227
$course_routes->delete('/settings/:setting_id')->to('Settings#deleteCourseSetting');
227228
return;

lib/WeBWorK3/Controller/Settings.pm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ sub getCourseSettings ($c) {
4242
return;
4343
}
4444

45+
sub getCourseSetting ($c) {
46+
my $course_setting = $c->schema->resultset('Course')->getCourseSetting(
47+
info => {
48+
course_id => int($c->param('course_id')),
49+
setting_id => int($c->param('setting_id'))
50+
}
51+
);
52+
$c->render(json => $course_setting);
53+
return;
54+
}
55+
4556
sub updateCourseSetting ($c) {
4657
my $course_setting = $c->schema->resultset('Course')->updateCourseSetting(
4758
info => {

lib/WeBWorK3/Utils/Settings.pm

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ our @EXPORT_OK = qw/isValidSetting mergeCourseSettings isInteger isTimeString is
1313

1414
use Exception::Class qw/
1515
DB::Exception::UndefinedCourseField
16-
DB::Exception::InvalidCourseField
16+
DB::Exception::RequiredCourseField
1717
DB::Exception::InvalidCourseFieldType
1818
/;
1919

@@ -45,8 +45,6 @@ This checks if the setting given the type, value and list of options (if needed)
4545
4646
=over
4747
48-
=item Ensure that all fields passed in are valid
49-
5048
=item Ensure that all require fields are present
5149
5250
=item Checks that the default value is appropriate for the type
@@ -61,18 +59,11 @@ sub isValidSetting ($setting, $value = undef) {
6159

6260
# If $value is not passed in, use the default_value for the setting
6361
my $val = $value // $setting->{default_value};
64-
# Check that each of the setting fields is allowed.
65-
for my $field (keys %$setting) {
66-
my @fields = grep { $_ eq $field } @allowed_fields;
67-
DB::Exception::InvalidCourseField->throw(
68-
message => "The field: $field is not an allowed field of the setting $setting->{setting_name}")
69-
if scalar(@fields) == 0;
70-
}
7162

7263
# Check that each of the required fields is present in the setting.
7364
for my $field (@required_fields) {
7465
my @fields = grep { $_ eq $field } (keys %$setting);
75-
DB::Exception::InvalidCourseField->throw(
66+
DB::Exception::RequiredCourseField->throw(
7667
message => "The field: $field is a required field for the setting $setting->{setting_name}")
7768
if scalar(@fields) == 0;
7869
}

src/stores/settings.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,16 @@ export const useSettingsStore = defineStore('settings', {
9898
/**
9999
* Deletes the course setting from both the store and sends a delete request to the database.
100100
*/
101-
async deleteCourseSetting(course_setting: CourseSetting): Promise<CourseSetting> {
101+
async deleteCourseSetting(course_setting: CourseSetting): Promise<void> {
102102
const session = useSessionStore();
103103
const course_id = session.course.course_id;
104104

105105
const i = this.db_course_settings.findIndex(setting => setting.setting_id == course_setting.setting_id);
106106
if (i < 0) {
107107
throw `The setting with name: '${course_setting.setting_name}' has not been defined for this course.`;
108108
}
109-
const response = await api.delete(`/courses/${course_id}/settings/${course_setting.setting_id}`);
109+
await api.delete(`/courses/${course_id}/settings/${course_setting.setting_id}`);
110110
this.db_course_settings.splice(i, 1);
111-
const deleted_setting = new DBCourseSetting(response.data as ParseableDBCourseSetting);
112-
return new CourseSetting(Object.assign(deleted_setting.toObject(), course_setting.toObject()));
113111
},
114112
/**
115113
* Used to clear out all of the settings. Useful when logging out.

0 commit comments

Comments
 (0)