ActionRegistry.vala 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. using Invercargill.DataStructures;
  2. using InvercargillJson;
  3. using Inversion;
  4. namespace Statum {
  5. /**
  6. * Resolves Statum actions to/from their auto-assigned GUID endpoints; the
  7. * design follows the old Spry 0.1 `PathProvider`, removed in Spry 0.2.
  8. *
  9. * Every action registered via {@link StatumConfigurator.action} is assigned a
  10. * random GUID and served at `/_statum/action/{guid}` by a single
  11. * {@link ActionEndpoint}. The same GUID (looked up by type) is used when an
  12. * {@link Statum.Model.ActionDto} reference is authored for embedding in state.
  13. *
  14. * GUIDs are random per process start; this is fine because action references
  15. * are re-embedded into state on every entrypoint load, so a stale GUID never
  16. * survives a page render.
  17. */
  18. public class ActionRegistry : Object {
  19. private Cryptography.EncryptionProvider encryption_provider = inject<Cryptography.EncryptionProvider>();
  20. private Dictionary<string, Type> guid_to_type = new Dictionary<string, Type>();
  21. private Dictionary<string, string> type_to_guid = new Dictionary<string, string>();
  22. /** Registers an action type and assigns it a GUID (idempotent). */
  23. public void register<TAction>() {
  24. register_type(typeof(TAction));
  25. }
  26. /** Registers an action type and assigns it a GUID (idempotent). */
  27. public void register_type(Type type) {
  28. var name = type.name();
  29. if (type_to_guid.has(name)) {
  30. return;
  31. }
  32. var guid = Uuid.string_random();
  33. guid_to_type[guid] = type;
  34. type_to_guid[name] = guid;
  35. }
  36. /** The GUID assigned to `TAction` (registering it first if needed). */
  37. public string guid_for<TAction>() {
  38. return guid_for_type(typeof(TAction));
  39. }
  40. /** The GUID assigned to `type` (registering it first if needed). */
  41. public string guid_for_type(Type type) {
  42. var name = type.name();
  43. string guid;
  44. if (type_to_guid.try_get(name, out guid)) {
  45. return guid;
  46. }
  47. register_type(type);
  48. type_to_guid.try_get(name, out guid);
  49. return guid;
  50. }
  51. /** Resolves a GUID back to its action type for dispatch, or `null`. */
  52. public Type? resolve(string guid) {
  53. Type type;
  54. if (guid_to_type.try_get(guid, out type)) {
  55. return type;
  56. }
  57. return null;
  58. }
  59. /** The seal namespace for `TAction`'s private data (its type name). */
  60. public string namespace_for<TAction>() {
  61. return typeof(TAction).name();
  62. }
  63. /** The seal namespace for `type`'s private data (its type name). */
  64. public string namespace_for_type(Type type) {
  65. return type.name();
  66. }
  67. /**
  68. * Authors an {@link Model.ActionDto} reference for `TAction` (no private
  69. * data) that the client can invoke via `stm-action`.
  70. */
  71. public Model.ActionDto author<TAction>(string method = "POST") {
  72. return new Model.ActionDto() {
  73. uri = @"/_statum/action/$(guid_for<TAction>())",
  74. method = method
  75. };
  76. }
  77. /**
  78. * Authors an {@link Model.ActionDto} reference for `TAction` carrying
  79. * `model` as sealed private data. The blob is sealed under
  80. * {@link namespace_for}'s namespace, which the matching
  81. * `TypedStatumAction<TPrivate>` handler decrypts with its per-type
  82. * namespace.
  83. *
  84. * (Named distinctly from {@link author} because Vala does not permit
  85. * methods that overload on generic arity.)
  86. */
  87. public Model.ActionDto author_private<TAction, TPrivate>(TPrivate model, string method = "POST") throws GLib.Error {
  88. var blob = encryption_provider.author_properties(
  89. namespace_for<TAction>(),
  90. GObjectMapping.to_properties((Object) model));
  91. return new Model.ActionDto() {
  92. uri = @"/_statum/action/$(guid_for<TAction>())",
  93. method = method,
  94. @private = blob
  95. };
  96. }
  97. }
  98. }