Skip to content
DTPY edited this page Mar 7, 2025 · 60 revisions

T.A.R.A.L.L.O. APIv2

Introduction

The T.A.R.A.L.L.O. inventory system comes with a fancy API that lets you obtain and update information about the items, add and delete items via HTTP requests.

The API uses JSON format for gathering requests contents and for giving responses.

Index


Authentication

For authenticating yourself, you need to put in the HTTP header of the request the Key Authorization
and in the value the string Token, a space and the token obtained from TARALLO.

e.g. Authorization: Token yoLeCHmEhNNseN0BlG0s3A:ksfPYziGg7ebj0goT0Zc7pbmQEIYvZpRTIkwuscAM_k

Specifically, that's the default token for the development version. When you do a make up, you get that token already configured.

If you want to add more tokens (e.g. in production):

  1. Open TARALLO
  2. Go to Options
  3. Type any description ("My awesome program that accesses the APIs")
  4. Press "Get token"
  5. Copy the token from the green confirmation message

Part of the token is hashed in the database, that's why it's replaced by ********** in the table, copy it from the green thing 'cause that's the only time you're going to see it un-hashed and in plain text.

Checking the session status

Do a GET request to /v2/session.
In case of session expired or user not authenticated the response will look like this:

{
  "exception": "WEEEOpen\\Tarallo\\HTTP\\AuthenticationException",
  "message": "Not authenticated or session expired"
}

If the session is valid the response will look like this:

{
  "username": "John.Doe",
  "cn": "John Doe",
  "level": 0
}

Response codes:
200 - The user is authenticated (valid session)
401 - The user is not authenticated or session has expired


Get a Item

Do a GET request to /v2/items/CODE, where CODE is the Item code.
You must use the session cookie to authenticate this request.

If the Item exists in the database, the response will look like this:

{
    "code": "95",
    "features": {
      "brand": "HP",
      "model": "Compaq nc6000 (PD468AA#ABZ)",
      "motherboard-form-factor": "proprietary-laptop",
      "notes": "Supporta PXE (attivare in BIOS > security, F12 per usarlo), A61 adatto. No PAE, usare \"forcepae\" e parte. Scheda wifi rilevata ma è disattivata e non trova reti, capire perché. La batteria dura 38 min. Lettore di schede SD.",
      "os-license-code": "TH1S-1SN0T-AV4L-1DPR-0DUC-TK3Y",
      "os-license-version": "Windows XP Professional",
      "power-connector": "barrel",
      "psu-ampere": 3.5,
      "psu-volt": 18.5,
      "sn": "CNU666D24G",
      "software": "Xubuntu 18.04 LTS",
      "type": "case",
      "working": "yes"
    },
    "contents": [
      {
        "code": "B117",
        "features": {
          "brand": "HP",
          "ethernet-ports-1000m-n": 1,
          "ide-ports-n": 1,
          "integrated-graphics-brand": "ATI",
          "integrated-graphics-model": "Mobility Radeon 9600/9700",
          "key-bios-setup": "F10",
          "mac": "c0:ff:ee:b3:33:3f",
          "mini-jack-ports-n": 2,
          "mini-pci-sockets-n": 1,
          "model": "0890h",
          "motherboard-form-factor": "proprietary-laptop",
          "owner": "DISAT",
          "parallel-ports-n": 1,
          "ram-form-factor": "sodimm",
          "ram-type": "ddr",
          "rj11-ports-n": 1,
          "s-video-ports-n": 1,
          "serial-ports-n": 1,
          "type": "motherboard",
          "vga-ports-n": 1,
          "working": "yes"
        },
        "contents": [
          {
            "code": "C122",
            "features": {
              "brand": "Intel",
              "core-n": 1,
              "frequency-hertz": 1700000000,
              "model": "Pentium M 1.70 GHz",
              "owner": "DISAT",
              "type": "cpu",
              "variant": "17",
              "working": "yes"
            }
          },
          {
            "code": "R101",
            "features": {
              "brand": "Nanya",
              "capacity-byte": 536870912,
              "frequency-hertz": 167000000,
              "model": "NT512D64SH8A0FM-6K",
              "owner": "DISAT",
              "ram-form-factor": "sodimm",
              "ram-type": "ddr",
              "type": "ram",
              "working": "yes"
            }
          },
          {
            "code": "W5",
            "features": {
              "brand": "HP",
              "brand-manufacturer": "Intel",
              "mac": "00:0e:35:7d:33:b0",
              "mini-pci-sockets-n": 1,
              "model": "PRO/Wireless 2200BG",
              "sn": "CNX7373ASD",
              "type": "wifi-card"
            }
          }
        ]
      },
      {
        "code": "HDD197",
        "features": {
          "brand": "Hitachi",
          "capacity-decibyte": 40000000000,
          "hdd-odd-form-factor": "2.5-9.5mm",
          "mini-ide-ports-n": 1,
          "model": "MK4026GAX",
          "owner": "DISAT",
          "smart-data": "ok",
          "sn": "X1HF4444T",
          "spin-rate-rpm": 5400,
          "type": "hdd",
          "working": "yes"
        }
      },
      {
        "code": "ODD138",
        "features": {
          "brand": "HL-DT-ST",
          "color": "black",
          "hdd-odd-form-factor": "laptop-odd-9.5mm",
          "jae-ports-n": 1,
          "model": "RW/DVD GCC-4243N",
          "notes": "Scrive i CD ma i DVD li legge solo solo... 💿",
          "odd-type": "dvd-r",
          "owner": "DISAT",
          "type": "odd"
        }
      }
    ],
    "location": [
      "Polito",
      "LabFis4",
      "ArmadioL"
    ]
}

However, note that products and items are merged. This is easier when you just want to read, but if you plan to modify an item in some way it's not ideal.

Alternatively, you can make a request to /v2/items/CODE?separate.

Compare, without ?separate (item and product merged):

{
  "code": "R777",
  "features": {
    "capacity-byte": 536870912,
    "color": "green",
    "frequency-hertz": 667000000,
    "ram-ecc": "no",
    "ram-form-factor": "dimm",
    "ram-type": "ddr2",
    "type": "ram",
    "brand": "Samsung",
    "model": "S667ABC512",
    "sn": "ASD10A7C123456",
    "variant": "v1",
    "working": "no"
  },
  "location": [
    "Polito",
    "Chernobyl",
    "Table",
    "RamBox"
  ]
}

and with ?separate:

{
  "code": "R777",
  "features": {
    "brand": "Samsung",
    "model": "S667ABC512",
    "sn": "ASD10A7C123456",
    "variant": "v1",
    "working": "no"
  },
  "product": {
    "brand": "Samsung",
    "model": "S667ABC512",
    "variant": "v1",
    "features": {
      "capacity-byte": 536870912,
      "color": "green",
      "frequency-hertz": 667000000,
      "ram-ecc": "no",
      "ram-form-factor": "dimm",
      "ram-type": "ddr2",
      "type": "ram"
    }
  },
  "location": [
    "Polito",
    "Chernobyl",
    "Table",
    "RamBox"
  ]
}

Response codes:
200 - Item found
404 - Item not found
401 - The user is not authenticated or session has expired

From a single feature (if it's reasonably unique)

There's a GET /v1/features/{feature}/{value} endpoint that returns a list of items with a given feature value that must match exactly. Only text features are supported, i.e. the ones that you type into a text box (no dropdown, no numeric values with units, etc...).

It returns only the codes of the first few items, so it is useful only for features that should be unique, like serial numbers. The code that feeds the endpoint is used internally to find duplicate serial numbers, for example.

Examples:

GET /v2/features/sn/CN0M618F179729260FVOA00
{
  "status": "success",
  "data": [
    "A5"
  ]
}
GET /v2/features/sn/serial-number-that-doesnt-exist
{
  "status": "success",
  "data": []
}

Response codes:
200 - Request succeeded (may contain results or an empty array)
401 - The user is not authenticated or session has expired

From features

If you need anything more complex or expect a lot of results, use the search endpoints.


Adding a new Item

Requests:

POST /v2/items

If you want to manually assign a code use a PUT request instead

PUT /v2/items/{CODE}

If some items don't have a code, the server will try to generate one for each item.

You must use the session cookie to authenticate this request.

The request body should look like this:

{
  "parent": "SomeLocation",
  "features": {
    "brand": "Dill",
    "model": "XP-S92",
    "type": "case"
  },
  "contents": []
}

It's also possible to add a tree of items. In that case, if you want to specify a code for inner items, you should use the "code" parameter.
The "code" parameter is not allowed for outer items.

{
  "parent": "SomeLocation",
  "features": {
    "model": "XP-S92",
    "brand": "Dill",
    "type": "case"
  },
  "contents": [
    {
      "features": {
        "type": "motherboard",
        "brand": "Dell",
        "model": "F00-B4R"
      },
      "contents": [
        {
          "features": {
            "type": "cpu",
            "brand": "Intel",
            "model": "Core 3 Trio E3800",
            "frequency-hertz": "2800000000"
          },
          "contents": []
        }
      ]
    },
    {
      "code": "ASD",
      "features": {
        "type": "psu",
        "brand": "Gamma Electronics",
        "model": "ASD-400W"
      },
      "contents": []
    }
  ]
}

Responses contain the item code as the only data:

{"status": "success", "data": "PC42"}

or you may use the ?loopback parameter to request a copy of the entire item, e.g. do a POST /v2/items?loopback or a PUT /v2/items/CODE?loopback.

In any case, the response contains a Location: header with the URL of the newly added item, e.g.:

Location: /v2/items/PC42

Response codes:
201 - Item created
400 - Bad request (malformed JSON, validation failed, location does not exist, etc...)
401 - The user is not authenticated or session has expired
403 - The user doesn't have permission to add new items
404 - Item code contains invalid characters. Yes this is a bug and will be fixed someday


Updating an item's features

Request:

PATCH /v2/items/{id}/features

The body of the request must contain a JSON where you put every feature you want to change, with the new value. Other features will be unchanged.


Example:

{
  "feature1": "value1",
  "feature2": "value2",
  "feature3": "value3"
}

Setting an item's features

Request:

PUT /v2/items/{id}/features

Set the features of an item and delete every other features. The body is the same as above


Deleting and restoring an item

Deleting

Request:

DELETE /v2/items/CODE

Not that items that contain other items cannot be deleted and there's not API endpoint for recursive deletion, you'll have to delete their content or move it elsewhere. If you try to delete them, you'll get this 400 response:

{
  "status": "fail",
  "data": {
    "*": "Cannot delete an item while contains other items"
  }
}

Response codes:
204 - Item deleted successfully
400 - Invalid code item code (this will become a 404 some day)
400 - Items contains other items
401 - User not authenticated or session expired
403 - User not authorized to delete items
404 - Item doesn't exist or already deleted

Checking if it has been deleted

Items can be only soft-deleted: that is, they are removed from the item tree and any attempt to get them will return a 404, but they still retain all their features and can be recovered.

You can check if an item has been soft deleted and thus can be recovered with

GET /v2/deleted/R420

This means it has been deleted but can be recovered:

{
  "status": "success",
  "data": "2018-11-16T23:28:01+00:00"
}

The data is the deletion time and date.

A 404 response on the other hand means that it still exists and hasn't been deleted, or it never existed, or it has been permanently deleted and cannot be recovered.

Response codes:
200 - Item has been soft-deleted and can be recovered
401 - User not authenticated or session expired
404 - Item is not in the trash can (not deleted or not existing)

Recovering items

It has been deleted but you still need it? Good, there's a way to recover it!

Do a

PUT /v2/deleted/CODE/parent

the request body must be a JSON string (delimited by "") that is the item code of the parent item where the recovered item will be placed.

That is, to place R420 into computer 94, the request will look like this:

PUT /v2/deleted/R420/parent HTTP/1.1
Host: 127.0.0.1:8080
content-type: application/json
Cookie: ...

"94"

If the operation succeeds, you'll get a 201 response with no body.

If it doesn't, you may get this kind of responses:

{
  "status": "fail",
  "data": {
    "*": "Parent item doesn't exist"
  }
}
{
  "status": "fail",
  "data": {
    "*": "Request body should be a string"
  }
}

Response codes:
201 - Item has been restored
400 - Malformed request or parent item doesn't exist
401 - User not authenticated or session expired
403 - User not authorized to move items around
404 - Item is not in the trash can (not deleted or not existing)


Validation and "fixup"

All items go through a two-step validation process, that takes into account their features and their location:

  • Fixup: try to move misplaced items to their correct location
  • Validation: check that no impossible placements are still there, accept or reject the entire request accordingly

For instance, fixup moves RAM modules from a case to the motherboard contained inside it. If the motherboard could not be found or the fixup could not be applied, the request still goes through to the next phase.

Validation checks that RAM and motherboards have the same socket, and does the same for CPUs and motherboards, and prevents impossible placements like a CPU inside a HDD, and the like. If any validation step fails, the entire request is rejected.

This is done both for newly added items, and for "move" operations, even moves that restore deleted items.
The src/ItemLocationValidator.php file contains the code that does all these checks.

Both validation and fixup can be disabled on a per-request basis, with a parameter which will not be named since you shouldn't use it: if you really (and desperately) need it, check APIv2/Controller.php.


Moving and Losing an Item

Moving an item

Request:

PUT /v2/items/{id}/parent

The body of the request must be a string that is the name of the new location

Body example:

ArmadioL

Losing an item

Request:

DELETE /v2/items/{id}/parent

In case you lost an item and don't know where is it.


Searching an item

Compute the search

Request:

POST /v2/search

In the body you put all the parameters you need to search

Example:

{
  "location": "Rambox",
  "features": {
    "type": "case",
    "brand": "HP"
  }
}

You will get an id in response


Request:

PATCH /v2/search/{id}

Filter your previous search, where id is the id of the search

Example:

{
  "features": {
    "working": "yes"
  }
}

You will get an id in response

Getting a search

Request:

GET /search/{id}[/page/{page}]

Under costruction


Get an item's history

Request:

GET /v2/items/{id}/history

Return:

  • change string the change occured, see the list below
  • other string where is moved, if moved, else null
  • time string when the change happened
  • user string who made the change

Change char list:

  • C: create
  • U: update, feature(s) are modificated
  • M: move
  • D: delete
  • R: rename
  • L: lose

Example:

[
  {
  "change": "M",
  "other": "B25",
  "time": "1590355283.959208",
  "user": "ExampleScript"
  },
  {
    "change": "U",
    "other": null,
    "time": "1590355283.956257",
    "user": "ExampleScript"
  },
  {
    "change": "C",
    "other": null,
    "time": "1590355283.949887",
    "user": "ExampleScript"
  }
]

Get an item's summary

TODO


Getting stats

Get list of item IDs that have a certain feature

Request:

GET /getItemsForEachValue/{feature}[/{filter}[/{location}[/{creation}[/{deleted}]]]]

Send this request to get a list of item IDs that have a certain feature. They will be grouped by feature value. If filter is specified it will show only IDs of items that have that particular feature of that particular value. If none exist it will return an empty array. Example response below.

  • feature Feature name to look for
  • filter The value you want the feature to be
  • location Only items that are in a location
  • limit Maximum number of results
  • creation creation date (starts from here)
  • deleted Also count deleted/lost items
GET /getItemsForEachValue/color

{
   "green": ["B100", "B200", "..." ],
   "red": ["C100", "C200", "..." ]
}

GET /getItemsForEachValue/color/green

{
   "green": ["B100", "B200", "..." ]
}

GET /getItemsForEachValue/color/teal

[]

Get item by not feature

Request:

GET /getItemByNotFeature/{filter}/{notFeature}[/{location}[/{limit}[/{creation}[/{deleted}]]]]

Send this request to get a list of items with the feature in filter and don't have a feature specified in notFeature

  • filter feature that should be there write as type=value
  • notFeature Feature that should not be present at all
  • location Only items that are in a location
  • limit Maximum number of results
  • creation creation date (starts from here)
  • deleted Also count deleted/lost items

Example: /getItemByNotFeature/type=cpu/brand will return the list of CPUs without a brand specified

Get recent audit by type

Request:

GET /getRecentAuditByType/{type}[/{howMany}]

Get all recent changes filtered by change type

  • type the change you want to filter. See the list below.
  • howMany how many changes you want to receive. Default is 100.

Change char list:

  • C: create
  • U: update, feature(s) are modified
  • M: move
  • D: delete
  • R: rename
  • L: lose

You will get a json with format "code": "time of the change" Example:

{
  "203": "1602753536.986703",
  "R87": "1602753562.273268",
  "B237": "1602753524.907019",
  "C254": "1602751601.707732",
  "D12": "1602491494.600757"
}

Get count by feature

Request:

GET /getCountByFeature/{feature}[/{filter}[/{location}[/{creation[/{deleted[/{cutoff}]]]]]

Return the count of items with a specific set of feature

  • feature The feature you want to count
  • filter Feature that must match, useful to select items by type
  • location Consider only in this location and subtree
  • creation Creation date
  • deleted Also count deleted/lost items, defaults to false (don't count them)
  • cutoff Report features only if count is greater than (or equal to) this number, useful for text features with lots of possible values

Adding bulk items

Request:

POST /v2/bulk/add/{identifier}

Call this request to bulk add items. You must chose an identifier for the bulk. You can add ?overwrite=yes at the end if you want to replace a bulk with the same identifer.

The body must be a json array specifying for each element if is a item or a product with the parameter "type"="I"/"P"

Example:

[
  {
    "type": "I",
    "features": {
      "brand": "Olidata",
      "model": "Denver 69000",
      "variant": "default",
      "type": "case",
      "working": "yes"
    },
    "contents": [
      {
        "features": {
          "brand": "ASUSTek Computer inc.",
          "model": "P5KPL-VM",
          "variant": "default",
          "mac": "00:1a:22:52:a4:be",
          "sn": "MT707BK05303585",
          "type": "motherboard",
          "working": "yes"
        },
        "contents": [
          {
            "code": "C251",
            "features": {
              "brand": "Intel",
              "model": "Core 2 Duo E8200",
              "variant": "default",
              "type": "cpu",
              "working": "yes"
            }
          },
          {
            "features": {
              "brand": "Samsung",
              "model": "M3 78T2863DZS-CF7",
              "sn": "589442786",
              "variant": "default",
              "type": "ram",
              "working": "yes"
            }
          },
          {
            "code": "R597",
            "features": {
              "brand": "Samsung",
              "model": "M3 78T2953EZ3-CF7",
              "sn": "1231847313",
              "variant": "default",
              "type": "ram",
              "working": "yes"
            }
          }
        ]
      }
    ]
  },
  {
    "type": "P",
    "brand": "Samsung",
    "model": "M3 78T2953EZ3-CF7",
    "variant": "default",
    "features": {
      "capacity-byte": 1073741824,
      "color": "green",
      "frequency-hertz": 800000000,
      "ram-ecc": "no",
      "ram-form-factor": "dimm",
      "ram-timings": "6-6-6-18 as DDR2-800",
      "ram-type": "ddr2",
      "type": "ram"
    }
  },
  {
    "type": "P",
    "brand": "Intel",
    "model": "Core 2 Duo E8200",
    "variant": "default",
    "features": {
      "core-n": 2,
      "cpu-socket": "lga775",
      "frequency-hertz": 2660000000,
      "isa": "x86-64",
      "thread-n": 2,
      "type": "cpu"
    }
  },
  {
    "type": "P",
    "brand": "ASUSTek Computer inc.",
    "model": "P5KPL-VM",
    "variant": "default",
    "features": {
      "color": "golden",
      "cpu-socket": "lga775",
      "ethernet-ports-1000m-n": 1,
      "integrated-graphics-brand": "Intel",
      "integrated-graphics-model": "82G33/G31 Express",
      "key-bios-setup": "Del",
      "mini-jack-ports-n": 3,
      "motherboard-form-factor": "microatx",
      "ps2-ports-n": 2,
      "psu-connector-cpu": "4pin",
      "psu-connector-motherboard": "atx-24pin",
      "ram-form-factor": "dimm",
      "ram-type": "ddr2",
      "sata-ports-n": 4,
      "serial-ports-n": 1,
      "type": "motherboard",
      "usb-ports-n": 4,
      "vga-ports-n": 1
    }
  }
] 

Create a product

Request:

PUT /v2/products/{brand}/{model}/{variant}

Where brand, model and variant are the parameter of the new product.
In the body you must put a json array with the features of the product.
Add ?loopback=yes a the end if you want the product created as a JSON response.


Getting a product

Request:

GET /v2/products/{brand}/{model}/{variant}

Where brand, model and variant are the paramenters of the product you want to get.
Return a JSON of the product.

Getting products

Request:

GET /v2/products/{brand}/{model}

You can also get more than one product by not specifying the variant


Renaming a product

Request:

PATCH /v2/products/{brand}/{model}/{variant}

The brand, model and variant in the request are of the product you want to rename.
In the body you put a JSON array with the new brand, model and variant.
Add ?loopback=yes at the end of the request if you want the renamed product in response.


Updating a product

Request:

PATCH /v2/products/{brand}/{model}/{variant}/features

The brand, model and variant in the request are of the product you want to rename.
In the body you put a JSON array with the new brand, model and variant: specify features to update and to delete, other are left as they are.
Add ?loopback=yes at the end of the request if you want the renamed product in response.

Setting a product

Request:

POST /v2/products/{brand}/{model}/{variant}/features

The brand, model and variant in the request are of the product you want to rename.
In the body you put a JSON array with the new brand, model and variant: delete every feature, replace with new ones.
Add ?loopback=yes at the end of the request if you want the renamed product in response.


Deleting a product

Request:

DELETE /v2/products/{brand}/{model}/{variant}

Delete the product with the same parameters in the request


Autosuggest items

Items

This request is used in autosuggest to fetch items with the typed initials.

Requests:

GET /v2/autosuggest/code?q={chars}

where chars is the initials of the item

Example:
/v2/autosuggest/code?q=hdd1
return up to 10 items with initial code hdd1 (ex. hdd101, hdd105 etc...)

Locations

This request is used in autosuggest to fetch locations with the typed initials.

Requests:

GET /v2/autosuggest/location?q={chars}

where chars is the initials of the item, returns an array of object with name and color (might be null) of the locations. max 10 locations

Example:

GET /v2/autosuggest/location?q=box

[
   {
      "name" : "CpuBox",
      "color" : null
   },
   {
      "name" : "RamBox",
      "color" : "red"
   }
]