# Adding Built-in Functions

[Built-in Functions](/docs/policy-reference#built-in-functions) can be added inside the `topdown` package.

Built-in functions may be upstreamed if they are generally useful and provide functionality that would be impractical to implement natively in Rego (e.g., CIDR arithmetic). Implementations should avoid third-party dependencies. If absolutely necessary, consider importing the code manually into the `internal` package.

info

Read more about extending OPA with custom built-in functions in go [in the Extensions documentation](/docs/extensions#custom-built-in-functions-in-go).

Adding a new built-in function involves the following steps:

1.  [Declare and register](#declare-and-register) the function
2.  [Implement](#implement) the function
3.  [Test](#test) the function
4.  [Document](#document) the function
5.  [Add a capability](#add-a-capability) for the function

## Example

The following example adds a simple built-in function, `repeat(string, int)`, that returns a given string repeated a given number of times.

### Declare and Register

In `ast/builtins.go`, we declare the structure of our built-in function with a `Builtin` struct instance:

```
var Repeat = &Builtin{    Name:        "repeat", // The name of the function    Description: "Returns, as a string, the given string repeated the given number of times.",    Decl: types.NewFunction(        types.Args( // The built-in takes two arguments, where ..            types.Named("str", types.S).Description("string to repeat"),            // named string argument            types.Named("count", types.N).Description("how often to repeat `str`"), // named number argument        ),        types.Named("output", types.S).Description("the repetitions"), // The return type is a string.    ),  Categories: category("strings"), // the category the built-in belongs to}
```

To register the new built-in function, we locate the `DefaultBuiltins` array in `ast/builtins.go`, and add the `Builtin` instance to it:

```
var DefaultBuiltins = [...]*Builtin{    ...    Repeat,    ...}
```

### Implement

In the `topdown` package, we locate a suitable source file for our new built-in function, or add a new file, as appropriate.

In this example, we introduce a new source file, `topdown/repeat.go`:

```
package topdownimport (    "fmt"    "strings"    "github.com/open-policy-agent/opa/v1/ast"    "github.com/open-policy-agent/opa/v1/topdown/builtins")// implements topdown.BuiltinFuncfunc builtinRepeat(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {    // Get the first argument as a string, returning an error if it's not the correct type.    str, err := builtins.StringOperand(operands[0].Value, 1)    if err != nil {        return err    }    // Get the second argument as an int, returning an error if it's not the correct type or not a positive value.    count, err := builtins.IntOperand(operands[1].Value, 2)    if err != nil {        return err    } else if count < 0 {        // Defensive check, strings.Repeat(...) will panic for count<0        return fmt.Errorf("count must be a positive integer")    }    // Return a string by invoking the given iterator function    return iter(ast.StringTerm(strings.Repeat(string(str), count)))}func init() {    RegisterBuiltinFunc(ast.Repeat.Name, builtinRepeat)}
```

In the above code, `builtinRepeat` implements the `topdown.BuiltinFunc` function type. The call to `RegisterBuiltinFunc(...)` in `init()` adds the built-in function to the evaluation engine; binding the implementation to `ast.Repeat` that was registered in [an earlier step](#declare-and-register).

### Test

All built-in function implementations must include a test suite. Test cases for built-in functions are written in YAML and located under `test/cases/testdata/v1`.

We create two new test cases (one positive, expecting a string output; and one negative, expecting an error) for our built-in function:

```
cases:- note: repeat/positive  query: data.test.p = x  modules:  - |      package test      p := repeated if {        repeated := repeat(input.str, input.count)      }  input: { "str": "Foo", "count": 3 }  want_result:  - x: FooFooFoo- note: repeat/negative  query: data.test.p = x  modules:  - |      package test      p := repeated if {        repeated := repeat(input.str, input.count)      }  input: { "str": "Foo", "count": -3 }  strict_error: true  want_error_code: eval_builtin_error  want_error: "repeat: count must be a positive integer"
```

The above test cases can be run separate from all other tests through: `go test ./topdown -v -run 'TestRego/v1/repeat'`

See [test/cases/testdata/helloworld](https://github.com/open-policy-agent/opa/tree/main/v1/test/cases/testdata/v0/helloworld) for a more detailed example of how to implement tests for your built-in functions.

info

Note: We can manually test our new built-in function by [building](/docs/contrib-development#getting-started) and running the `eval` command. E.g.: `$./opa_<OS>_<ARCH> eval 'repeat("Foo", 3)'`

### Document

All built-in functions will automatically be documented in `docs/content/policy-reference.md` under an appropriate subsection.

For this example, we'll get an entry for our new function under the `Strings` section.

### Add a capability

info

Read more about extending the default capabilities list for built-ins [in the Operations documentation](/docs/operations#built-ins).

One of the security features of OPA is [capabilities](/docs/operations#capabilities) checks on policies, allowing users to restrict which built-in functions will be available to policies at runtime. To ensure that our new `repeat` function will be available to callers, we'll need to add it to the `capabilities.json` file at the root of the repo. We can have this entry auto-generated for us by running `make generate`.

After running `make generate` we should see a new JSON object entry in the list under the `"builtins"` key:

```
...{  "name": "repeat",  "decl": {    "args": [      {        "type": "string"      },      {        "type": "number"      }    ],    "result": {      "type": "string"    },    "type": "function"  }},...
```