Skip to content

Commit

Permalink
feat(#493): Enable tags again
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesvedder committed Jul 30, 2023
1 parent 052517f commit 505d66f
Show file tree
Hide file tree
Showing 24 changed files with 1,124 additions and 4 deletions.
2 changes: 2 additions & 0 deletions core/lib/src/models/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export 'tables/repo.dart';
export 'tables/study.dart';
export 'tables/study_invite.dart';
export 'tables/study_subject.dart';
export 'tables/study_tag.dart';
export 'tables/subject_progress.dart';
export 'tables/tag.dart';
export 'tables/user.dart';
export 'tasks/tasks.dart';
15 changes: 14 additions & 1 deletion core/lib/src/models/tables/study.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class Study extends SupabaseObjectFunctions<Study> {
@JsonKey(name: 'registry_published')
late bool registryPublished = false;

@JsonKey(includeToJson: false, includeFromJson: false)
List<StudyTag> studyTags = [];
@JsonKey(includeToJson: false, includeFromJson: false)
int participantCount = 0;
@JsonKey(includeToJson: false, includeFromJson: false)
Expand Down Expand Up @@ -99,6 +101,17 @@ class Study extends SupabaseObjectFunctions<Study> {

factory Study.fromJson(Map<String, dynamic> json) {
final study = _$StudyFromJson(json);
final List? studyTags = json['study_tags'] as List?;
if (studyTags != null) {
study.studyTags = studyTags
.map(
(json) => StudyTag.fromTag(
studyId: study.id,
tag: Tag.fromJson(json as Map<String, dynamic>),
),
)
.toList();
}

final List? repo = json['repo'] as List?;
if (repo != null && repo.isNotEmpty) {
Expand Down Expand Up @@ -244,6 +257,6 @@ class Study extends SupabaseObjectFunctions<Study> {

@override
String toString() {
return 'Study{id: $id, title: $title, description: $description, userId: $userId, participation: $participation, resultSharing: $resultSharing, contact: $contact, iconName: $iconName, published: $published, questionnaire: $questionnaire, eligibilityCriteria: $eligibilityCriteria, consent: $consent, interventions: $interventions, observations: $observations, schedule: $schedule, reportSpecification: $reportSpecification, results: $results, collaboratorEmails: $collaboratorEmails, registryPublished: $registryPublished, participantCount: $participantCount, endedCount: $endedCount, activeSubjectCount: $activeSubjectCount, missedDays: $missedDays, repo: $repo, invites: $invites, participants: $participants, participantsProgress: $participantsProgress, createdAt: $createdAt}';
return 'Study{id: $id, title: $title, description: $description, studyTags: $studyTags, userId: $userId, participation: $participation, resultSharing: $resultSharing, contact: $contact, iconName: $iconName, published: $published, questionnaire: $questionnaire, eligibilityCriteria: $eligibilityCriteria, consent: $consent, interventions: $interventions, observations: $observations, schedule: $schedule, reportSpecification: $reportSpecification, results: $results, collaboratorEmails: $collaboratorEmails, registryPublished: $registryPublished, participantCount: $participantCount, endedCount: $endedCount, activeSubjectCount: $activeSubjectCount, missedDays: $missedDays, repo: $repo, invites: $invites, participants: $participants, participantsProgress: $participantsProgress, createdAt: $createdAt}';
}
}
70 changes: 70 additions & 0 deletions core/lib/src/models/tables/study_tag.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:studyu_core/core.dart';

part 'study_tag.g.dart';

@JsonSerializable()
class StudyTag extends SupabaseObjectFunctions<StudyTag> {
static const String tableName = 'study_tag';

@override
Map<String, dynamic> get primaryKeys => {'tag_id': tagId, 'study_id': studyId};

@JsonKey(name: 'study_id')
final String studyId;
@JsonKey(name: 'tag_id')
final String tagId;

@JsonKey(includeToJson: false, includeFromJson: false)
late Tag tag;

/*@JsonKey(includeToJson: false, includeFromJson: false)
late Study study;*/

StudyTag({
required this.studyId,
required this.tagId,
});

StudyTag.fromTag({
required this.tag,
required this.studyId,
}) : tagId = tag.id;

factory StudyTag.fromJson(Map<String, dynamic> json) {
final studyTag = _$StudyTagFromJson(json);

/*final Map<String, dynamic>? study = json['study'] as Map<String, dynamic>?;
if (study != null) {
studyTag.study = Study.fromJson(study);
}*/

final Map<String, dynamic>? tag = json['tag'] as Map<String, dynamic>?;
if (tag != null) {
studyTag.tag = Tag.fromJson(tag);
}

return studyTag;
}

String get name => tag.name;

String get id => tag.id;

@override
Map<String, dynamic> toJson() => _$StudyTagToJson(this);

@override
String toString() => toJson().toString();

@override
bool operator ==(Object other) =>
identical(this, other) || other is StudyTag && studyId == other.studyId && tag == other.tag;

@override
int get hashCode => id.hashCode ^ name.hashCode ^ studyId.hashCode ^ tag.hashCode;
}

extension StudyTagListToTagList on List<StudyTag> {
List<Tag> toTagList() => map((studyTag) => studyTag.tag).toList();
}
17 changes: 17 additions & 0 deletions core/lib/src/models/tables/study_tag.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions core/lib/src/models/tables/tag.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:json_annotation/json_annotation.dart';

import 'package:studyu_core/core.dart';
import 'package:studyu_core/src/util/supabase_object.dart';

part 'tag.g.dart';

@JsonSerializable()
class Tag extends SupabaseObjectFunctions<Tag> {
static const String tableName = 'tag';

@override
Map<String, dynamic> get primaryKeys => {'id': id};

@JsonKey(name: 'id')
String id;
@JsonKey(name: 'name')
String name;

Tag({required this.id, required this.name});

factory Tag.fromJson(Map<String, dynamic> json) => _$TagFromJson(json);

@override
Map<String, dynamic> toJson() => _$TagToJson(this);

@override
bool operator ==(Object other) =>
identical(this, other) || other is Tag && id == other.id && name == other.name;

@override
int get hashCode => id.hashCode ^ name.hashCode;
}
17 changes: 17 additions & 0 deletions core/lib/src/models/tables/tag.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions core/lib/src/util/supabase_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ String tableName(Type cls) {
return StudyInvite.tableName;
case StudyUUser:
return StudyUUser.tableName;
case StudyTag:
return StudyTag.tableName;
case Tag:
return Tag.tableName;
default:
print('$cls is not a supported Supabase type');
throw TypeError();
Expand All @@ -49,6 +53,10 @@ abstract class SupabaseObjectFunctions<T extends SupabaseObject> implements Supa
return StudyInvite.fromJson(json) as T;
case StudyUUser:
return StudyUUser.fromJson(json) as T;
case StudyTag:
return StudyTag.fromJson(json) as T;
case Tag:
return Tag.fromJson(json) as T;
default:
print('$T is not a supported Supabase type');
throw TypeError();
Expand Down
129 changes: 129 additions & 0 deletions database/migrate_tags.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
--
-- Name: tag; Type: TABLE; Schema: public; Owner: supabase_admin
--

CREATE TABLE public.tag (
id uuid DEFAULT gen_random_uuid() NOT NULL,
name text NOT NULL,
color text,
parent_id uuid
-- rename result_sharing to visibility
--visibility public.result_sharing NOT NULL DEFAULT 'private'::public.result_sharing,
);

ALTER TABLE public.tag OWNER TO supabase_admin;


--
-- Name: tag tag_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin
--

ALTER TABLE ONLY public.tag
ADD CONSTRAINT tag_pkey PRIMARY KEY (id);


--
-- Name: study_tag; Type: TABLE; Schema: public; Owner: supabase_admin
--

CREATE TABLE public.study_tag (
study_id uuid NOT NULL,
tag_id uuid NOT NULL
);

ALTER TABLE public.study_tag OWNER TO supabase_admin;


--
-- Name: study_tag study_tag_pkey; Type: CONSTRAINT; Schema: public; Owner: supabase_admin
--

ALTER TABLE ONLY public.study_tag
ADD CONSTRAINT "study_tag_pkey" PRIMARY KEY (study_id, tag_id);


--
-- Name: tag tag_parentId_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin
--

ALTER TABLE ONLY public.tag
ADD CONSTRAINT "tag_parentId_fkey" FOREIGN KEY (parent_id) REFERENCES public.tag(id) ON DELETE CASCADE;


--
-- Name: tag study_tag_studyId_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin
--

ALTER TABLE ONLY public.study_tag
ADD CONSTRAINT "study_tag_studyId_fkey" FOREIGN KEY (study_id) REFERENCES public.study(id) ON DELETE CASCADE;


--
-- Name: tag study_tag_tagId_fkey; Type: FK CONSTRAINT; Schema: public; Owner: supabase_admin
--

ALTER TABLE ONLY public.study_tag
ADD CONSTRAINT "study_tag_tagId_fkey" FOREIGN KEY (tag_id) REFERENCES public.tag(id) ON DELETE CASCADE;


-- TODO VERIFY all policies regarding anonymous select, update, insert, delete and authenticated behavior regarding auth.uid()

--
-- Name: study_tag Allow read access but deny write access for tags; Type: POLICY; Schema: public; Owner: supabase_admin
--

CREATE POLICY "Allow read access, deny write access"
ON public.tag
FOR SELECT
USING (true);


--
-- Name: Allow study creators to manage tags; Type: POLICY; Schema: public; Owner: supabase_admin
--

CREATE POLICY "Allow study creators to manage tags"
ON public.study_tag
FOR ALL
USING (
EXISTS (
SELECT 1
FROM study
WHERE study.id = study_tag.study_id
AND study.user_id = auth.uid()
)
);


--
-- Name: Allow subscribed users to select study tags; Type: POLICY; Schema: public; Owner: supabase_admin
--

CREATE POLICY "Allow subscribed users to select study tags"
ON public.study_tag
FOR SELECT
USING (
EXISTS (
SELECT 1
FROM public.study_subject
WHERE study_subject.study_id = study_tag.study_id
AND study_subject.user_id = auth.uid()
)
);


-- todo deny insert, delete, update for everyone else
-- todo deny select for everyone except study creators and users subscribed to the study


--
-- Name: tag; Type: ROW SECURITY; Schema: public; Owner: supabase_admin
--

ALTER TABLE public.tag ENABLE ROW LEVEL SECURITY;

--
-- Name: study_tag; Type: ROW SECURITY; Schema: public; Owner: supabase_admin
--

ALTER TABLE public.study_tag ENABLE ROW LEVEL SECURITY;
Loading

0 comments on commit 505d66f

Please sign in to comment.