# API

## Ajax endpoints

The endpoints are standard [Wordpress ajax endpoints](https://codex.wordpress.org/AJAX_in_Plugins) with "**concepts**" as the `action`.

The API is called upon using the url `<domain>/ajax.php`.

Use the following parameters in your requests:

* **uuid** - The object to handle
* **route** - The action to take

> Use query parameters for `GET` and *form-data* for `POST`

## Routes

| Method | Route  | Description                      |
| ------ | ------ | -------------------------------- |
| `GET`  | show   | Reading or retrieving data.      |
| `POST` | create | Add/Create Concept to site.      |
| `POST` | delete | Remove/Delete Concept from site. |
| `POST` | update | Update Concept on site.          |
| `POST` | sync   | Synchronize Concept to the site. |

```
Ex. https://www.example.com/ajax.php?action=concepts&route=show&uuid=123-456-789
```

### Parents and parent creation

Concepts are hierarchical which means that they have a parent-child relation to it self. They will therefor depend on its parent to exist in order to set itself as *child*. To make sure its parent always exist in Wordpress, both `create` and `update` will first check if its parent exist and try to create it if it doesn't. This behaviour is recursive which means that each "parent creation" will trigger a check for its parent which might trigger a new "parent creation" and so on. Only the needed parents will be checked so if parent is found no further checks will be done further up in the hierarchy.

### Synchronizing Concepts

The `sync` route combines the CRUD-functionality to perform the task of keeping a concept in sync between Open Content and Wordpress. It will *create* non existing concepts and *update* existing ones. Concepts that can't be retrieved from Open Content will be *removed* for Wordpress if they are found.

### Events

We trigger events whenever a certain endpoints cause concepts to be *created*, *updated* or *deleted*. Use our the `\Everyware\Concepts\ConceptEvents` to listen to these events.

```php
use \Everyware\Concepts\ConceptEvents;

$eventHandler = new MyEventHandler(new DependencyClass);
ConceptEvents::listen('create', [$eventHandler, 'handle']);

// Or use one of the predefined methods

ConceptEvents::onCreate([$eventHandler, 'handle']);
```

The `listen()` method takes up to three arguments:

1. The event name (string) that this listener wants to listen to;
2. A PHP callable that will be executed when the specified event is dispatched;
3. An optional priority, defined as a positive or negative integer (defaults to 0). The higher the number, the earlier the listener is called. If two listeners have the same priority, they are executed in the order that they were added.

A [PHP callable](https://php.net/manual/en/language.pseudo-types.php#language.types.callback) is a PHP variable that can be used by the `call_user_func()` function and returns *true* when passed to the `is_callable()` function. It can be a `\Closure` instance, an object implementing an `__invoke()` method (which is what closures are in fact), a string representing a function or an array representing an object method or a class method.

Once a listener is registered, it waits until the event is notified. In the above example, when the **create** event is triggered, the `MyEventHandler::handle()` method will be called will pass an event which implements `Everyware\Concepts\Contracts\ConceptEvent` as the single argument:

```php
use \Everyware\Concepts\ConceptEvents;

class MyEventHandler
{
    // ...

    public function handle(ConceptEvents $event)
    {
        $uuid = $event->getUuid();
        $effectedPost = $event->getPost();

        // ... do something
    }
}
```

The `Everyware\Concepts\Contracts\ConceptEvent` event will include the uuid and the Post that triggered the event.

So far, you've seen how PHP objects can be registered as listeners. You can also register PHP [Closures](https://php.net/manual/en/functions.anonymous.php) as event listeners:

```php
use \Everyware\Concepts\ConceptEvents;

ConceptEvents::onCreate(function(ConceptEvent $event) {
    $uuid = $event->getUuid();
    $effectedPost = $event->getPost();

    // ... do something
});
```

We have also added some other ways to use classes:

```php
use \Everyware\Concepts\ConceptEvents;

ConceptEvents::onCreate('Foo\MyEventHandler'); // Will by default call the "Foo\MyEventHandler::handle()" method on a callable class.

ConceptEvents::onCreate('Foo\MyEventHandler@customMethod'); // Will call the "Foo\MyEventHandler::customMethod()" method on a callable class.
```

The events are handled by Wordpress action hooks. This means that you can use wordpress to handle these events to:

```php
use Everyware\Concepts\Contracts\ConceptEvent;

add_action('ew_concept_created', function(ConceptEvent $event) {
    $uuid = $event->getUuid();
    $effectedPost = $event->getPost();

    // Do some stuff with this post
});
```

The available hooks are:

* **ew\_concept\_created**
* **ew\_concept\_deleted**
* **ew\_concept\_updated**

> Note! Learn more about listening to wordpress actions [here](https://developer.wordpress.org/reference/functions/add_action/)

## Response

These endpoints have a number of responses depending on the outcome. Every responses comes with "responseCodes" to describe the outcome of the request and any side-effects that might have had. The possible "responseCodes":

| ResponseCodes                | Description                                                                                                                                                                                                            |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ALREADY_EXISTS`             | Is primarily used on `create` and indicates that the requested *Concept* already exists in Wordpress.                                                                                                                  |
| `INTERNAL_ERROR`             | The server encountered an unexpected condition with unknown side-effects.                                                                                                                                              |
| `INVALID_ROUTE`              | The `route` value is invalid. Either it doesn't exist or the wrong method is used.                                                                                                                                     |
| `MOVED`                      | Is primarily used on `update` and indicates that the requested *Concept* have been moved in its hierarchical order (Its *Parent* has changed).                                                                         |
| `NOT_FOUND_IN_WP`            | The requested *Concept* could not be found in Wordpress.                                                                                                                                                               |
| `NOT_FOUND_IN_SOURCE`        | The requested *Concept* could not be retrieved from Open Content.                                                                                                                                                      |
| `PARENT_CREATED`             | The request resulted in the creation of parents. This can be in direct and indirect relation(*parent* and/or *parents parent*).                                                                                        |
| `PARENT_NOT_CREATED`         | The server encountered an unexpected condition when trying to create a parent. The requested *Concept* will not keep its parent relation on the site. This will result in a hierarchical miss-match with Open Content. |
| `PARENT_NOT_FOUND_IN_WP`     | The requested *Concepts* parent could not be found in Wordpress. This will trigger creation for that *Parent*.                                                                                                         |
| `PARENT_NOT_FOUND_IN_SOURCE` | The requested *Concepts* parent could not be retrieved from Open Content. This pretty much means `PARENT_NOT_CREATED` where we know the condition for failing creation.                                                |

### Show `GET`

```
Ex. https://www.example.com/ajax.php?action=concepts&route=show&uuid=XXX-XXX-XXX
```

**form-data:** none

#### Found

**Status Code:** 200

```javascript
{
    "responseCodes": [],
    "data": {
        "uuid": "XXX-XXX-XXX",
        "postId": "<POST_ID>",
        "permalink": "<POST_PERMALINK>",
        "parent": {
            "uuid": "XXX-XXX-XXX",
            "postId": "<POST_ID>",
            "permalink": "<POST_PERMALINK>",
            "parent": {}
        }
    }
}
```

#### Not found

**Status Code:** 404

```javascript
{
    "responseCodes": [],
    "data": {}
}
```

### Create `POST`

```
https://www.example.com/ajax.php?action=concepts&route=create&uuid=XXX-XXX-XXX
```

#### Created

**Status Code:** 201

```javascript
{
    "responseCodes": []
}
```

#### Created as well as one or more parents in the hierarchy

**Status Code:** 201

```javascript
{
    "responseCodes": ["PARENT_NOT_FOUND_IN_WP", "PARENT_CREATED"]
}
```

#### Created but parent was not

**Status Code:** 201

```javascript
{
    "responseCodes": ["PARENT_NOT_FOUND_IN_WP", "PARENT_NOT_CREATED"]
}
```

#### Created but could not retrieve one or more parents from Open Content

**Status Code:** 201

```javascript
{
    "responseCodes": ["PARENT_NOT_FOUND_IN_WP", "PARENT_NOT_FOUND_IN_SOURCE"]
}
```

#### Not Created since it already exists

**Status Code:** 409

```javascript
{
    "responseCodes": ["ALREADY_EXISTS"]
}
```

#### Not Created since it could not be retrieved from Open Content

**Status Code:** 424

```javascript
{
    "responseCodes": ["NOT_FOUND_IN_SOURCE"]
}
```

#### Not Created because of internal error

**Status Code:** 500

```javascript
{
    "responseCodes": ["INTERNAL_ERROR"]
}
```

### Delete `POST`

```
https://www.example.com/ajax.php?action=concepts&route=delete&uuid=XXX-XXX-XXX
```

#### Deleted

**Status Code:** 200

```javascript
{
    "responseCodes": []
}
```

#### Not Deleted because it could not be found in Wordpress

**Status Code:** 404

```javascript
{
    "responseCodes": ["NOT_FOUND_IN_WP"]
}
```

#### Not Deleted because of internal error

**Status Code:** 500

```javascript
{
    "responseCodes": ["INTERNAL_ERROR"]
}
```

### Update `POST`

```
https://www.example.com/ajax.php?action=concepts&route=update&uuid=XXX-XXX-XXX
```

#### Updated

**Status Code:** 200

```javascript
{
     "responseCodes": []
}
```

#### Updated and one or more parents in the hierarchy was created

**Status Code:** 200

```javascript
{
    "responseCodes": ["PARENT_NOT_FOUND_IN_WP", "PARENT_CREATED"]
}
```

#### Updated and parent was changed

**Status Code:** 200

```javascript
{
    "responseCodes": ["MOVED"]
}
```

#### Updated and moved to a newly created parent this might include creation of other parents in the hierarchy

**Status Code:** 200

```javascript
{
    "responseCodes": ["PARENT_NOT_FOUND_IN_WP", "PARENT_CREATED", "MOVED"]
}
```

#### Updated but parent was not found in Wordpress and could not be created

**Status Code:** 200

```javascript
{
    "responseCodes": ["PARENT_NOT_FOUND_IN_WP", "PARENT_NOT_CREATED"]
}
```

#### Updated but could not retrieve one or more parents from Open Content

**Status Code:** 200

```javascript
{
    "responseCodes": ["PARENT_NOT_FOUND_IN_WP", "PARENT_NOT_FOUND_IN_SOURCE"]
}
```

#### Not updated because it could not be found in Wordpress

**Status Code:** 404

```javascript
{
    "responseCodes": ["NOT_FOUND_IN_WP"]
}
```

#### Not updated because it could not be retrieved from Open Content

**Status Code:** 424

```javascript
{
    "responseCodes": ["NOT_FOUND_IN_SOURCE"]
}
```

#### Not updated because of internal error

**Status Code:** 500

```javascript
{
    "responseCodes": ["INTERNAL_ERROR"]
}
```

### Sync `POST`

```
https://www.example.com/ajax.php?action=concepts&route=sync&uuid=XXX-XXX-XXX
```

#### Synchronized

**Status Code:** 200

```javascript
{
     "responseCodes": []
}
```

> Note! The Concept might have been created, updated or removed as part of the synchronization.

#### Synchronized and parent was changed

**Status Code:** 200

```javascript
{
    "responseCodes": ["MOVED"]
}
```

#### Not synchronized because it could not be retrieved from Open Content

**Status Code:** 424

```javascript
{
    "responseCodes": ["NOT_FOUND_IN_SOURCE"]
}
```

> Most of the errors available for **create**, **update** and **delete** will also be possible to sync.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.navigaglobal.com/everyware/mu-plugins/everyware-plugin-concepts/api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
