API:Templated parameters

From mediawiki.org

As of MediaWiki 1.32 , API calls may use two level arrays to provide data to the API. This extends the data formats available for API servers to receive arguments from API clients.

Overview

The API structure allows API calls to pass two levels of arguments to an API for evaluation.

  • A module can define a templated parameter like "{fruit}-quantity", where the actual parameters recognized correspond to the values of a multi-valued parameter. Then clients can make requests like "fruits=apples|bananas&apples-quantity=1&bananas-quantity=5".
  • action=paraminfo will return templated parameter definitions separately from normal parameters. All parameter definitions now include an "index" key to allow clients to maintain parameter ordering when merging normal and templated parameters.
Fruits Quantity
Apples 1
Bananas 5

Client API

The client can use templated parameters in the same fashion as invoking any web service, using query string arguments or HTTP POST arguments.

Parameter information

The "paraminfo" API query can be used to interrogate the API about the structure of it's parameters.

In this example, the demonstration extension is called ParameterTemplateDemo.

For a development machine example (wiki hosted at machine/wikiprefix), the API test URL would be: http://machine/wikiprefix/api.php?action=paraminfo&modules=ParameterTemplateDemo

which will produce:

{
    "paraminfo": {
        "helpformat": "none",
        "modules": [
            {
                "name": "ParameterTemplateDemo",
                "classname": "ParameterTemplateDemoAPI",
                "path": "ParameterTemplateDemo",
                "group": "action",
                "prefix": "",
                "source": "ParameterTemplateDemo",
                "sourcename": "ParameterTemplateDemo",
                "readrights": "",
                "helpurls": [],
                "parameters": [
                    {
                        "index": 1,
                        "name": "fruits",
                        "required": "",
                        "multi": "",
                        "lowlimit": 50,
                        "highlimit": 500,
                        "limit": 500,
                        "type": "string"
                    }
                ],
                "templatedparameters": [
                    {
                        "index": 2,
                        "name": "{f}-quantity",
                        "templatevars": {
                            "f": "fruits"
                        },
                        "multi": "",
                        "lowlimit": 50,
                        "highlimit": 500,
                        "limit": 500,
                        "type": "integer"
                    }
                ]
            }
        ]
    }
}

Server API implementation

This section aims to aid extension developers who may want to introduce new API's as part of an extension.

Extension addition

Like all extensions, a unique extension directory needs to be added to the MediaWiki's extension directory. In this example, the extension will be named "MyExample".

The LocalSettings.php needs to include a reference to the new extension as:

wfLoadExtension( 'MyExample' );

The extension's JSON file be named extension.json in the root directory of the extension folder (in this case /extensions/ParameterTemplatesDemo/extension.json) and include the name, version, description, APIModules, and AutoloadClasses entries. The references to Special Page and Resource Modules are for client side queries.

{
	"name": "MyExample",
	"version": 1,
	"description": "Example that demonstrates API parameterized templates (both client and server)",
	"type": "other",

	"@APIModules": "Key is the API module name (lowercase, used for the api.php action parameter). Value is the PHP class that implements it.",
	"APIModules": {
		"parameterdemo": "MediaWiki\\Extension\\MyExample\\ApiParameterDemo"
	},
	"MessagesDirs": {
		"MyExample": [
			"i18n"
		]
	},
	"ResourceModules": {
		"ext.MyExample": {
			"packageFiles": [
				"modules/ext.MyExample/demo.js"
			],
			"dependencies": [
				"mediawiki.api",
				"mediawiki.util"
			]
		}
	},
	"AutoloadNamespaces": {
		"MediaWiki\\Extension\\MyExample\\" : "includes/"
	},
	"manifest_version": 1
}

I18N

The server implementation does rely on a valid i18n directory and JSON file existing, but not yet used in this example.

Implementation class

The implementation class must be in the includes directory. The implementation class must implement the interface of ApiBase.php .

<?php

namespace MediaWiki\Extension\MyExample;

class ApiParameterTemplateDemo  extends ApiBase {

    public function getAllowedParams() {
        return array(
            'fruits' => array (
                ApiBase::PARAM_TYPE => 'string',
                ApiBase::PARAM_REQUIRED => true,
                ApiBase::PARAM_ISMULTI => true,
            ),
            '{f}-quantity'=> array(
                ApiBase::PARAM_TYPE => 'integer',
                ApiBase::PARAM_ISMULTI => true,
                // a reference to prior parameter
                ApiBase::PARAM_TEMPLATE_VARS => [ 'f' => 'fruits'],
            )
        );
    }

    public function execute() {
        $result = $this->getResult();

        $requestValues = $this->getRequest()->getQueryValues();
        $fruits = explode('|', $this->getRequest()->getVal('fruits'));
        
        // wfDebug( __METHOD__ . ': fruits = ' . json_encode( $fruits ) );
        
        foreach ($fruits as $fruit) {
            $quantity = $this->getRequest()->getVal($fruit . '-quantity');
            // wfDebug( "$fruit is $quantity" );
            $result->addValue( null, $fruit, $quantity );
        }
     }
}

Testing this class can be accomplished by using the built-in API test module. The URL would be http://machine/wikiprefix/api.php?action=ParameterTemplateDemo&fruits=apples%7Cbananas&apples-quantity=1&bananas-quantity=5

A basically a "Hello World," this response just returns what it was passed in with the query string:

{
    "apples": "1",
    "bananas": "5"
}