| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- using Invercargill;
- using Invercargill.DataStructures;
- using Invercargill.Expressions;
- using InvercargillSql.Orm;
- using Inversion;
- using ClankerSaddle.Data;
- namespace ClankerSaddle.Services {
- /**
- * Capability storage: CRUD over `capabilities` plus their saddle and
- * task attachments, and the tag-matching predicate the dispatcher
- * routes by — a saddle is eligible for a task when its capability
- * set is a superset of the task's required set (an empty task set
- * matches any saddle).
- */
- public class CapabilityService : Object {
- private OrmSession db = inject<OrmSession>();
- /**
- * Inserts a capability; names are unique. Returns the stored row
- * (with its id).
- */
- public async Capability create_capability(string name) throws Error {
- if (name.strip().length == 0) {
- throw new Error(Quark.from_string("clanker-saddle"), 40,
- "A capability name is required");
- }
- var existing = yield db.query<Capability>()
- .where(expr("name == $0", elem(name.strip())))
- .first_async();
- if (existing != null) {
- throw new Error(Quark.from_string("clanker-saddle"), 41,
- @"A capability named '$(name)' already exists");
- }
- var capability = new Capability() {
- name = name.strip(),
- created_at = new DateTime.now_local()
- };
- yield db.insert_async<Capability>(capability);
- return capability;
- }
- /** Loads the capability identified by `id`, or null. */
- public async Capability? get_capability(int64 id) throws Error {
- return yield db.query<Capability>()
- .where(expr("id == $0", elem<int64?>(id)))
- .first_async();
- }
- /**
- * The capability named exactly `name`, or null — the subtask
- * callback's tag validation lookup (names are unique).
- */
- public async Capability? find_by_name(string name) throws Error {
- return yield db.query<Capability>()
- .where(expr("name == $0", elem(name)))
- .first_async();
- }
- /** Every capability ordered by name. */
- public async ImmutableLot<Capability> list_capabilities() throws Error {
- return yield db.query<Capability>()
- .order_by(expr("name"))
- .materialise_async();
- }
- /**
- * Deletes a capability and every attachment referencing it (a
- * saddle stops carrying it, a task stops requiring it).
- */
- public async void delete_capability(int64 id) throws Error {
- foreach (var link in yield db.query<TaskCapability>()
- .where(expr("capability_id == $0", elem<int64?>(id)))
- .materialise_async()) {
- yield db.delete_async<TaskCapability>(link);
- }
- foreach (var link in yield db.query<SaddleCapability>()
- .where(expr("capability_id == $0", elem<int64?>(id)))
- .materialise_async()) {
- yield db.delete_async<SaddleCapability>(link);
- }
- var capability = yield get_capability(id);
- if (capability != null) {
- yield db.delete_async<Capability>((!)capability);
- }
- }
- /**
- * Attaches `capability_id` to `saddle_id`; duplicates are a no-op
- * (the pair is unique).
- */
- public async void attach_to_saddle(int64 capability_id, int64 saddle_id) throws Error {
- var capability = yield get_capability(capability_id);
- if (capability == null) {
- throw new Error(Quark.from_string("clanker-saddle"), 42,
- @"No capability exists with id $(capability_id)");
- }
- var existing = yield db.query<SaddleCapability>()
- .where(expr("capability_id == $0 && saddle_id == $1",
- elem<int64?>(capability_id), elem<int64?>(saddle_id)))
- .materialise_async();
- if (existing.length > 0) {
- return;
- }
- yield db.insert_async<SaddleCapability>(new SaddleCapability() {
- capability_id = capability_id,
- saddle_id = saddle_id
- });
- }
- /** Detaches the `saddle_capabilities` row identified by `id`. */
- public async void detach_from_saddle(int64 link_id) throws Error {
- var link = yield db.query<SaddleCapability>()
- .where(expr("id == $0", elem<int64?>(link_id)))
- .first_async();
- if (link != null) {
- yield db.delete_async<SaddleCapability>((!)link);
- }
- }
- /** The `saddle_capabilities` attachments of `saddle_id`, oldest first. */
- public async ImmutableLot<SaddleCapability> list_saddle_attachments(int64 saddle_id) throws Error {
- return yield db.query<SaddleCapability>()
- .where(expr("saddle_id == $0", elem<int64?>(saddle_id)))
- .order_by(expr("id"))
- .materialise_async();
- }
- /** The capabilities a saddle carries, attachment order. */
- public async ImmutableLot<Capability> list_for_saddle(int64 saddle_id) throws Error {
- var rows = new Vector<Capability>();
- foreach (var link in yield list_saddle_attachments(saddle_id)) {
- var capability = yield get_capability(link.capability_id);
- if (capability != null) {
- rows.add((!)capability);
- }
- }
- return rows.to_immutable_buffer();
- }
- /**
- * Attaches `capability_id` to `task_id` (a required capability);
- * duplicates are a no-op (the pair is unique).
- */
- public async void attach_to_task(int64 capability_id, int64 task_id) throws Error {
- var capability = yield get_capability(capability_id);
- if (capability == null) {
- throw new Error(Quark.from_string("clanker-saddle"), 42,
- @"No capability exists with id $(capability_id)");
- }
- var existing = yield db.query<TaskCapability>()
- .where(expr("capability_id == $0 && task_id == $1",
- elem<int64?>(capability_id), elem<int64?>(task_id)))
- .materialise_async();
- if (existing.length > 0) {
- return;
- }
- yield db.insert_async<TaskCapability>(new TaskCapability() {
- capability_id = capability_id,
- task_id = task_id
- });
- }
- /** Detaches the `task_capabilities` row identified by `id`. */
- public async void detach_from_task(int64 link_id) throws Error {
- var link = yield db.query<TaskCapability>()
- .where(expr("id == $0", elem<int64?>(link_id)))
- .first_async();
- if (link != null) {
- yield db.delete_async<TaskCapability>((!)link);
- }
- }
- /** The `task_capabilities` attachments of `task_id`, oldest first. */
- public async ImmutableLot<TaskCapability> list_task_attachments(int64 task_id) throws Error {
- return yield db.query<TaskCapability>()
- .where(expr("task_id == $0", elem<int64?>(task_id)))
- .order_by(expr("id"))
- .materialise_async();
- }
- /** The capabilities a task requires, attachment order. */
- public async ImmutableLot<Capability> list_for_task(int64 task_id) throws Error {
- var rows = new Vector<Capability>();
- foreach (var link in yield list_task_attachments(task_id)) {
- var capability = yield get_capability(link.capability_id);
- if (capability != null) {
- rows.add((!)capability);
- }
- }
- return rows.to_immutable_buffer();
- }
- /** Every `saddle_capabilities` row (for dispatch-time batching). */
- public async ImmutableLot<SaddleCapability> all_saddle_attachments() throws Error {
- return yield db.query<SaddleCapability>().materialise_async();
- }
- /** Every `task_capabilities` row (for usage counts and routing). */
- public async ImmutableLot<TaskCapability> all_task_attachments() throws Error {
- return yield db.query<TaskCapability>().materialise_async();
- }
- /**
- * True when `saddle_id`'s capability set is a superset of
- * `task_id`'s required set — the dispatcher's eligibility check.
- */
- public async bool saddle_covers_task(int64 saddle_id, int64 task_id) throws Error {
- var required = new HashSet<string>();
- foreach (var capability in yield list_for_task(task_id)) {
- required.add(capability.name);
- }
- if (required.length == 0) {
- return true;
- }
- foreach (var capability in yield list_for_saddle(saddle_id)) {
- required.remove(capability.name);
- }
- return required.length == 0;
- }
- }
- }
|