@pgpm/achievements 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +22 -0
  2. package/Makefile +6 -0
  3. package/README.md +5 -0
  4. package/__tests__/__snapshots__/achievements.test.ts.snap +180 -0
  5. package/__tests__/__snapshots__/triggers.test.ts.snap +112 -0
  6. package/__tests__/achievements.test.ts +250 -0
  7. package/__tests__/triggers.test.ts +167 -0
  8. package/deploy/schemas/status_private/procedures/status_triggers.sql +148 -0
  9. package/deploy/schemas/status_private/procedures/upsert_achievement.sql +24 -0
  10. package/deploy/schemas/status_private/procedures/user_completed_step.sql +16 -0
  11. package/deploy/schemas/status_private/procedures/user_incompleted_step.sql +22 -0
  12. package/deploy/schemas/status_private/schema.sql +15 -0
  13. package/deploy/schemas/status_public/procedures/steps_required.sql +59 -0
  14. package/deploy/schemas/status_public/procedures/user_achieved.sql +29 -0
  15. package/deploy/schemas/status_public/schema.sql +15 -0
  16. package/deploy/schemas/status_public/tables/level_requirements/table.sql +21 -0
  17. package/deploy/schemas/status_public/tables/levels/table.sql +15 -0
  18. package/deploy/schemas/status_public/tables/user_achievements/policies/enable_row_level_security.sql +11 -0
  19. package/deploy/schemas/status_public/tables/user_achievements/policies/user_achievements_policy.sql +38 -0
  20. package/deploy/schemas/status_public/tables/user_achievements/table.sql +20 -0
  21. package/deploy/schemas/status_public/tables/user_levels/table.sql +20 -0
  22. package/deploy/schemas/status_public/tables/user_steps/table.sql +18 -0
  23. package/deploy/schemas/status_public/tables/user_steps/triggers/update_achievements_tg.sql +25 -0
  24. package/jest.config.js +15 -0
  25. package/launchql-achievements.control +8 -0
  26. package/launchql.plan +20 -0
  27. package/package.json +29 -0
  28. package/revert/schemas/status_private/procedures/status_triggers.sql +10 -0
  29. package/revert/schemas/status_private/procedures/upsert_achievement.sql +7 -0
  30. package/revert/schemas/status_private/procedures/user_completed_step.sql +7 -0
  31. package/revert/schemas/status_private/procedures/user_incompleted_step.sql +7 -0
  32. package/revert/schemas/status_private/schema.sql +7 -0
  33. package/revert/schemas/status_public/procedures/steps_required.sql +7 -0
  34. package/revert/schemas/status_public/procedures/user_achieved.sql +7 -0
  35. package/revert/schemas/status_public/schema.sql +7 -0
  36. package/revert/schemas/status_public/tables/level_requirements/table.sql +7 -0
  37. package/revert/schemas/status_public/tables/levels/table.sql +7 -0
  38. package/revert/schemas/status_public/tables/user_achievements/policies/enable_row_level_security.sql +8 -0
  39. package/revert/schemas/status_public/tables/user_achievements/policies/user_achievements_policy.sql +18 -0
  40. package/revert/schemas/status_public/tables/user_achievements/table.sql +7 -0
  41. package/revert/schemas/status_public/tables/user_levels/table.sql +7 -0
  42. package/revert/schemas/status_public/tables/user_steps/table.sql +7 -0
  43. package/revert/schemas/status_public/tables/user_steps/triggers/update_achievements_tg.sql +8 -0
  44. package/sqitch.plan +20 -0
  45. package/sql/launchql-achievements--0.4.6.sql +264 -0
  46. package/verify/schemas/status_private/procedures/status_triggers.sql +10 -0
  47. package/verify/schemas/status_private/procedures/upsert_achievement.sql +7 -0
  48. package/verify/schemas/status_private/procedures/user_completed_step.sql +7 -0
  49. package/verify/schemas/status_private/procedures/user_incompleted_step.sql +7 -0
  50. package/verify/schemas/status_private/schema.sql +7 -0
  51. package/verify/schemas/status_public/procedures/steps_required.sql +7 -0
  52. package/verify/schemas/status_public/procedures/user_achieved.sql +7 -0
  53. package/verify/schemas/status_public/schema.sql +7 -0
  54. package/verify/schemas/status_public/tables/level_requirements/table.sql +7 -0
  55. package/verify/schemas/status_public/tables/levels/table.sql +7 -0
  56. package/verify/schemas/status_public/tables/user_achievements/policies/enable_row_level_security.sql +7 -0
  57. package/verify/schemas/status_public/tables/user_achievements/policies/user_achievements_policy.sql +15 -0
  58. package/verify/schemas/status_public/tables/user_achievements/table.sql +7 -0
  59. package/verify/schemas/status_public/tables/user_levels/table.sql +7 -0
  60. package/verify/schemas/status_public/tables/user_steps/table.sql +7 -0
  61. package/verify/schemas/status_public/tables/user_steps/triggers/update_achievements_tg.sql +8 -0
package/jest.config.js ADDED
@@ -0,0 +1,15 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+
6
+ // Match both __tests__ and colocated test files
7
+ testMatch: ['**/?(*.)+(test|spec).{ts,tsx,js,jsx}'],
8
+
9
+ // Ignore build artifacts and type declarations
10
+ testPathIgnorePatterns: ['/dist/', '\\.d\\.ts$'],
11
+ modulePathIgnorePatterns: ['<rootDir>/dist/'],
12
+ watchPathIgnorePatterns: ['/dist/'],
13
+
14
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
15
+ };
@@ -0,0 +1,8 @@
1
+ # launchql-achievements extension
2
+ comment = 'launchql-achievements extension'
3
+ default_version = '0.4.6'
4
+ module_pathname = '$libdir/launchql-achievements'
5
+ requires = 'plpgsql,uuid-ossp,launchql-jwt-claims,launchql-verify'
6
+ relocatable = false
7
+ superuser = false
8
+
package/launchql.plan ADDED
@@ -0,0 +1,20 @@
1
+ %syntax-version=1.0.0
2
+ %project=launchql-achievements
3
+ %uri=launchql-achievements
4
+
5
+ schemas/status_private/schema [launchql-jwt-claims:schemas/jwt_public/procedures/current_user_id] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/schema
6
+ schemas/status_public/schema 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/schema
7
+ schemas/status_public/tables/user_steps/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_steps/table
8
+ schemas/status_private/procedures/user_completed_step [schemas/status_private/schema schemas/status_public/tables/user_steps/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/user_completed_step
9
+ schemas/status_private/procedures/user_incompleted_step [schemas/status_private/schema schemas/status_public/tables/user_steps/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/user_incompleted_step
10
+ schemas/status_private/procedures/status_triggers [schemas/status_private/schema schemas/status_private/procedures/user_completed_step schemas/status_private/procedures/user_incompleted_step] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/status_triggers
11
+ schemas/status_public/tables/user_achievements/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_achievements/table
12
+ schemas/status_private/procedures/upsert_achievement [schemas/status_private/schema schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/upsert_achievement
13
+ schemas/status_public/tables/levels/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/levels/table
14
+ schemas/status_public/tables/level_requirements/table [schemas/status_public/schema schemas/status_public/tables/levels/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/level_requirements/table
15
+ schemas/status_public/procedures/steps_required [schemas/status_public/schema schemas/status_public/tables/level_requirements/table schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/procedures/steps_required
16
+ schemas/status_public/procedures/user_achieved [schemas/status_public/schema schemas/status_public/procedures/steps_required schemas/status_public/tables/level_requirements/table schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/procedures/user_achieved
17
+ schemas/status_public/tables/user_achievements/policies/enable_row_level_security [schemas/status_public/schema schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_achievements/policies/enable_row_level_security
18
+ schemas/status_public/tables/user_achievements/policies/user_achievements_policy [schemas/status_public/schema schemas/status_public/tables/user_achievements/table schemas/status_public/tables/user_achievements/policies/enable_row_level_security] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_achievements/policies/user_achievements_policy
19
+ schemas/status_public/tables/user_levels/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_levels/table
20
+ schemas/status_public/tables/user_steps/triggers/update_achievements_tg [schemas/status_public/schema schemas/status_public/tables/user_steps/table schemas/status_public/tables/user_achievements/table schemas/status_private/procedures/upsert_achievement] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_steps/triggers/update_achievements_tg
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@pgpm/achievements",
3
+ "version": "0.4.0",
4
+ "description": "Achievement system for tracking user progress and milestones",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "scripts": {
9
+ "bundle": "lql package",
10
+ "test": "jest",
11
+ "test:watch": "jest --watch"
12
+ },
13
+ "dependencies": {
14
+ "@pgpm/jwt-claims": "0.4.0",
15
+ "@pgpm/verify": "0.4.0"
16
+ },
17
+ "devDependencies": {
18
+ "@launchql/cli": "^4.9.0"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/launchql/extensions"
23
+ },
24
+ "homepage": "https://github.com/launchql/extensions",
25
+ "bugs": {
26
+ "url": "https://github.com/launchql/extensions/issues"
27
+ },
28
+ "gitHead": "cc9f52a335caa6e21ee7751b04b77c84ce6cb809"
29
+ }
@@ -0,0 +1,10 @@
1
+ -- Revert schemas/status_private/procedures/status_triggers from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION status_private.tg_achievement;
6
+ DROP FUNCTION status_private.tg_achievement_toggle;
7
+ DROP FUNCTION status_private.tg_achievement_boolean;
8
+ DROP FUNCTION status_private.tg_achievement_toggle_boolean;
9
+
10
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_private/procedures/upsert_achievement from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION status_private.upsert_achievement;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_private/procedures/user_completed_step from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION status_private.user_completed_step;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_private/procedures/user_incompleted_step from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION status_private.user_incompleted_step;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_private/schema from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP SCHEMA status_private;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/procedures/steps_required from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION status_public.steps_required;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/procedures/user_achieved from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP FUNCTION status_public.user_achieved;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/schema from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP SCHEMA status_public;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/tables/level_requirements/table from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP TABLE status_public.level_requirements;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/tables/levels/table from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP TABLE status_public.levels;
6
+
7
+ COMMIT;
@@ -0,0 +1,8 @@
1
+ -- Revert schemas/status_public/tables/user_achievements/policies/enable_row_level_security from pg
2
+
3
+ BEGIN;
4
+
5
+ ALTER TABLE status_public.user_achievements
6
+ DISABLE ROW LEVEL SECURITY;
7
+
8
+ COMMIT;
@@ -0,0 +1,18 @@
1
+ -- Revert schemas/status_public/tables/user_achievements/policies/user_achievements_policy from pg
2
+
3
+ BEGIN;
4
+
5
+
6
+ REVOKE INSERT ON TABLE status_public.user_achievements FROM authenticated;
7
+ REVOKE SELECT ON TABLE status_public.user_achievements FROM authenticated;
8
+ REVOKE UPDATE ON TABLE status_public.user_achievements FROM authenticated;
9
+ REVOKE DELETE ON TABLE status_public.user_achievements FROM authenticated;
10
+
11
+
12
+ DROP POLICY can_select_user_achievements ON status_public.user_achievements;
13
+ DROP POLICY can_insert_user_achievements ON status_public.user_achievements;
14
+ DROP POLICY can_update_user_achievements ON status_public.user_achievements;
15
+ DROP POLICY can_delete_user_achievements ON status_public.user_achievements;
16
+
17
+
18
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/tables/user_achievements/table from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP TABLE status_public.user_achievements;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/tables/user_levels/table from pg
2
+
3
+ BEGIN;
4
+
5
+ -- DROP TABLE status_public.user_levels;
6
+
7
+ COMMIT;
@@ -0,0 +1,7 @@
1
+ -- Revert schemas/status_public/tables/user_steps/table from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP TABLE status_public.user_steps;
6
+
7
+ COMMIT;
@@ -0,0 +1,8 @@
1
+ -- Revert schemas/status_public/tables/user_steps/triggers/update_achievements_tg from pg
2
+
3
+ BEGIN;
4
+
5
+ DROP TRIGGER update_achievements_tg ON status_public.user_steps;
6
+ DROP FUNCTION status_private.tg_update_achievements_tg;
7
+
8
+ COMMIT;
package/sqitch.plan ADDED
@@ -0,0 +1,20 @@
1
+ %syntax-version=1.0.0
2
+ %project=launchql-achievements
3
+ %uri=launchql-achievements
4
+
5
+ schemas/status_private/schema [launchql-jwt-claims:schemas/jwt_public/procedures/current_user_id] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/schema
6
+ schemas/status_public/schema 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/schema
7
+ schemas/status_public/tables/user_steps/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_steps/table
8
+ schemas/status_private/procedures/user_completed_step [schemas/status_private/schema schemas/status_public/tables/user_steps/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/user_completed_step
9
+ schemas/status_private/procedures/user_incompleted_step [schemas/status_private/schema schemas/status_public/tables/user_steps/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/user_incompleted_step
10
+ schemas/status_private/procedures/status_triggers [schemas/status_private/schema schemas/status_private/procedures/user_completed_step schemas/status_private/procedures/user_incompleted_step] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/status_triggers
11
+ schemas/status_public/tables/user_achievements/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_achievements/table
12
+ schemas/status_private/procedures/upsert_achievement [schemas/status_private/schema schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_private/procedures/upsert_achievement
13
+ schemas/status_public/tables/levels/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/levels/table
14
+ schemas/status_public/tables/level_requirements/table [schemas/status_public/schema schemas/status_public/tables/levels/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/level_requirements/table
15
+ schemas/status_public/procedures/steps_required [schemas/status_public/schema schemas/status_public/tables/level_requirements/table schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/procedures/steps_required
16
+ schemas/status_public/procedures/user_achieved [schemas/status_public/schema schemas/status_public/procedures/steps_required schemas/status_public/tables/level_requirements/table schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/procedures/user_achieved
17
+ schemas/status_public/tables/user_achievements/policies/enable_row_level_security [schemas/status_public/schema schemas/status_public/tables/user_achievements/table] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_achievements/policies/enable_row_level_security
18
+ schemas/status_public/tables/user_achievements/policies/user_achievements_policy [schemas/status_public/schema schemas/status_public/tables/user_achievements/table schemas/status_public/tables/user_achievements/policies/enable_row_level_security] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_achievements/policies/user_achievements_policy
19
+ schemas/status_public/tables/user_levels/table [schemas/status_public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_levels/table
20
+ schemas/status_public/tables/user_steps/triggers/update_achievements_tg [schemas/status_public/schema schemas/status_public/tables/user_steps/table schemas/status_public/tables/user_achievements/table schemas/status_private/procedures/upsert_achievement] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/status_public/tables/user_steps/triggers/update_achievements_tg
@@ -0,0 +1,264 @@
1
+ \echo Use "CREATE EXTENSION launchql-achievements" to load this file. \quit
2
+ CREATE SCHEMA IF NOT EXISTS status_private;
3
+
4
+ GRANT USAGE ON SCHEMA status_private TO authenticated, anonymous;
5
+
6
+ ALTER DEFAULT PRIVILEGES IN SCHEMA status_private
7
+ GRANT EXECUTE ON FUNCTIONS TO authenticated;
8
+
9
+ CREATE SCHEMA IF NOT EXISTS status_public;
10
+
11
+ GRANT USAGE ON SCHEMA status_public TO authenticated, anonymous;
12
+
13
+ ALTER DEFAULT PRIVILEGES IN SCHEMA status_public
14
+ GRANT EXECUTE ON FUNCTIONS TO authenticated;
15
+
16
+ CREATE TABLE status_public.user_steps (
17
+ id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
18
+ user_id uuid NOT NULL,
19
+ name text NOT NULL,
20
+ count int NOT NULL DEFAULT 1,
21
+ created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
22
+ );
23
+
24
+ COMMENT ON TABLE status_public.user_steps IS 'The user achieving a requirement for a level. Log table that has every single step ever taken.';
25
+
26
+ CREATE INDEX ON status_public.user_steps (user_id, name);
27
+
28
+ CREATE FUNCTION status_private.user_completed_step(step text, user_id uuid DEFAULT jwt_public.current_user_id()) RETURNS void AS $EOFCODE$
29
+ INSERT INTO status_public.user_steps ( name, user_id, count )
30
+ VALUES ( step, user_id, 1 );
31
+ $EOFCODE$ LANGUAGE sql VOLATILE SECURITY DEFINER;
32
+
33
+ CREATE FUNCTION status_private.user_incompleted_step(step text, user_id uuid DEFAULT jwt_public.current_user_id()) RETURNS void AS $EOFCODE$
34
+ BEGIN
35
+ DELETE FROM status_public.user_steps s
36
+ WHERE s.user_id = user_incompleted_step.user_id
37
+ AND s.name = step;
38
+ DELETE FROM status_public.user_achievements a
39
+ WHERE a.user_id = user_incompleted_step.user_id
40
+ AND a.name = step;
41
+ END;
42
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
43
+
44
+ CREATE FUNCTION status_private.tg_achievement() RETURNS trigger AS $EOFCODE$
45
+ DECLARE
46
+ is_null boolean;
47
+ task_name text;
48
+ BEGIN
49
+ IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
50
+ task_name = TG_ARGV[1]::text;
51
+ EXECUTE format('SELECT ($1).%s IS NULL', TG_ARGV[0])
52
+ USING NEW INTO is_null;
53
+ IF (is_null IS FALSE) THEN
54
+ PERFORM status_private.user_completed_step(task_name);
55
+ END IF;
56
+ RETURN NEW;
57
+ END IF;
58
+ END;
59
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
60
+
61
+ CREATE FUNCTION status_private.tg_achievement_toggle() RETURNS trigger AS $EOFCODE$
62
+ DECLARE
63
+ is_null boolean;
64
+ task_name text;
65
+ BEGIN
66
+ IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
67
+ task_name = TG_ARGV[1]::text;
68
+ EXECUTE format('SELECT ($1).%s IS NULL', TG_ARGV[0])
69
+ USING NEW INTO is_null;
70
+ IF (is_null IS TRUE) THEN
71
+ PERFORM status_private.user_incompleted_step(task_name);
72
+ ELSE
73
+ PERFORM status_private.user_completed_step(task_name);
74
+ END IF;
75
+ RETURN NEW;
76
+ END IF;
77
+ END;
78
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
79
+
80
+ CREATE FUNCTION status_private.tg_achievement_boolean() RETURNS trigger AS $EOFCODE$
81
+ DECLARE
82
+ is_true boolean;
83
+ task_name text;
84
+ BEGIN
85
+ IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
86
+ task_name = TG_ARGV[1]::text;
87
+ EXECUTE format('SELECT ($1).%s IS TRUE', TG_ARGV[0])
88
+ USING NEW INTO is_true;
89
+ IF (is_true IS TRUE) THEN
90
+ PERFORM status_private.user_completed_step(task_name);
91
+ END IF;
92
+ RETURN NEW;
93
+ END IF;
94
+ END;
95
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
96
+
97
+ CREATE FUNCTION status_private.tg_achievement_toggle_boolean() RETURNS trigger AS $EOFCODE$
98
+ DECLARE
99
+ is_true boolean;
100
+ task_name text;
101
+ BEGIN
102
+ IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
103
+ task_name = TG_ARGV[1]::text;
104
+ EXECUTE format('SELECT ($1).%s IS TRUE', TG_ARGV[0])
105
+ USING NEW INTO is_true;
106
+ IF (is_true IS TRUE) THEN
107
+ PERFORM status_private.user_completed_step(task_name);
108
+ ELSE
109
+ PERFORM status_private.user_incompleted_step(task_name);
110
+ END IF;
111
+ RETURN NEW;
112
+ END IF;
113
+ END;
114
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
115
+
116
+ CREATE TABLE status_public.user_achievements (
117
+ id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
118
+ user_id uuid NOT NULL,
119
+ name text NOT NULL,
120
+ count int NOT NULL DEFAULT 0,
121
+ created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
122
+ CONSTRAINT user_achievements_unique_key
123
+ UNIQUE (user_id, name)
124
+ );
125
+
126
+ COMMENT ON TABLE status_public.user_achievements IS 'This table represents the users progress for particular level requirements, tallying the total count. This table is updated via triggers and should not be updated maually.';
127
+
128
+ CREATE INDEX ON status_public.user_achievements (user_id, name);
129
+
130
+ CREATE FUNCTION status_private.upsert_achievement(vuser_id uuid, vname text, vcount int) RETURNS void AS $EOFCODE$
131
+ BEGIN
132
+ INSERT INTO status_public.user_achievements (user_id, name, count)
133
+ VALUES
134
+ (vuser_id, vname, GREATEST(vcount, 0))
135
+ ON CONFLICT ON CONSTRAINT user_achievements_unique_key
136
+ DO UPDATE SET
137
+ -- look ma! you can actually do aliases inside on conflict
138
+ count = user_achievements.count + EXCLUDED.count
139
+ ;
140
+ END;
141
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE;
142
+
143
+ CREATE TABLE status_public.levels (
144
+ name text NOT NULL PRIMARY KEY
145
+ );
146
+
147
+ COMMENT ON TABLE status_public.levels IS 'Levels for achievement';
148
+
149
+ GRANT SELECT ON status_public.levels TO PUBLIC;
150
+
151
+ CREATE TABLE status_public.level_requirements (
152
+ id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
153
+ name text NOT NULL,
154
+ level text NOT NULL,
155
+ required_count int DEFAULT 1,
156
+ priority int DEFAULT 100,
157
+ UNIQUE (name, level)
158
+ );
159
+
160
+ COMMENT ON TABLE status_public.level_requirements IS 'Requirements to achieve a level';
161
+
162
+ CREATE INDEX ON status_public.level_requirements (name, level, priority);
163
+
164
+ GRANT SELECT ON status_public.levels TO authenticated;
165
+
166
+ CREATE FUNCTION status_public.steps_required(vlevel text, vrole_id uuid DEFAULT jwt_public.current_user_id()) RETURNS SETOF status_public.level_requirements AS $EOFCODE$
167
+ BEGIN
168
+ RETURN QUERY
169
+ SELECT
170
+ level_requirements.id,
171
+ level_requirements.name,
172
+ level_requirements.level,
173
+ -1*(coalesce(user_achievements.count,0)-level_requirements.required_count) as required_count,
174
+ level_requirements.priority
175
+ FROM
176
+ status_public.level_requirements
177
+ FULL OUTER JOIN status_public.user_achievements ON (
178
+ user_achievements.name = level_requirements.name
179
+ AND user_achievements.user_id =vrole_id
180
+ )
181
+ JOIN status_public.levels ON (level_requirements.level = levels.name)
182
+ WHERE
183
+ level_requirements.level = vlevel
184
+ AND -1*(coalesce(user_achievements.count,0)-level_requirements.required_count) > 0
185
+ ORDER BY priority ASC
186
+ ;
187
+ END;
188
+ $EOFCODE$ LANGUAGE plpgsql STABLE;
189
+
190
+ CREATE FUNCTION status_public.user_achieved(vlevel text, vrole_id uuid DEFAULT jwt_public.current_user_id()) RETURNS boolean AS $EOFCODE$
191
+ DECLARE
192
+ c int;
193
+ BEGIN
194
+ SELECT COUNT(*) FROM
195
+ status_public.steps_required(
196
+ vlevel,
197
+ vrole_id
198
+ )
199
+ INTO c;
200
+
201
+ RETURN c <= 0;
202
+ END;
203
+ $EOFCODE$ LANGUAGE plpgsql STABLE;
204
+
205
+ ALTER TABLE status_public.user_achievements
206
+ ENABLE ROW LEVEL SECURITY;
207
+
208
+ CREATE POLICY can_select_user_achievements
209
+ ON status_public.user_achievements
210
+ AS PERMISSIVE
211
+ FOR SELECT
212
+ TO PUBLIC
213
+ USING (
214
+ jwt_public.current_user_id() = user_id
215
+ );
216
+
217
+ CREATE POLICY can_insert_user_achievements
218
+ ON status_public.user_achievements
219
+ AS PERMISSIVE
220
+ FOR INSERT
221
+ TO PUBLIC
222
+ WITH CHECK (
223
+ false
224
+ );
225
+
226
+ CREATE POLICY can_update_user_achievements
227
+ ON status_public.user_achievements
228
+ AS PERMISSIVE
229
+ FOR UPDATE
230
+ TO PUBLIC
231
+ USING (
232
+ false
233
+ );
234
+
235
+ CREATE POLICY can_delete_user_achievements
236
+ ON status_public.user_achievements
237
+ AS PERMISSIVE
238
+ FOR DELETE
239
+ TO PUBLIC
240
+ USING (
241
+ false
242
+ );
243
+
244
+ GRANT INSERT ON status_public.user_achievements TO authenticated;
245
+
246
+ GRANT SELECT ON status_public.user_achievements TO authenticated;
247
+
248
+ GRANT UPDATE ON status_public.user_achievements TO authenticated;
249
+
250
+ GRANT DELETE ON status_public.user_achievements TO authenticated;
251
+
252
+ CREATE FUNCTION status_private.tg_update_achievements_tg() RETURNS trigger AS $EOFCODE$
253
+ DECLARE
254
+ BEGIN
255
+ PERFORM status_private.upsert_achievement(NEW.user_id, NEW.name, NEW.count);
256
+ RETURN NEW;
257
+ END;
258
+ $EOFCODE$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
259
+
260
+ CREATE TRIGGER update_achievements_tg
261
+ AFTER INSERT
262
+ ON status_public.user_steps
263
+ FOR EACH ROW
264
+ EXECUTE PROCEDURE status_private.tg_update_achievements_tg();
@@ -0,0 +1,10 @@
1
+ -- Verify schemas/status_private/procedures/status_triggers on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('status_private.tg_achievement');
6
+ SELECT verify_function ('status_private.tg_achievement_toggle');
7
+ SELECT verify_function ('status_private.tg_achievement_boolean');
8
+ SELECT verify_function ('status_private.tg_achievement_toggle_boolean');
9
+
10
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_private/procedures/upsert_achievement on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('status_private.upsert_achievement');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_private/procedures/user_completed_step on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('status_private.user_completed_step');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_private/procedures/user_incompleted_step on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('status_private.user_incompleted_step');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_private/schema on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_schema ('status_private');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_public/procedures/steps_required on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('status_public.steps_required');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_public/procedures/user_achieved on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_function ('status_public.user_achieved');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_public/schema on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_schema ('status_public');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_public/tables/level_requirements/table on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_table ('status_public.level_requirements');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_public/tables/levels/table on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_table ('status_public.levels');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_public/tables/user_achievements/policies/enable_row_level_security on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_security ('status_public.user_achievements');
6
+
7
+ ROLLBACK;
@@ -0,0 +1,15 @@
1
+ -- Verify schemas/status_public/tables/user_achievements/policies/user_achievements_policy on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_policy ('can_select_user_achievements', 'status_public.user_achievements');
6
+ SELECT verify_policy ('can_insert_user_achievements', 'status_public.user_achievements');
7
+ SELECT verify_policy ('can_update_user_achievements', 'status_public.user_achievements');
8
+ SELECT verify_policy ('can_delete_user_achievements', 'status_public.user_achievements');
9
+
10
+ SELECT has_table_privilege('authenticated', 'status_public.user_achievements', 'INSERT');
11
+ SELECT has_table_privilege('authenticated', 'status_public.user_achievements', 'SELECT');
12
+ SELECT has_table_privilege('authenticated', 'status_public.user_achievements', 'UPDATE');
13
+ SELECT has_table_privilege('authenticated', 'status_public.user_achievements', 'DELETE');
14
+
15
+ ROLLBACK;
@@ -0,0 +1,7 @@
1
+ -- Verify schemas/status_public/tables/user_achievements/table on pg
2
+
3
+ BEGIN;
4
+
5
+ SELECT verify_table ('status_public.user_achievements');
6
+
7
+ ROLLBACK;