diff --git a/docs/api/API Overview.md b/docs/api/API Overview.md deleted file mode 100644 index 6384e0d..0000000 --- a/docs/api/API Overview.md +++ /dev/null @@ -1,177 +0,0 @@ -# *(draft)* API Overview -Using Flask, Flask Restful and Flask JWT (correct?) The api works as a direct connection to the storage for the items stored by the Qcli, allowing front end calls to request parsed information via url. - -Endpoints | Use -:---: | :--- -[**/api/login**](#login) | Receives a JSON encoded username and password. Sets a Refresh token cookie and returns an access token. -[**/api/tokens/refresh**](#tokensrefresh) | Gets a new refresh token. -[**/api/data/<string:queue>/<int:number>**](#acessing-items) | Returns a JSON encoded Item. -[**/api/data/<string:queues>**](#accessing-queues) | Returns a JSON encoded Queue. -[**/api/data/get_queues**](#accessing-queue-counts) | Returns a list of queues and their item counts. - -## login - -#### **Description** -Acts as a way to authenticate users to access data parsed in the back end. - -#### **Arguments** -The api login accpets a username and an already hashed password. -```json -{ - "username": form.username, - "password": sha256(form.password) -} -``` -The api accepts the hashed password and salts and hashes it an additional time and attempts to authenticate the username and the password using *insert part of flask being used here*. - -#### **Returns** -After sucesfully authenticating the username and password, the api will return an access token to then be stored in a browser cookie on the frontend -```json -{ "access_token": access_token } -``` - -#### **Errors** - -| Error | Reason | -| ---- | ---- | -| **422** | Returns if data is not json or if the json data is not formatted correctly | -| **401** | Returns if the username and/or password is invalid | - -## tokens/refresh - -#### **Description** - -Acts as a revalidation of an already authenticated user and supplies a newly created access token. - -## Acessing Items - -#### **Description** - -Items are individual "tickets", stored in a collective queue with other items. Item numbers are unique only within each queue, meaning that to access a specific item, its queue name must also be known in addition to it's item number. `ce100`, `me23`, and `eee48` would all be specific items that can all be accessed. - -#### **Arguments** - -**/api/data/*<string:queue>*/*<int:number>*** - -Argument | Type | Description -:---- | :----: | :---- -`queue` | String | The queue an item is located in -`item` | Int | The item's distinct number within the specified queue -`headersOnly` *(Optional)* | Boolean | Specify weather to only returns the headers of an item or the whole item - -An item can be accessed directly from the url by passing the queue name and the item number. -By default, the entire contents of an item are returned, but the headers of an item, without any of the content within that item, can also be returned by passing the `headersOnly=True` argument within the url: - -`/api/data/ce/100` will default to `headersOnly=False` and return the entirety of `ce 100` - -`/api/data/ce/100?headersOnly=True` will return only the headers of `ce 100` - -#### **Returns** - -When `headersOnly=False`, the full contents of an item will be returned in a json structure with these keys: - -Key | Value -------|------ -`lastUpdated` | An ISO 8601 formatted time string showing the last time the file was updated. -`headers` | A list of dictionaries containing header keys and values. -`content` | A list of section dictionaries. -`isLocked` | A boolean showing whether or not a lockfile for the item is present. -`userEmail` | The email address of the person who this item is from. -`userName` | The real name of the person who this item is from. -`userAlias` | The Purdue career account alias of the person this item is from. -`assignedTo` | The Purdue career account alias of the person this item is assigned to -`subject` | The subject of the original message for this item. -`status` | The most recent status update for the item. -`priority` | The most recent priority for this item. -`department` | The most recent department for this item. -`dateReceived` | The date this item was created. -`jsonData` | A JSON serializable representation of the Item. - -If `headersOnly=True`, then all of the key/value pairs are returned except for the `content` key and the list of dictionaries associated with it. - -## Accessing Queues - -#### **Description** - -Additionally, the information relating to all items in a queue can be accesed by simply naming the queue in the URL. Unlike acessing a specific item, acessing a queue only returns the header information for each item to decrease the time it takes to parse the information from the stored items. - -#### **Arguments** - -Accessing a queue is similar to accessing an individual item except that multiple item dictionaries are returned within a list as opposed to just one item in a singular dictionary. - -**/api/data/*<string:queues>*** - -| Argument | Type | Description | -| ---- | :----: | :---- | -| `queue` | String | The name of a queue | -| `headersOnly` *(Optional)* | Boolean | Specify weather to only returns the headers of an item or the whole item | - -By default, acessing an entire queue will return only the header information for the items within a given queue, however, it is possible to return the entire contents of all the items within a queue by passing `headersOnly=False` within the url: - -| URL | Return Description | -| --- | --- | -| `/api/data/me` | Defaults to `headersOnly=True` and returns a list of all items in the `me` queue with only the header information included. | -| `/api/data/ce?headersOnly=False` | Returns a list of all the items in the `me` queue with the entire contents of every item included. | - -It is also possible to return multiple queues by passing more than one queue name in the url. separating each queue name with "+": - -| URL | Return Description | -| --- | --- | -| `/api/data/eee+cnit` | Defaults to `headersOnly=True` and returns a list of all the items in both the `eee` and `cnit` queues with only the header information included. | -| `/api/data/eee+cnit?headersOnly=False` | Returns a list of all the items in both the `eee` and `cnit` queues with the entire contents of every item from both queues included. | - -#### **Returns** - -Returns a list of queues, with each queue being it's own dictionary with a list of items, each item represented by a dictionary. - -**JSON output:** -```json -[ - {'name': 'queue_name', - 'items': ['list_of_item_dictionaries] - }, - - {'name': 'queue_name', - 'items': ['list_of_item_dictionaries] - } -] -``` -## Accessing Queue Counts - -#### **Description** - -This will return a dictionary of all valid queues with the number of items within each queue. - -#### **Arguments** - -This function doesn't require any arguments from the url. - -#### **Returns** - -Returns a list of dictionaries with these key value pairs: -**/api/data/get_queues** - -| Key | Type | Value | -| --- | ---- | ----- | -| `name` | string | the name of the queue | -| `number_of_items` | int | the number of items within the queue | - -**JSON output:** -```json -[ - { - 'name': 'bidc', - 'number_of_items': 5 - }, - - { - 'name': 'epics', - 'number_of_items': 6 - }, - - { - 'name': 'wang', - 'number_of_items': 13 - } -] -``` \ No newline at end of file diff --git a/docs/api/Authentication.md b/docs/api/Authentication.md index 1cedd3e..8d1b613 100644 --- a/docs/api/Authentication.md +++ b/docs/api/Authentication.md @@ -2,6 +2,7 @@ The webqueue2 API uses a two stage authentication system combining Active Directory and [HTTP Token](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) (or "bearer") authentication. +## Getting an Access Token All API calls require an access token. You can get an access token by making a POST request to the `/api/login` endpoint containing the JSON encoded username and password of a valid user. ??? info "Who is a valid user?" @@ -11,66 +12,68 @@ All API calls require an access token. You can get an access token by making a P - `00000227-ECNStuds` - `00000227-ECN-webqueue-misc` -=== "fetch" - ```javascript - const accessToken = (async _ => { - const loginInit = { - method: "POST", - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({ "username": USERNAME, "password": PASSWORD}) - }; +### Example +!!! example "Get an access token." + + ```js + {% include 'api/get_access_token.js' %} + ``` + ```js + // Expected Output + "{{ example_access_token }}" + ``` - let loginResponse = await fetch("API_URL/api/login", loginInit); - let data = await loginResponse.json(); +## Making Calls With Access Token +To interact with the API, add an `Authorization` header to your request with a value of `Bearer TOKEN` where `TOKEN` is your access token. - if (data === null){ - return false; - } +### Example: +!!! example "Get item CE 100." + ```js + let access_token = "{{ example_access_token }}"; + let queue = "ce"; + let item_number = 100; - if (!loginResponse.ok){ - console.error(`Login failed. Got code ${loginResponse.status} (${loginResponse.statusText})`); - return false; - } - - return data.access_token || false; - })(); + fetch( + `https://engineering.purdue.edu/webqueue/webqueue2/build/api/data/${queue}/${item_number}`, + { headers: {"Authorization":`Bearer ${access_token}` }} + ) + .then( resp => resp.json() ) + .then( data => console.log( data )); ``` - -=== "cURL" - !!! warning - It is **strongly** recomended to store and retrieve your login credentials in a [cURL config file](https://everything.curl.dev/cmdline/configfile) that only your user can read to avoid exposing your credentials to your shell history or the host process table. - - Create a cURL config file named `wq2_login.config`: - ```bash - nano ~/wq_login.config + ```js + // Expected Output + {queue: "ce", number: 100, lastUpdated: "2021-03-11T07:24:00-0500" ... } ``` - Add the cURL options: - ```python - # ~/wq_login.config - request = POST - header = "Content-Type: application/json" - data = {"username":"USERNAME","password":"PASSWORD"} - url = "API_URL/api/login" - ``` +## Refreshing Access Tokens +When you login, you'll receive an access token that expires 15 minutes after creation as well as two cookies needed to get a new access token. Those cookies are: - Restrict read permissions to your user: - ```bash - chmod 700 ~/wq_login.config - ``` - - Run cURL using your new config file: - ```bash - curl -K ~/wq_login.config - ``` - ``` - # Expected Output: - {"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTYxODI1MjMsIm5iZiI6MTYxNjE4MjUyMywianRpIjoiYzI2ZjY4NDctZGU2OC00ODUzLWI4ODctMTBmZDAyMDcyY2U0IiwiZXhwIjoxNjE2MTgzNDIzLCJzdWIiOiJjYW1wYjMwMyIsImZyZXNoIjpmYWxzZSwidHlwZSI6ImFjY2VzcyIsImNzcmYiOiJjYjYzZWE1My1jOWQ2LTQ5YTItYmZhMi0wY2U4N2Q3YzcxZDcifQ.T7bezsOMreMCXWR0R5w5BKI673hpOquCOnvT1XkyDjY"} - ``` +Name | Value | Path | Expiration | SameSite +-- | -- | -- | -- | -- +`refresh_token_cookie` | Your refresh token. | `/api/tokens/refresh` | 30 Days | Yes +`csrf_refresh_token` | Additional verification data. (e.g. `{{ example_csrf_token }}`) | `/` | Session | Yes + +The `refresh_token_cookie` is used to generate a new access token and will be sent back to the server with every request automatically. It expires 30 days after login. The `csrf_refresh_token` is used to verify the `refresh_token_cookie` and needs sent back as an `X-CSRF-TOKEN` header. - ??? tip "Tip for Parsing Tokens in Bash" - You can parse the access token using Python like this: - ```bash - curl -K ~/wq_login.config | \ - python3 -c "import sys, json; print(json.load(sys.stdin)['access_token'])" - ``` \ No newline at end of file +To refresh your access token, make a POST request to the `/api/tokens/refresh` endpoint with the value of the `csrf_refresh_token` cookies inside a `X-CSRF-TOKEN` header: + +### Example +!!! example "Get a new refresh token." + ```js + // Get this value from your cookies. + const csrf_refresh_token = "{{ example_csrf_token }}" + + fetch( + `{{ production_url }}/api/tokens/refresh`, + { + method: "POST", + headers: {'X-CSRF-TOKEN': csrf_refresh_token} + } + ) + .then( resp => resp.json() ) + .then( data => console.log( data.access_token )); + ``` + ```js + // Expected Output + {{ example_access_token }} + ``` \ No newline at end of file diff --git a/docs/api/Getting Started.md b/docs/api/Getting Started.md index e6b5fea..128d990 100644 --- a/docs/api/Getting Started.md +++ b/docs/api/Getting Started.md @@ -2,26 +2,41 @@ The webqueue2 API is organized around [REST](https://en.wikipedia.org/wiki/Representational_state_transfer). The API has resource oriented URLs, accepts and returns JSON encoded request bodies, can be modified via the query string and uses standard HTTP response codes and verbs. +You can use the webqueue2 API hosted at [{{ production_url }}/api]({{ production_url }}/api) or [install it on your own machine](Installation.md). + ## Basic Usage -!!! example "Get the number of Items in a Queue" - === "fetch" - ```javascript - ()(); - ``` +!!! example "Get the first item in CE queue." + ```javascript + let access_token = "{{ example_access_token }}"; + let queue = "ce"; - === "cURL" - ```bash - curl -K - ``` + fetch( + `https://engineering.purdue.edu/webqueue/webqueue2/build/api/data/${queue}`, + { headers: {"Authorization":`Bearer ${access_token}` }} + ) + .then( resp => resp.json() ) + .then( data => console.log( data[0].items[0] )); + ``` + ```js + // Expected Output + { queue: "ce", number: 17, lastUpdated: "2021-03-29T17:12:00-0400" ... } + ``` -!!! example "Get the subject of an Item" - === "fetch" - ```javascript - console.log - ``` +!!! example "Get the subject of an CE 1." + ```javascript + let access_token = "{{ example_access_token }}"; + let queue = "ce"; + let item_number = 1; - === "cURL" - ```bash - curl -K - ``` \ No newline at end of file + fetch( + `https://engineering.purdue.edu/webqueue/webqueue2/build/api/data/${queue}/${item_number}`, + { headers: {"Authorization":`Bearer ${access_token}` }} + ) + .then( resp => resp.json() ) + .then( data => console.log( data.subject )); + ``` + ```js + // Expected Output + "Linux Server Purchase" + ``` \ No newline at end of file diff --git a/docs/api/Installation.md b/docs/api/Installation.md index 6552a27..f04d7ed 100644 --- a/docs/api/Installation.md +++ b/docs/api/Installation.md @@ -1,57 +1,31 @@ # Installation -The api is hosted as its own package in a dedicated git repository, https://github.itap.purdue.edu/ECN/webqueue2-api, and is easily installable via `pip`. - -### Install via command line -This is the syntax for installing the api: -``` -pip install git+https://github.itap.purdue.edu/ECN/webqueue2-api@#egg=webqueue2-api -``` -For example, to install version `0.9.1`, replace `` in the above command like this: -``` -pip install git+https://github.itap.purdue.edu/ECN/webqueue2-api@0.9.1#egg=webqueue2-api -``` - -### Install via requirements.txt -Append this line in the requirements file: -``` -git+https://github.itap.purdue.edu/ECN/webqueue2-api@#egg=webqueue2-api -``` -For example, to install version `0.9.1`, replace `` in the above line above like this: -``` -git+https://github.itap.purdue.edu/ECN/webqueue2-api@0.9.1#egg=webqueue2-api -``` -### Package Structure -The name of the package itself is `webqueue2-api` and follows this structure: -``` -─webqueue2-api (git repo) -│ -├───__init__.py -├───api.py -├───ECNQueue.py -└───setup.py -``` -**ECNQueue.py:** -The parser that iterates through items and resturns a json formatted string. - -**api.py:** -The bridge between the fronted and the parser: the frontend sends requests to `api.py` which are translated into functions present in the `ECNQueue.py`, and the json returned by `ECNQueue.py` is thus returned to the frontend. - -**setup.py:** -Stores important information about the `webqueue2-api` package including: - -* The version -* Dependencies that are installed with `webqueue2-api` -* The name of the package (`webqueue2-api`) -* The python version requirement, which is anything including and beyond python version 3.6 - -**\_\_init\_\_.py** -Specifies to `pip` that the current directory is a package, doesn't contain any code, but any code contained within `__init__.py` is executed when ever the `webqueue2-api` package is imported. - -### Importing the package - -The package cannot be referenced directly (need to check on this), however, both the `api` and `ECNQueue` modules can be directly imported after successfully installing the webqueue2-api package: -```python -import ECNQueue -import api -``` \ No newline at end of file +The webqueue2 API is publicly available at [{{ production_url }}/api]({{ production_url }}/api) but you can install it on your own machine if you'd like. + +The code is available on [GitHub]({{ config.repo_url }}). Available package versions can be seen on the [releases page]({{ config.repo_url }}/releases) and can be installed via pip, a requirements file, or downloaded and installed manually. + +!!! warning "pip available on ECN machines needs updated." + Installation via pip is possible through pip's [VCS support](https://pip.pypa.io/en/stable/reference/pip_install/#vcs-support) which was introduced in pip 19. The version of pip available on ECN supported machines is too old and needs updated. To update pip, run: + + ```bash + pip install -U pop + ``` + +=== "Install via pip" + To install webqueue2-api 0.9.1, you can run: + + ``` + pip install git+{{ config.repo_url }}@0.9.1#egg=webqueue2-api + ``` + + If you'd like a version other than 0.9.1 simply replace the that version number with a different version listed on the [releases page]({{ config.repo_url }}/releases). + +=== "Install via requirements file" + To install webqueue2-api 0.9.1, place the following line your requirements file: + + ```bash + echo "git+{{ config.repo_url }}@0.9.1#egg=webqueue2-api" >> requirements.txt + pip install -r requirements.txt + ``` + + If you'd like a version other than 0.9.1 simply replace the that version number with a different version listed on the [releases page]({{ config.repo_url }}/releases). \ No newline at end of file diff --git a/docs/api/Items.md b/docs/api/Items.md index 1eea0ec..9d14e8f 100644 --- a/docs/api/Items.md +++ b/docs/api/Items.md @@ -1 +1,443 @@ # Items + +An item is a chronological representation of an interaction with a user. + +## Endpoint +``` +GET /api/{queue}/{number} +``` +### Parameters +Name | Value +- | - +`queue` | The queue of the item. +`number` | The number of the item. + +### Query String Options +Name | Description | Possible Values +- | - | - +`headersOnly` | When `"True"`, only meta data will be loaded. When `"False"` content will be parsed. (Defaults to `"False"`.) | `"True"` \| `"False"` + +### Return Codes +Code | Description +- | - +`200 - Ok` | On success. +`500 - Internal Service Error` | On failure. + + +## Properties + +### `Item.lastUpdated` +Type | Description +- | - +`String` | An ISO 8601 formatted time string showing the last time the file was updated according to the filesystem. + +!!! example + ```js + console.log(Item.lastUpdated) + // Expected Output + "2021-04-03T00:48:38+00:00" + ``` + + +### `Item.headers` +Type | Description +- | - +`Array` | An array of objects containing parsed item headers in `type`/`value` pairs. + +!!! example + ```js + console.log(Item.headers[0]) + // Expexted Output + { type: "From", content: "\"Campbell, Justin Tyler\" \n"} + ``` + +### `Item.content` +Type | Description +- | - +`Array` | A chronological array of objects representing 1 of 9 possible actions taken on an item. + +**Possible Actions** +??? example "Directory Information" + Information about the user info collected from ECNDB including but not limited to alias, phone number and office location. + + ??? info "Properties" + | Key | Value | + | - | - | + |`type`|`directory_information`| + | `Name` | The real name of the sender. | + | `Login` | The career account alias of the sender. | + | `Computer` | The computer the item is related to. Formatting may vary. | + | `Location` | Where the computer is located. | + | `Email` | The email address of the sender. | + | `Phone` | The phone number of the sender. | + | `Office` | The office location of the sender. | + | `UNIX Dir` | The home directory for the user on non-Windows systems | + | `Zero Dir` | The home directory for the user via Active Directory | + | `User ECNDB` | Link to the sender's username report in ECNDB | + | `Host ECNDB` | Link to the computer report in ECNDB | + | `Subject` | The subject of the email sent to the queue | + + ??? quote "Parsed Example" + ```js + { + "type": "directory_information", + "Name": "Nestor Fabian Rodriguez Buitrago", + "Login": "rodri563", + "Computer": "ce-205-38 (128.46.205.67)", + "Location": "HAMP G230", + "Email": "rodri563@purdue.edu", + "Phone": "7654766893", + "Office": "HAMP G230", + "UNIX Dir": "/home/bridge/b/rodri563", + "Zero Dir": "U=\\\\bridge.ecn.purdue.edu\\rodri563", + "User ECNDB": "http://eng.purdue.edu/jump/2e8399a", + "Host ECNDB": "http://eng.purdue.edu/jump/2e83999", + "Subject": "Autocad installation" + } + ``` +??? example "Initial Message" + The body of the email the item originated from. + + ??? info "Properties" + | Key | Value | + | - | - | + | `type` | `initial_message` | + | `datetime` | RFC 8061 formatted datetime string. | + | `from_name` | The sender's real name. Formatting may vary. This can be empty. | + | `from_email` | The sender's email address. | + | `to` | A list of names(s) and email(s) of people this message was sent to. | + | `cc` | A list of name(s) and email(s) of people who were CC'd. This can be empty. | + | `subject` | The subject of the initial message. | + | `content` | The content of the message as an list of strings. | + + ??? quote "Parsed Example" + ```js + { + "type": "initial_message", + "datetime": "2020-09-11T01:26:45+00:00", + "from_name": "Justin Campbell", + "from_email": "campb303@purdue.edu", + "to": [ + { "name": "John Doe", "email": "johndoe@example.com" }, + ], + "cc": [ + { "name": "", "email": "janesmith@example.com" } + ], + "subject": Maps to item.subject, + "content": [ + "I need some help with something.\n" + ] + } + ``` +??? example "Edit" + Information added by someone at ECN, usually for internal use and/or communication. + + ??? info "Properties" + | Key | Value | + | - | - | + | `type` | `edit` | + | `datetime` | RFC 8061 formatted datetime string. | + | `by` | The career account alias of the person who added the edit. | + | `content` | The content of the edit as a list of strings. | + + ??? quote "Parsed Example" + ```js + { + "type": "edit", + "datetime": "2020-04-22T16:39:51", + "by": "knewell", + "content": [ + "This is related to another item. I need to do X next.\n" + ] + } + ``` +??? example "Status" + A short message about the progress of the item. + + ??? info "Properties" + | Key | Value | + | - | - | + | `type` | `status` | + | `datetime` | RFC 8061 formatted datetime string. | + | `by` | The career account alias of the person who updated the status. | + | `content` | The content of the status as a list of strings. | + + ??? quote "Parsed Example" + ```js + { + "type": "status", + "datetime": "2020-04-23T10:35:47", + "by": "knewell", + "content": [ + "Doing X thing." + ] + } + ``` +??? example "Assignment" + Assigning the item to someone. + + ??? info "Properties" + | Key | Value | + | - | - | + | `type` | `assignment` | + | `datetime` | RFC 8061 formatted datetime string. | + | `by` | The career account alias of the person who changed the |assignment. | + | `to` | The career account alias of the person who the item was assigned to. | + + ??? quote "Parsed Example" + ```js + { + "type": "assignment", + "datetime": "2020-06-23T13:27:00", + "by": "harley", + "to": "campb303", + } + ``` +??? example "Reply To User" + A message from ECN to the user and/or related parties. + + ??? info "Properties" + | Key | Value | + | - | - | + | `type` | `reply_to_user` | + | `datetime` | RFC 8061 formatted datetime string. | + | `by` | The sender's real name. Formatting may vary. This can be empty. | + | `content` | The content of the message as an list of strings | + + ??? quote "Parsed Example" + ```js + { + "type": "reply_to_user", + "datetime": "2020-05-08T09:21:43", + "by": "ewhile", + "content": [ + "Sascha,\n", + "\n", + "Chicken kevin biltong, flank jowl prosciutto shoulder meatball meatloaf sirloin.\n", + "\n", + "Ethan White\n", + "ECN" + ] + } + ``` +??? example "Reply To User" + A message from ECN to the user and/or related parties. + + ??? info "Properties" + | Key | Value | + | - | - | + | `type` | `reply_to_user` | + | `datetime` | RFC 8061 formatted datetime string. | + | `by` | The sender's real name. Formatting may vary. This can be empty. | + | `content` | The content of the message as an list of strings | + + ??? quote "Parsed Example" + ```js + { + "type": "reply_to_user", + "datetime": "2020-05-08T09:21:43", + "by": "ewhile", + "content": [ + "Sascha,\n", + "\n", + "Chicken kevin biltong, flank jowl prosciutto shoulder meatball meatloaf sirloin.\n", + "\n", + "Ethan White\n", + "ECN" + ] + } + ``` +??? example "Reply From User" + A message from the user and/or related parties. + + ??? info "Properties" + | Key | Value | + | - | - | + | `type` | `reply_from_user` | + | `datetime` | RFC 8061 formatted datetime string. | + | `from_name` | The sender's real name. Formatting may vary. This can be empty. | + | `from_email` | The sender's email address. | + | `cc` | A list of name(s) and email(s) of people who were CC'd. This can be empty. | + | `headers` | A dictionary of headers from the reply. | + | `subject` | The subject of the reply. | + | `content` | The content of the message as an list of strings | + + ??? quote "Parsed Example" + ```js + { + "type": "reply_from_user", + "datetime": "2020-05-08T13:57:18+00:00", + "from_name": "Reckowsky, Michael J.", + "from_email": "mreckowsky@purdue.edu", + "cc": [ + { "name": "John Doe", "email": "johndoe@example.com" }, + { "name": "", "email": "janesmith@example.com" } + ], + "headers" : [ + { + "type": "Subject", + "content": "RE: New Computer Deploy" + }, + { + "type": "From", + "content": "\"Reckowsky, Michael J.\" " + }, + { + "type": "Date", + "content": "Fri, 8 May 2020 13:57:17 +0000" + }, + ], + "subject": "RE: New Computer Deploy", + "content": [ + "Ethan,\n", + "\n", + "Biltong beef ribs doner chuck, pork chop jowl salami cow filet mignon pork.\n", + "\n", + "Mike\n", + ] + } + ``` +??? example "Parse Error" + An error caused by a malformed delimiter or nested delimiters. + + ??? info "Properties" + | Key | Value | + | - | - | + |`type`|`parse_error`| + | `datetime` | RFC 8061 formatted datetime string. | + | `file_path` | Full path of the item with the error. | + | `expected` | Description of what the parser was expecting. | + | `got` | Line that cause the parse error. | + | `line_num` | The line number in the item that caused the parse error. | + + ??? quote "Parsed Example" + ```js + { + 'type': 'parse_error', + 'datetime': '2020-10-16T10:44:45', + 'file_path': '/home/pier/e/benne238/webqueue2/q-snapshot/aae/2', + 'expected': 'Did not encounter a reply-from-user ending delimiter', + 'got': 'Kris', + 'line_num': 468 + } + ``` + +### `Item.isLocked` +Type | Description +- | - +`Boolean` | A boolean showing whether or not a lockfile for the item is present. + +!!! example + ```js + console.log(Item.isLocked) + // Expexted Output + true + ``` + +### `Item.userEmail` +Type | Description +- | - +`String` | The email address of the person who this item is from. + +!!! example + ```js + console.log(Item.userEmail) + // Expexted Output + "campb303@purdue.edu" + ``` + +### `Item.userName` +Type | Description +- | - +`String` | The real name of the person who this item is from. + +!!! example + ```js + console.log(Item.userName) + // Expexted Output + "Justin Campbell" + ``` + +### `Item.userAlias`: +Type | Description +- | - +`String` | The Purdue career account alias of the person this item is from. + +!!! example + ```js + console.log(Item.userAlias) + // Expexted Output + "campb303" + ``` + +### `Item.assignedTo`: +Type | Description +- | - +`String` | The Purdue career account alias of the person this item is assigned to. + +!!! example + ```js + console.log(Item.assignedTo) + // Expexted Output + "sundeep" + ``` + +### `Item.subject:` +Type | Description +- | - +`String` | The subject of the original message for this item. + +!!! example + ```js + console.log(Item.subject) + // Expexted Output + "Can't Access Outlook" + ``` + +### `Item.status` +Type | Description +- | - +`String` | The most recent status update for the item. + +!!! example + ```js + console.log(Item.status) + // Expexted Output + "Waiting for Reply" + ``` + +### `Item.priority:` +Type | Description +- | - +`String` | The most recent priority for this item. + +!!! example + ```js + console.log(Item.priority) + // Expexted Output + "COVID" + ``` + +### `Item.department` +Type | Description +- | - +`String` | The most recent department for this item. + +!!! example + ```js + console.log(Item.department) + // Expexted Output + "Bussiness Office" + ``` + +### `Item.dateReceived` +Type | Description +- | - +`String` | An ISO 8601 formatted time string showing the date this item was created. + +!!! example + ```js + console.log(Item.dateReceived) + // Expexted Output + "2021-04-03T00:48:38+00:00" + ``` \ No newline at end of file diff --git a/docs/api/Queues.md b/docs/api/Queues.md index c7b35ed..0ef4ce9 100644 --- a/docs/api/Queues.md +++ b/docs/api/Queues.md @@ -1 +1,53 @@ # Queues + +A collection of [Items](/api/Items/). + +## Endpoint +``` +GET /api/{queue} +``` +### Parameters +Name | Value +- | - +`queue` | The name of the queue. + +### Query String Options +Name | Description | Possible Values +- | - | - +`headersOnly` | When `"True"`, only meta data will be loaded. When `"False"` content will be parsed. (Defaults to `"False"`.) | `"True"` \| `"True"` + +### Return Codes +Code | Description +- | - +`200 - Ok` | On success. +`500 - Internal Service Error` | On failure. + +## Properties + +### `Queue.name` +Type | Description +- | - +`String` | The name of the queue. + +!!! example + ```js + console.log(Queue.name) + // Expected Output + "ce" + ``` + +### `Queue.items` +Type | Description +- | - +`Array` | The [Items](/api/Items/) in the queue. + +!!! example + ```js + console.log(Queue.items) + // Expected Output + [ + { queue: "ce", number: 01, lastUpdated: "2021-03-26T17:12:00-0400" ... } + { queue: "ce", number: 02, lastUpdated: "2021-05-29T17:12:00-0400" ... } + { queue: "ce", number: 03, lastUpdated: "2020-12-29T17:12:00-0400" ... } + ] + ``` \ No newline at end of file diff --git a/docs/api/get_access_token.js b/docs/api/get_access_token.js new file mode 100644 index 0000000..c3b5929 --- /dev/null +++ b/docs/api/get_access_token.js @@ -0,0 +1,10 @@ +fetch( + "{{ production_url }}/api/login", + { + method: "POST", + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ "username": USERNAME, "password": PASSWORD}) + } + ) + .then( resp => resp.json() ) + .then( data => console.log( data.access_token )) \ No newline at end of file diff --git a/docs/awesome-pages.yaml b/docs/awesome-pages.yaml index e502d65..0e8234b 100644 --- a/docs/awesome-pages.yaml +++ b/docs/awesome-pages.yaml @@ -1,4 +1,5 @@ # YAML Configuration for Awesome Pages mkdocs Plugin # See: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin nav: - - ... \ No newline at end of file + - index.md + - API: api \ No newline at end of file diff --git a/docs/css/custom_css.css b/docs/css/custom_css.css index 9147825..f1ca50a 100644 --- a/docs/css/custom_css.css +++ b/docs/css/custom_css.css @@ -1,9 +1,9 @@ +/* Make Tables Fullwidth */ .md-typeset__table { width: 100% !important; } - table { width: 100% !important; display: table !important; border-spacing: 0px !important; -} +} \ No newline at end of file diff --git a/docs/ecnqueue.md b/docs/ecnqueue.md deleted file mode 100644 index 9f329aa..0000000 --- a/docs/ecnqueue.md +++ /dev/null @@ -1,21 +0,0 @@ -# ECNQueue documentation - -## Item Class - -::: webqueue2_api.ECNQueue.Item - :docstring: - -### Item functions - -::: webqueue2_api.ECNQueue.Item - :members: - -## Queue Class - -::: webqueue2_api.ECNQueue.Queue - :docstring: - -### Queue Functions - -::: webqueue2_api.ECNQueue.Queue - :memebers: diff --git a/docs/index.md b/docs/index.md index 000ea34..80934d4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1 @@ -# Welcome to MkDocs - -For full documentation visit [mkdocs.org](https://www.mkdocs.org). - -## Commands - -* `mkdocs new [dir-name]` - Create a new project. -* `mkdocs serve` - Start the live-reloading docs server. -* `mkdocs build` - Build the documentation site. -* `mkdocs -h` - Print help message and exit. - -## Project layout - - mkdocs.yml # The configuration file. - docs/ - index.md # The documentation homepage. - ... # Other markdown pages, images and other files. +See the [API Docs](api/Getting Started/). \ No newline at end of file diff --git a/docs/webqueue2 backend overview.md b/docs/webqueue2 backend overview.md deleted file mode 100644 index 2620d60..0000000 --- a/docs/webqueue2 backend overview.md +++ /dev/null @@ -1,12 +0,0 @@ -# *(draft)* Webqueue2 backend overview - -!!! note - *This document should be renamed to `index.md` to make it the default landing page. The current index.md file contains information for mkdocs that maybe useful later* - -## Overview -In order to function, the backend of Webqueue2 relies primarily on two scripts, the `ECNQueue.py` script, and the `api.py` script. These two scripts work in conjuction with eachother inorder to read, parse, and transfer item and queue information created by the `qcli` to the frontend. - -### ECNQueue.py -The `ECNQueue.py` script does all of the data and information parsing. - -Items are currently stored in plain text within text files and all updates to those items are documented directly into those textfiles via the `qcli`. The `ECNQueue.py` script parses these text files and stores all the information regarding the item into a custom `Item` object. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index bc8596a..2cda46b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,13 +4,16 @@ ################################################################################ site_name: webqueue2 API -repo_url: https://github.itap.purdue.edu/ECN/webqueue2 +repo_url: https://github.itap.purdue.edu/ECN/webqueue2-api plugins: - search # Awesome Pages Settings # See: https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin - awesome-pages: filename: awesome-pages.yaml + # Macros Settings + # See: https://squidfunk.github.io/mkdocs-material/reference/variables/ + - macros markdown_extensions: - toc: permalink: ⚓︎ @@ -23,7 +26,6 @@ markdown_extensions: - pymdownx.emoji: emoji_index: !!python/name:materialx.emoji.twemoji emoji_generator: !!python/name:materialx.emoji.to_svg - - mkautodoc @@ -32,9 +34,6 @@ markdown_extensions: # See: https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/ ################################################################################ -# TODO: Implement versioning -# See: https://squidfunk.github.io/mkdocs-material/setup/setting-up-versioning/#versioning - theme: name: material palette: @@ -54,8 +53,12 @@ extra: link: https://purdueecn.slack.com/archives/C019K7PSPNW name: webqueue2 on Slack - icon: fontawesome/brands/github - link: https://github.itap.purdue.edu/ECN/webqueue2 - name: webqueue2 on GitHub + link: https://github.itap.purdue.edu/ECN/webqueue2-api + name: webqueue2-API on GitHub + # Variables for Macros + production_url: "https://engineering.purdue.edu/webqueue/webqueue2/build" + example_access_token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTY1NTIyMDIsIm5iZiI6MTYxNjU1MjIwMiwianRpIjoiZDgyNGM1MWItM2JmNy00ZDUzLWE0YTgtY2VhZWQ5ZmVjNGYzIiwiZXhwIjoxNjE2NTUzMTAyLCJzdWIiOiJjYW1wYjMwMyIsImZyZXNoIjpmYWxzZSwidHlwZSI6ImFjY2VzcyIsImNzcmYiOiI1Yjk5NWQ5OS05YjIzLTQyMjYtYTc0OC1lMmQ5OTA4MDkzOTQifQ.6z7EReDfhPkBkuAMHEvDuMDV4wVbqrWSjQXdRyv_5hE" + example_csrf_token: "7b7c1ea8-f6bb-4204-99af-cd4124a69d89" extra_javascript: - https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js diff --git a/setup.py b/setup.py index abb6552..b22ce74 100644 --- a/setup.py +++ b/setup.py @@ -8,8 +8,8 @@ "docs": [ "mkdocs", "mkdocs-material", - "mkautodoc", - "mkdocs-awesome-pages-plugin" + "mkdocs-awesome-pages-plugin", + "mkdocs-macros-plugin" ], } diff --git a/webqueue2_api/ECNQueue.py b/webqueue2_api/ECNQueue.py index 17d9c5a..4fbcd65 100644 --- a/webqueue2_api/ECNQueue.py +++ b/webqueue2_api/ECNQueue.py @@ -85,25 +85,31 @@ def isValidItemName(name: str) -> bool: class Item: """A single issue. - Example: - # Create an Item (ce100) - >>> item = Item("ce", 100) - - Attributes: - lastUpdated: An ISO 8601 formatted time string showing the last time the file was updated according to the filesystem. - headers: A list of dictionaries containing header keys and values. - content: A list of section dictionaries. - isLocked: A boolean showing whether or not a lockfile for the item is present. - userEmail: The email address of the person who this item is from. - userName: The real name of the person who this item is from. - userAlias: The Purdue career account alias of the person this item is from. - assignedTo: The Purdue career account alias of the person this item is assigned to - subject: The subject of the original message for this item. - status: The most recent status update for the item. - priority: The most recent priority for this item. - department: The most recent department for this item. - dateReceived: The date this item was created. - jsonData: A JSON serializable representation of the Item. + **Example:** + + ``` + # Create an Item (ce100) + >>> item = Item("ce", 100) + ``` + + **Attributes:** + + ``` + lastUpdated: An ISO 8601 formatted time string showing the last time the file was updated according to the filesystem. + headers: A list of dictionaries containing header keys and values. + content: A list of section dictionaries. + isLocked: A boolean showing whether or not a lockfile for the item is present. + userEmail: The email address of the person who this item is from. + userName: The real name of the person who this item is from. + userAlias: The Purdue career account alias of the person this item is from. + assignedTo: The Purdue career account alias of the person this item is assigned to + subject: The subject of the original message for this item. + status: The most recent status update for the item. + priority: The most recent priority for this item. + department: The most recent department for this item. + dateReceived: The date this item was created. + jsonData: A JSON serializable representation of the Item. + ``` """ def __init__(self, queue: str, number: int) -> None: @@ -155,11 +161,15 @@ def __init__(self, queue: str, number: int) -> None: def __getLastUpdated(self) -> str: """Returns last modified time of item reported by the filesystem in mm-dd-yy hh:mm am/pm format. - Example: - 07-23-20 10:34 AM + **Example:** + ``` + 07-23-20 10:34 AM + ``` - Returns: - str: last modified time of item reported by the filesystem in mm-dd-yy hh:mm am/pm format. + **Returns:** + ``` + str: last modified time of item reported by the filesystem in mm-dd-yy hh:mm am/pm format. + ``` """ # TODO: Simplify this code block by allowing __getFormattedDate to accept milliseconds since the epoch. unixTime = os.path.getmtime(self.__path) @@ -170,8 +180,10 @@ def __getLastUpdated(self) -> str: def __getRawItem(self) -> list: """Returns a list of all lines in the item file - Returns: - list: List of all the lines in the item file + **Returns:** + ``` + list: List of all the lines in the item file + ``` """ with open(self.__path, errors="replace") as file: return file.readlines() @@ -179,13 +191,18 @@ def __getRawItem(self) -> list: def __getHeaderBoundary(self) -> int: """Returns the 0 based line number where the Item headers stop. - Example: The header end would be on line 13 - 12: X-ECN-Queue-Original-URL: - 13: - 14: I need help. - - Returns: - int: line number where the Item headers end + **Example:** + ``` + The header end would be on line 13 + 12: X-ECN-Queue-Original-URL: + 13: + 14: I need help. + ``` + + **Returns:** + ``` + int: line number where the Item headers end + ``` """ for lineNumber, line in enumerate(self.__rawItem): if line == "\n": @@ -195,16 +212,20 @@ def __parseHeaders(self) -> list: """Returns a list containing dictionaries of header type and data. Removes queue prefixes and whitespace. - Examples: - "[ce] QStatus: Dont Delete\\nFrom: Justin Campbell \\n" - becomes - [ - {"QStatus": "Don't Delete"}, - {"From": "Justin Campbell "} - ] + **Examples:** + ``` + "[ce] QStatus: Dont Delete\\nFrom: Justin Campbell \\n" + becomes + [ + {"QStatus": "Don't Delete"}, + {"From": "Justin Campbell "} + ] + ``` - Returns: - list: Header dicts + **Returns:** + ``` + list: Header dicts + ``` """ headerString = "" @@ -413,25 +434,31 @@ def __parseSections(self) -> list: def __directoryParsing(self, directoryStartLine: int) -> dict: """Returns a dictionary with directory information - Example: - Name: Nestor Fabian Rodriguez Buitrago - Login: rodri563 - Computer: ce-205-38 (128.46.205.67) - Location: HAMP G230 - Email: rodri563@purdue.edu - Phone: 7654766893 - Office: HAMP G230 - UNIX Dir: /home/bridge/b/rodri563 - Zero Dir: U=\\bridge.ecn.purdue.edu\rodri563 - User ECNDB: http://eng.purdue.edu/jump/2e8399a - Host ECNDB: http://eng.purdue.edu/jump/2e83999 - Subject: Autocad installation - - Args: - directoryStartLine (int): line number within the item that the directory starts on - - Returns: - dict: dictionary that splits each line within the directory into a key and a value + **Example:** + + ``` + Name: Nestor Fabian Rodriguez Buitrago + Login: rodri563 + Computer: ce-205-38 (128.46.205.67) + Location: HAMP G230 + Email: rodri563@purdue.edu + Phone: 7654766893 + Office: HAMP G230 + UNIX Dir: /home/bridge/b/rodri563 + Zero Dir: U=\\\\bridge.ecn.purdue.edu\\rodri563 + User ECNDB: http://eng.purdue.edu/jump/2e8399a + Host ECNDB: http://eng.purdue.edu/jump/2e83999 + Subject: Autocad installation + ``` + + **Args:** + + `directoryStartLine (int)`: line number within the item that the directory starts on + + **Returns:** + ``` + dict: dictionary that splits each line within the directory into a key and a value + ``` """ directoryInformation = {"type": "directory_information"} @@ -525,22 +552,29 @@ def __directoryParsing(self, directoryStartLine: int) -> dict: def __assignmentParsing(self, contentStart: int) -> list: """Returns a list with assignment information dictionaries - Example: - Assigned-To: campb303 - Assigned-To-Updated-Time: Tue, 23 Jun 2020 13:27:00 EDT - Assigned-To-Updated-By: campb303 + **Example:** - Args: - contentStart (int): line number where the content starts + ``` + Assigned-To: campb303 + Assigned-To-Updated-Time: Tue, 23 Jun 2020 13:27:00 EDT + Assigned-To-Updated-By: campb303 + ``` - Returns: - list: [ - {"type": "assignment", - "datetime": datetime of the assignment, - "by": user who initiated the assignment, - "to": user who was assigned - }, - ] + **Args:** + + `contentStart (int)`: line number where the content starts + + **Returns:** + + ``` + list: [ + {"type": "assignment", + "datetime": datetime of the assignment, + "by": user who initiated the assignment, + "to": user who was assigned + }, + ] + ``` """ assignmentList = [] @@ -587,24 +621,28 @@ def __assignmentParsing(self, contentStart: int) -> list: def __initialMessageParsing(self, content: list) -> dict: """Returns a dictionary with initial message information - Example: - \n - Testtest\n - \n - - Args: - content (list): content of the initial message - - Returns: - dict: - "type": "initial_message", - "datetime": datetime the initial message was sent, - "from_name": from_name, - "from_email": user_email, - "to": [{email, name}], - "cc": [{email, name}], - "subject": initial message subject - "content": content of the initial message + **Example:** + ``` + \n + Testtest\n + \n + ``` + + **Args:** + `content (list)`: content of the initial message + + **Returns:** + ``` + dict: + "type": "initial_message", + "datetime": datetime the initial message was sent, + "from_name": from_name, + "from_email": user_email, + "to": [{email, name}], + "cc": [{email, name}], + "subject": initial message subject + "content": content of the initial message + ``` """ initialMessageDictionary = {} @@ -665,24 +703,27 @@ def __initialMessageParsing(self, content: list) -> dict: def __editParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with edit information - Example: - *** Edited by: campb303 at: 06/23/20 13:27:56 ***\n - \n - This be an edit my boy\n - \n - \n - \n + **Example:** + ``` + \*\*\* Edited by: campb303 at: 06/23/20 13:27:56 \*\*\* + This be an edit my boy - Args: - content (list): content of an edit - lineNum (int): line number of an edit within an item - Returns: - dict: a dictionary with these keys, - "type": "edi", - "by": initiator of the edit, - "datetime": datetime of the edit, - "content": content of the edit + ``` + **Args:** + + `content (list)`: content of an edit + + `lineNum (int)`: line number of an edit within an item + + **Returns:** + ``` + dict: a dictionary with these keys: + "type": "edit", + "by": initiator of the edit, + "datetime": datetime of the edit, + "content": content of the edit + ``` """ # Edit Info dictionary @@ -724,25 +765,30 @@ def __editParsing(self, content: list, lineNum: int) -> dict: def __replyToParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with reply to user information - Example: - *** Replied by: campb303 at: 06/23/20 13:28:18 ***\n - \n - This be a reply my son\n - \n - Justin\n - ECN\n - \n + **Example:** + ``` + \*\*\* Replied by: campb303 at: 06/23/20 13:28:18 \*\*\* - Args: - content (list): content of a reply to user - lineNum (int): line number of a reply to user in an item + This be a reply my son + + Justin + ECN + ``` - Returns: - dict: a dictionary with these keys, - "type": "reply_to_user", - "by": initiator of the reply to user, - "datetime": datetime of the reply to user, - "content": content of the reply to user + **Args:** + + `content (list)`: content of a reply to user + + `lineNum (int)`: line number of a reply to user in an item + + **Returns:** + ``` + dict: a dictionary with these keys, + "type": "reply_to_user", + "by": initiator of the reply to user, + "datetime": datetime of the reply to user, + "content": content of the reply to user + ``` """ replyInfo = {} @@ -781,20 +827,27 @@ def __replyToParsing(self, content: list, lineNum: int) -> dict: def __statusParsing(self, content: list, lineNum: int) -> dict: """Returns a dictionary with status information - Example: - *** Status updated by: campb303 at: 6/23/2020 13:26:55 ***\n - Dont Delete\n + **Example:** + ``` + \*\*\* Status updated by: campb303 at: 6/23/2020 13:26:55 \*\*\* + Dont Delete + \n + ``` - Args: - content (list): The content of a status update - lineNum (int): The line number of a status update in an item + **Args:** - Returns: - dict: a dictionary with these keys, - "type": "status", - "by": initiator of the status update, - "datetime": datetime of the status update, - "content": content of the status update + `content (list)`: The content of a status update + + `lineNum (int)`: The line number of a status update in an item + + **Returns:** + ``` + dict: a dictionary with these keys, + "type": "status", + "by": initiator of the status update, + "datetime": datetime of the status update, + "content": content of the status update + ``` """ statusInfo = {} @@ -836,41 +889,47 @@ def __statusParsing(self, content: list, lineNum: int) -> dict: def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: """Returns a dictionary with user reply information - Example: - === Additional information supplied by user ===\n - \n - Subject: Re: Beepboop\n - From: Justin Campbell \n - Date: Tue, 23 Jun 2020 13:30:45 -0400\n - X-ECN-Queue-Original-Path: /home/pier/e/queue/Attachments/inbox/2020-06-23/212-original.txt\n - X-ECN-Queue-Original-URL: https://engineering.purdue.edu/webqueue/Attachments/inbox/2020-06-23/212-original.txt\n - \n - Huzzah!\n - \n - ===============================================\n - \n - Args: - replyContent (list): The entire section of a reply-from-user - lineNumber (int): The line number of the begining of a reply-from-user section within and item + **Example:** + ``` + === Additional information supplied by user === + + Subject: Re: Beepboop\n + From: Justin Campbell \n + Date: Tue, 23 Jun 2020 13:30:45 -0400\n + X-ECN-Queue-Original-Path: /home/pier/e/queue/Attachments/inbox/2020-06-23/212-original.txt\n + X-ECN-Queue-Original-URL: https://engineering.purdue.edu/webqueue/Attachments/inbox/2020-06-23/212-original.txt\n + + Huzzah! + + =============================================== + \n + ``` + **Args:** + + `replyContent (list)`: The entire section of a reply-from-user + + `lineNumber (int)`: The line number of the begining of a reply-from-user section within and item Returns: - dict: a dictionary with these keys, - "type": "reply_from_user", - "from_name": name of the user that sent the reply, - "from_email": email of the user that sent the reply, - "subject": subject of the reply, - "datetime": the datetime of the reply, - "cc": [ - {"name": name of the carbon copied recipient, - "email": email of the carbon copied recipient - }, - ] - "content": content of the reply - "headers": [ - {"type": headerType, - "content": content - }, - ] + ``` + dict: a dictionary with these keys, + "type": "reply_from_user", + "from_name": name of the user that sent the reply, + "from_email": email of the user that sent the reply, + "subject": subject of the reply, + "datetime": the datetime of the reply, + "cc": [ + {"name": name of the carbon copied recipient, + "email": email of the carbon copied recipient + }, + ] + "content": content of the reply + "headers": [ + {"type": headerType, + "content": content + }, + ] + ``` """ replyFromInfo = {} @@ -987,22 +1046,23 @@ def __userReplyParsing(self, replyContent: list, lineNumber: int) -> dict: def __getFormattedSectionContent(self, sectionContent: list) -> list: """Returns a list with message content that is stripped of unnecessary newlines and begining delimiters - Example: - *** Edited by: mph at: 02/21/20 10:27:16 ***\n - \n - Still need to rename machines - but the networking issue now seems to \n - be resolved via another ticket.\n - \n - \n - \n - \n - \n + **Example:** + ``` + \*\*\* Edited by: mph at: 02/21/20 10:27:16 \*\*\* + + Still need to rename machines - but the networking issue now seems to \n + be resolved via another ticket. + \n + ``` - Args: - sectionContent (list): The section content of a parsed section + **Args:** - Returns: - list: the section content of a parsed section without any delimiters and unnecessary newlines + `sectionContent (list)`: The section content of a parsed section + + **Returns:** + ``` + list: the section content of a parsed section without any delimiters and unnecessary newlines + ``` """ # Continually removes the first line of sectionContent if it is a newline or delimiter in each iteration while len(sectionContent) > 1: @@ -1037,22 +1097,29 @@ def __getFormattedSectionContent(self, sectionContent: list) -> list: def __errorParsing(self, line: str, lineNum: int, expectedSyntax: str) -> dict: """Returns a dictionary with error parse information when a line is malformed - Example: - "*** Status updated by: ewhile at: 5/7/2020 10:59:11 *** sharing between\n" + **Example:** + ``` + \*\*\* Status updated by: ewhile at: 5/7/2020 10:59:11 \*\*\* + ``` - Args: - line (str): line of that threw error - lineNum (int): line number in the item that threw error - expectedSyntax (str): a message stating the syntax the line should follow + **Args:** - Returns: - dict: a dictionary with these keys, - "type": "parse_error", - "datetime": time the error was encountered, - "file_path": path of the item with erroneos line, - "expected": expectedSyntax, - "got": line, - "line_num": lineNum + `line (str)`: line of that threw error + + `lineNum (int)`: line number in the item that threw error + + `expectedSyntax (str)`: a message stating the syntax the line should follow + + **Returns:** + ``` + dict: a dictionary with these keys, + "type": "parse_error", + "datetime": time the error was encountered, + "file_path": path of the item with erroneos line, + "expected": expectedSyntax, + "got": line, + "line_num": lineNum + ``` """ errorDictionary = {} @@ -1081,14 +1148,19 @@ def __errorParsing(self, line: str, lineNum: int, expectedSyntax: str) -> dict: def __getSortedSections(self, sectionsList: list) -> list: """Sorts the sections chronologically by datetime - Example: - [example] need to do + **Example:** + ``` + [example] need to do + ``` - Args: - sections (list): the list of sections to be sorted + **Args:** - Returns: - list: a list of sections sorted by datetime + `sections (list)`: the list of sections to be sorted + + **Returns:** + ``` + list: a list of sections sorted by datetime + ``` """ sectionsLength = len(sectionsList) sortedSections = [] @@ -1120,14 +1192,21 @@ def __getSortedSections(self, sectionsList: list) -> list: def __isLocked(self) -> Union[str, bool]: """Returns a string info about the lock if true and a bool False if false - Example: A file is locked - "CE 100 is locked by campb303 using qvi" + **Example:** + ``` + A file is locked + "CE 100 is locked by campb303 using qvi" + ``` - Example: a file is not locked - False + **Example:** + ``` + a file is not locked + False + ``` - Returns: - Union[str, bool]: String with info about lock if true, bool False if false + **Returns:** + + `Union[str, bool]`: String with info about lock if true, bool False if false """ lockFile = self.__path + ".lck" if os.path.exists(lockFile): @@ -1143,19 +1222,28 @@ def __getMostRecentHeaderByType(self, headerType: str) -> str: """Return the data of most recent header of the given type. If no header of that type exists, return an empty string. - Example: Requesting a Status header that does exist - __getMostRecentHeaderByType("Status") - becomes "Waiting for Reply" + **Example:** + ``` + Requesting a Status header that does exist + __getMostRecentHeaderByType("Status") + becomes "Waiting for Reply" + ``` - Example: Requesting a Status header that doesn't exist - __getMostRecentHeaderByType("Status") - becomes "" + **Example:** + ``` + Requesting a Status header that doesn't exist + __getMostRecentHeaderByType("Status") + becomes "" + ``` - Args: - headerType (str): Type of header to return. + **Args:** - Returns: - str: data of most recent header of the given type or empty string. + `headerType (str)`: Type of header to return. + + **Returns:** + ``` + str: data of most recent header of the given type or empty string. + ``` """ for header in self.headers: if header["type"] == headerType: @@ -1166,15 +1254,21 @@ def __parseFromData(self, data: str) -> str: """Parse From header and return requested data. Returns empty string if requested data is unavailable. - Examples: From data is "From: Campbell, Justin " - __parseFromData(data="userName") returns "Campbell, Justin" - __parseFromData(data="userEmail") returns "campb303@purdue.edu" + **Examples:** + ``` + From data is "From: Campbell, Justin " + __parseFromData(data="userName") returns "Campbell, Justin" + __parseFromData(data="userEmail") returns "campb303@purdue.edu" + ``` - Args: - data (str): The data desired; can be "userName" or "userEmail". + **Args:** - Returns: - str: userName, userEmail or empty string. + `data (str)`: The data desired; can be "userName" or "userEmail". + + **Returns:** + ``` + str: userName, userEmail or empty string. + ``` """ fromHeader = self.__getMostRecentHeaderByType("From") userName, userEmail = email.utils.parseaddr(fromHeader) @@ -1191,14 +1285,22 @@ def __getUserAlias(self) -> str: """Returns user's Career Account alias if present. If Career Account alias isn't present, returns empty string. - Example: Email from campb303@purdue.edu - userAlias = "campb303" - - Example: Email from spam@spammer.net - userAlias = "" - - Returns: - str: User's Career Account alias if present or empty string + **Example:** + ``` + Email from campb303@purdue.edu + userAlias = "campb303" + ``` + + **Example:** + ``` + Email from spam@spammer.net + userAlias = "" + ``` + + **Returns:** + ``` + str: User's Career Account alias if present or empty string + ``` """ @@ -1220,8 +1322,10 @@ def __getFormattedDate(self, date: str) -> str: Returns empty string if the string argument passed to the function is not a datetime. See: https://en.wikipedia.org/wiki/ISO_8601 - Returns: - str: Properly formatted date/time recieved or empty string. + **Returns:** + ``` + str: Properly formatted date/time recieved or empty string. + ``` """ try: # This date is never meant to be used. The default attribute is just to set timezone. @@ -1237,8 +1341,10 @@ def __getFormattedDate(self, date: str) -> str: def toJson(self) -> dict: """Returns a JSON safe representation of the item. - Returns: - dict: JSON safe representation of the item. + **Returns:** + ``` + dict: JSON safe representation of the item. + ``` """ return self.jsonData @@ -1249,14 +1355,20 @@ def __repr__(self) -> str: class Queue: """A collection of items. - Example: - # Create a queue (ce) - >>> queue = Queue("ce") + **Example:** + + ``` + # Create a queue (ce) + >>> queue = Queue("ce") + ``` + + **Attributes:** - Attributes: - name: The name of the queue. - items: A list of Items in the queue. - jsonData: A JSON serializable representation of the Queue. + ``` + name: The name of the queue. + items: A list of Items in the queue. + jsonData: A JSON serializable representation of the Queue. + ``` """ def __init__(self, name: str) -> None: @@ -1272,8 +1384,10 @@ def __init__(self, name: str) -> None: def __getItems(self) -> list: """Returns a list of items for this Queue - Returns: - list: a list of items for this Queue + **Returns:** + ``` + list: a list of items for this Queue + ``` """ items = [] @@ -1293,8 +1407,10 @@ def toJson(self) -> dict: The JSON representation of every item in the Queue is added to the Queue's JSON data then the Queue's JSON data is returned. - Returns: - dict: JSON safe representation of the Queue + **Returns:** + ``` + dict: JSON safe representation of the Queue + ``` """ items = [] for item in self.items: @@ -1312,11 +1428,15 @@ def __repr__(self) -> str: def getValidQueues() -> list: """Returns a list of queues on the filesystem excluding ignored queues. - Example: - ["bidc", "me", "ce"] + **Example:** + ``` + ["bidc", "me", "ce"] + ``` - Returns: - list: Valid queues + **Returns:** + ``` + list: Valid queues + ``` """ queues = [] @@ -1333,20 +1453,23 @@ def getValidQueues() -> list: def getQueueCounts() -> list: """Returns a list of dictionaries with the number of items in each queue. - Example: - [ - { - name: "me", - number_of_items: 42 - }, - { - name: "bidc", - number_of_items: 3 - } - ] - - Returns: - list: Dictionaries with the number of items in each queue. + **Example:** + ``` + [ + { + name: "me", + number_of_items: 42 + }, + { + name: "bidc", + number_of_items: 3 + } + ] + ``` + **Returns:** + ``` + list: Dictionaries with the number of items in each queue. + ``` """ queueInfo = [] for queue in getValidQueues(): @@ -1363,8 +1486,10 @@ def getQueueCounts() -> list: def loadQueues() -> list: """Return a list of Queues for each queue. - Returns: - list: list of Queues for each queue. + **Returns:** + ``` + list: list of Queues for each queue. + ``` """ queues = [] diff --git a/webqueue2_api/api.py b/webqueue2_api/api.py index 0edcd61..e8a0618 100644 --- a/webqueue2_api/api.py +++ b/webqueue2_api/api.py @@ -107,20 +107,24 @@ class Login(Resource): def post(self) -> tuple: """Validates username/password and returns both access and refresh tokens. - Return Codes: - 200 (OK): On success. - 401 (Unauthroized): When username or password are incorrect. - 422 (Unprocessable Entitiy): When the username or password can't be parsed. - - Example: - curl -X POST - -H "Content-Type: application/json" - -d '{"username": "bob", "password": "super_secret"}' - - { "access_token": fjr09hfp09h932jp9ruj3.3r8ihf8h0w8hr08ifhj804h8i.8h48ith08ity409hip0t4 } - - Returns: - tuple: Response containing tokens and HTTP response code. + **Return Codes:** + ``` + 200 (OK): On success. + 401 (Unauthroized): When username or password are incorrect. + 422 (Unprocessable Entitiy): When the username or password can't be parsed. + ``` + **Example:** + ``` + curl -X POST + -H "Content-Type: application/json" + -d '{"username": "bob", "password": "super_secret"}' + + { "access_token": fjr09hfp09h932jp9ruj3.3r8ihf8h0w8hr08ifhj804h8i.8h48ith08ity409hip0t4 } + ``` + **Returns:** + ``` + tuple: Response containing tokens and HTTP response code. + ``` """ if not request.is_json: return ({ "message": "JSON missing from request body"}, 422) @@ -160,34 +164,41 @@ class Item(Resource): def get(self, queue: str, number: int) -> tuple: """Returns the JSON representation of the item requested. - Return Codes: - 200 (OK): On success. - - Example: - /api/ce/100 returns: - { - "lastUpdated": "07-23-20 10:11 PM", - "headers": [...], - "content": [...], - "isLocked": "ce 100 is locked by knewell using qvi", - "userEmail": "campb303@purdue.edu", - "userName": "Justin Campbell", - "userAlias": "campb303", - "assignedTo": "campb303", - "subject": "Beepboop", - "status": "Dont Delete", - "priority": "", - "deparment": "", - "building": "", - "dateReceived": "Tue, 23 Jun 2020 13:25:51 -0400" - } - - Args: - queue (str): The queue of the item requested. - item (int): The number of the item requested. - - Returns: - tuple: Item as JSON and HTTP response code. + **Return Codes:** + ``` + 200 (OK): On success. + ``` + + **Example:** + ``` + /api/ce/100 returns: + { + "lastUpdated": "07-23-20 10:11 PM", + "headers": [...], + "content": [...], + "isLocked": "ce 100 is locked by knewell using qvi", + "userEmail": "campb303@purdue.edu", + "userName": "Justin Campbell", + "userAlias": "campb303", + "assignedTo": "campb303", + "subject": "Beepboop", + "status": "Dont Delete", + "priority": "", + "deparment": "", + "building": "", + "dateReceived": "Tue, 23 Jun 2020 13:25:51 -0400" + } + ``` + **Args:** + ``` + queue (str): The queue of the item requested. + item (int): The number of the item requested. + ``` + + **Returns:** + ``` + tuple: Item as JSON and HTTP response code. + ``` """ return (ECNQueue.Item(queue, number).toJson(), 200) @@ -196,14 +207,20 @@ class Queue(Resource): def get(self, queues: str) -> tuple: """Returns the JSON representation of the queue requested. - Return Codes: - 200 (OK): On success. + **Return Codes:** + ``` + 200 (OK): On success. + ``` - Args: - queues (str): Plus (+) deliminited list of queues. + **Args:** + ``` + queues (str): Plus (+) deliminited list of queues. + ``` - Returns: - tuple: Queues as JSON and HTTP response code. + **Returns:** + ``` + tuple: Queues as JSON and HTTP response code. + ``` """ queues_requested = queues.split("+") @@ -218,23 +235,28 @@ class QueueList(Resource): def get(self) -> tuple: """Returns a list of dictionaries with the number of items in each queue. - Return Codes: - 200 (OK): On success. - - Example: - [ - { - name: "me", - number_of_items: 42 - }, - { - name: "bidc", - number_of_items: 3 - } - ] - - Returns: - tuple: Queues and item counts as JSON and HTTP response code. + **Return Codes:** + ``` + 200 (OK): On success. + ``` + + **Example:** + ``` + [ + { + name: "me", + number_of_items: 42 + }, + { + name: "bidc", + number_of_items: 3 + } + ] + ``` + **Returns:** + ``` + tuple: Queues and item counts as JSON and HTTP response code. + ``` """ return (ECNQueue.getQueueCounts(), 200)