diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f11b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/api/delete/delete_foos.yaml b/api/delete/delete_foos.yaml deleted file mode 100644 index b28dd6f..0000000 --- a/api/delete/delete_foos.yaml +++ /dev/null @@ -1,77 +0,0 @@ ---- -what: - deletes foos by id xor (color and/or shape) - -DELETE: /foos?{id}{color}{shape} - -params: - declarations: - id: Int range (1,4000000) - color: UTF8 length (1,16) - shape: ASCII regex ^(circle|square|[a-z]+gon)$ # circle, square, hexagon, pentagon,... - combinations: - (id |xor| (color |or| shape)) # id xor (color or shape) must be set - -# see `user_roles.yaml` -# if this is absent, full access is silently granted. -# since the special `resource_owner` role is specified, we need an `resource_ownership_test` -# to determine if the user is indeed an owner. -access_control: [store_owner, admin, resource_owner] - -# Testing for resource_ownership. Default is false -# -# general switch statement, can embed all top-level declarations that will "percolate up", -# including nested `switch` statements -switch: - - - case: (headers.User_Id |and| id) - resource_ownership_test: - query: # returning a row signifies success; empty result-set is failure - select * from foo where id={id} and foo_owner_id={headers.User_Id} - - - case: (headers.User_Id |and| (color |or| shape)) - resource_ownership_test: - query: |- - select * from foo - where - foo_owner_id={headers.User_Id} and - [(color) color={color}] - [(shape) and shape={shape}]; - - - # default case has no `case` clause - resource_ownership_test: - expression: (F) # always false - -switch: - - - case: (id) # if `id` exists - query: - delete from foo where id={id}; - - - case: (color |or| shape) - query: |- - delete from foo - where - [(color) color={color}] - [(shape) and shape={shape}]; - -# Record and test the expected behavior of the query, if you'd like - tests: - - - it: should properly set the resource ownership test - when: - headers: - User_Id: 123abc - params: - id: doggo123 - then: - endpoint_meta: - resource_ownership_test: - query: |- - select * from foo where id={id} and foo_owner_id={headers.User_Id} - - - - - - \ No newline at end of file diff --git a/api/get/foo_by_id.dh b/api/get/foo_by_id.dh deleted file mode 100644 index 7d8f9c1..0000000 --- a/api/get/foo_by_id.dh +++ /dev/null @@ -1,27 +0,0 @@ -ENDPOINT selects a foo by id -GET /foos/{id} -PARAMS - id uint64 -RETURNING SINGLE FROM - select top 1 from foos where id={id}; - -ENSURING -> it shouldn't find results - given - truncate foos; - when - id = 1 - then - code 404 - -> it should find a record - given - truncate foos; - insert into foo (id, name) values (1, 'blah'), (2, 'yolo'); - when - id = 1 - then - code 200 - results - id name - 1 blah \ No newline at end of file diff --git a/api/get/search_foos.yaml b/api/get/search_foos.yaml deleted file mode 100644 index 88a808d..0000000 --- a/api/get/search_foos.yaml +++ /dev/null @@ -1,25 +0,0 @@ ---- -what: - Search foos by parameters - -GET: /foos?{color}{shape} -RESULT: PAGINATED_LIST # the PAGINATED_LIST listens for custom headers for: - # page-number, per-page, sort-by-field, sort-order - -params: - declarations: - color: UTF8 length (1,16) - shape: ASCII regex ^(circle|square|[a-z]+gon)$ # circle, square, hexagon, pentagon,... - combinations: # boolean expression that specifies valid parameter combinations - (color) # here, `color` is mandatory, while `shape` isn't - -# a paginated list query provides (and necessitates) the use of: -# {page_offset}, {per_page}, {sort_field}, {sort_order} -query: |- - select top({per_page}) from foo - where color={color}[(shape) and shape={shape}] # will emit `expr` if `shape` is set - offset {page_offset} - sort by {sort_field} {sort_order}; - -# Tests go here, if you'd like -#tests: \ No newline at end of file diff --git a/api/pet/post.dh b/api/pet/post.dh new file mode 100644 index 0000000..610734d --- /dev/null +++ b/api/pet/post.dh @@ -0,0 +1,30 @@ +# +# - experimental: recursive POST params +# + +ENDPOINT + Adds a new pet to the store + +POST /pet +BODY + id uint32 + category { + id uint32 + name string length[1,32] + } + name string length[1,32] + photo_urls [url] + tags { + id uint32 + name string length[1,32] + } + status regex ^(available|pending|sold)$ + +# The framework returns the result of the last insert statement, in this case our 'pet' +# All inserts are part of the same transaction; boundaries can default to the whole of a 'label' +FIRST + upsert into category (id, name) values ({category.id}, {category.name}); + upsert into tags (id, name) values ({tags.id}, {tags.name}); + +THEN RETURN RESULT FROM + insert into pets (id, name, photos_urls, status) values ({id}, {name}, {photosUrls}, {status}); diff --git a/api/pet_entity.dh b/api/pet_entity.dh new file mode 100644 index 0000000..7a7b165 --- /dev/null +++ b/api/pet_entity.dh @@ -0,0 +1,33 @@ +################################################### +# Describes a CRUD Entity mapped to a table +# Generates the following endpoints: +# - GET /pet/{primary key} +# - GET LIST /pet?{filter/search params}{pagination params} +# - POST /pet (create) +# - PUT /pet (upsert) +# - PATCH /pet (update) +# - DELETE /pet +# +# Caching (if "ON") uses "pets" as the cache key +# and caches results to /pets/{id}, +# invalidating the cache on PUT and DELETE to that {id} + +CRUD ENTITY /pets +MAPS TO TABLE pets +FIELDS + id uint32 pk + category uint32 fk(categories(id)) + name string size [1,32] + photoUrls [url] mapsToField(photo_urls) optional + tags [string] size [1,32] fk(tags) optional + status string regex ^(available|pending|sold)$ + +CACHING IS ON + +AUTHORIZING + GET BY ID to * + GET SEARCH to * + PUT to store_employee + POST to store_employee + PATCH to store_employee + DELETE to store_owner \ No newline at end of file diff --git a/api/store/get_inventory.dh b/api/store/get_inventory.dh new file mode 100644 index 0000000..5c30292 --- /dev/null +++ b/api/store/get_inventory.dh @@ -0,0 +1,26 @@ +ENDPOINT gets the aggregated inventory +GET LIST /store/inventory +FROM + select status, count(status) as pets from pets group by status; + +ENSURING + WITH CLEANUP + truncate pets; + + it should not find results when table is empty + then + code 404 + + it should return expected results + given + insert into pets (name, status) values ('pet1', 'pending'); + insert into pets (name, status) values ('pet2', 'available'); + insert into pets (name, status) values ('pet3', 'sold'); + insert into pets (name, status) values ('pet4', 'pending'); + then + code 200 + result + status pets + pending 2 + available 1 + sold 1 \ No newline at end of file diff --git a/database/migrations/1_initialize.dh b/database/migrations/1_initialize.dh deleted file mode 100644 index a68cde2..0000000 --- a/database/migrations/1_initialize.dh +++ /dev/null @@ -1,71 +0,0 @@ -MIGRATION(1) initialize the database -UP - CREATE TABLE pet_types ( - id int auto_increment, - name varchar(32) not null - ); - - CREATE TABLE pets ( - id int auto_increment, - pet_type int fk(pet_types(id)) not null, - date_of_birth datetime, - name varchar(32), - listed_price float(2), # with decimal precision of 2 - date_sold datetime - ); - - create function func1 (input) - function body - returns output - end func1; - - create procedure proc1 (input) - procedure body - change state in tables - no return - end proc1; - -DOWN - drop table pets; - drop table pet_types; - drop function func1; - drop procedure proc1; - -# for testing functions/procedures, but also constraints, and anything else -ENSURING -> it should test the stored proc - given - truncate foo; - truncate bar; - insert (a, b, c) into foo; - insert (a, b, c) into bar; - - # call the tested procedure and return a data-set - when - call proc1(x); - select * from foo; - # validate the returned tab-delimited, tabulated data set - then - Col1 Col2 Col3 - A' B' c - A2' B2' c - -> it shold test the stored function - # stateless functions may not have a `given` block - # OTOH, if `func1` relied on data, it should have been prepped - when - select func1('y') as Res - then - Res - Y' - -> it should test unique constraint foo(a) - given - truncate foo; - insert (a, b, c) into foo; - when - insert (a, d, e) into foo; - select * from foo; - then - COL1 COL2 COL3 - a b c \ No newline at end of file diff --git a/migrations/1_initialize.dh b/migrations/1_initialize.dh new file mode 100644 index 0000000..26ac3e2 --- /dev/null +++ b/migrations/1_initialize.dh @@ -0,0 +1,36 @@ +MIGRATION initialize the database +UP + CREATE TABLE categories ( + id serial PRIMARY KEY, + name varchar(32) not null + ); + + CREATE TYPE pet_status AS ENUM ('available', 'sold', 'pending'); + + CREATE TABLE pets ( + id serial PRIMARY KEY, + category integer REFERENCES categories (id) NOT NULL, + name varchar(32) NOT NULL, + tags varchar(32)[] NOT NULL, + photo_urls varchar(256)[] NOT NULL, + status pet_status NOT NULL + ); + + CREATE TYPE order_status AS ENUM ('placed', 'approved', 'delivered'); + + CREATE TABLE orders ( + id serial PRIMARY KEY, + pet_id integer REFERENCES pets (id) NOT NULL, + quantity integer NOT NULL, + ship_date timestamp with time zone NOT NULL, + status order_status NOT NULL, + complete boolean NOT NULL + ); + +DOWN + drop table categories; + drop table orders; + drop table pets; + drop type pet_status; + drop type order_status; + \ No newline at end of file diff --git a/migrations/2_something_else.dh b/migrations/2_something_else.dh new file mode 100644 index 0000000..69c344f --- /dev/null +++ b/migrations/2_something_else.dh @@ -0,0 +1,20 @@ +MIGRATION setting up some tables +UP + CREATE TABLE categories ( + id serial PRIMARY KEY, + name varchar(32) not null + ); +DOWN + drop table categories; + +ENSURING + it should test our clever assumptions + given + this thing right there + and that one, too + when + i do this really clever thing + then + col1 col2 `col 3` + 1 `2 3` 4 + a b c \ No newline at end of file