[Fleet] Add format query param to package policies API (#160921)

Closes https://github.com/elastic/kibana/issues/155044

## Summary

This PR implements a new `format` query parameter for the package
policies API endpoints that return one or a list of package policies.
This parameter is optional and is allowed the following two values:
* `legacy` (default behaviour): the package policy inputs are formatted
as they are currently, i.e. in an array.
* `simplified`: the package policy inputs are formatted in a key-value
map, which is the new preferred format.

Example:
```
GET kbn:/api/fleet/package_policies -> current format (array) for inputs
GET kbn:/api/fleet/package_policies?format=simplified -> new simplified format (map) for inputs
GET kbn:/api/fleet/package_policies?format=legacy -> current format (array) for inputs
GET kbn:/api/fleet/package_policies?format=foo -> 400 bad request
```

Endpoints with the new `format` query parameter:
* [GET
/package_policies](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#getPackagePolicies)
* [GET
/package_policies/{packagePolicyId}](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#getPackagePolicy)
* [POST
/package_policies](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#createPackagePolicy)
* [POST
/package_policies/_bulk_get](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#bulkGetPackagePolicies)
* [PUT
/package_policies/{packagePolicyId}](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#updatePackagePolicy)

Unaffected endpoints:
* [DELETE
/package_policies/{packagePolicyId}](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#deletePackagePolicy)
* [POST
/package_policies/delete](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#postDeletePackagePolicy)
* [POST
/package_policies/upgrade](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#upgradePackagePolicy)
* [POST
/package_policies/upgrade/dryrun](https://www.elastic.co/guide/en/fleet/master/fleet-apis.html#upgradePackagePolicyDryRun)
(⚠️ TO CHECK)

Note: with this implementation, it is possible in the `POST
/package_policies` and `PUT /package_policies/{packagePolicyId}`
endpoints to provide an inputs list in the deprecated array format and
still get the inputs in the new map format in the response by passing
`?format=simplified`. Conversely, it is possible to pass a map in the
body and get an array in the response.

⚠️ **Important**: as can be seen in the failing CI builds, adding the
`format` query param to `GET /package_policies` caused requests such as
```
GET /api/fleet/package_policies?perPage=10000&page=1&kuery=ingest-package-policies.package.name%3Atomcat
```
to fail with 400 Bad Request as `perPage`, `page` and `kuery` were not
listed as query params in the schema. I've added them, which fixed the
tests, but this might be risky, as I'm not sure whether this is also
needed elsewhere.

### Swagger screenshots

See [latest
commit](7dc8da691f/x-pack/plugins/fleet/common/openapi/bundled.json#/).

![Screenshot 2023-07-07 at 12 19
24](9888437d-4160-4aaa-82ee-5b0a1a2237a5)

![Screenshot 2023-07-07 at 12 19
32](6ab0a91b-d191-410a-be45-ff9c59c20eae)

![Screenshot 2023-07-07 at 12 19
43](608b6ea1-73e0-4481-88ad-3098d159da67)

![Screenshot 2023-07-07 at 12 20
07](9d6e6b6f-1138-42b1-a1e0-8d86db104e55)

![Screenshot 2023-07-07 at 12 20
23](5e02daba-4643-498b-9572-09ddffa51fc3)

### Checklist

Delete any items that are not applicable to this PR.

- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Jill Guyonnet 2023-07-11 10:16:51 +02:00 committed by GitHub
parent c30a7d47eb
commit 557dc27eb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 683 additions and 131 deletions

View file

@ -164,24 +164,30 @@ yarn jest --config x-pack/plugins/fleet/jest.config.js x-pack/plugins/fleet/comm
#### API integration tests
You need to have `docker` to run ingest manager api integration tests
You need to have `docker` to run ingest manager api integration tests.
1. In one terminal, run the tests from the Kibana root directory with
1. In one terminal, run the server from the Kibana root directory with
```
FLEET_PACKAGE_REGISTRY_PORT=12345 yarn test:ftr:server --config x-pack/test/fleet_api_integration/config.ts
FLEET_PACKAGE_REGISTRY_PORT=12345 yarn test:ftr:server --config x-pack/test/fleet_api_integration/<configFile>
```
where `configFile` is the relevant config file relevant from the following:
- config.agent.ts
- config.agent_policy.ts
- config.epm.ts
- config.fleet.ts
- config.package_policy.ts
1. in a second terminal, run the tests from the Kibana root directory with
1. In a second terminal, run the tests from the Kibana root directory with
```
FLEET_PACKAGE_REGISTRY_PORT=12345 yarn test:ftr:runner --config x-pack/test/fleet_api_integration/config.ts
FLEET_PACKAGE_REGISTRY_PORT=12345 yarn test:ftr:runner --config x-pack/test/fleet_api_integration/<configFile>
```
Optionally you can filter which tests you want to run using `--grep`
Optionally, you can filter which tests you want to run using `--grep`
```
FLEET_PACKAGE_REGISTRY_PORT=12345 yarn test:ftr:runner --config x-pack/test/fleet_api_integration/config.ts --grep='fleet'
FLEET_PACKAGE_REGISTRY_PORT=12345 yarn test:ftr:runner --config x-pack/test/fleet_api_integration/<configFile> --grep='fleet'
```
**Note** you can also supply which docker image to use for the package registry via the `FLEET_PACKAGE_REGISTRY_DOCKER_IMAGE` env variable. For example,
@ -192,7 +198,7 @@ FLEET_PACKAGE_REGISTRY_DOCKER_IMAGE='docker.elastic.co/package-registry/distribu
#### Cypress tests
We support testing UI end to end with cypress, you can find more information on how to run those tests [fleet/cypress/README.md](./fleet/cypress/README.md).
We support UI end-to-end testing with Cypress. Refer to [cypress/README.md](./cypress/README.md) for how to run these tests.
#### Jest integration tests

View file

@ -8,3 +8,8 @@
export const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies';
export const PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES = ['auto_configure', 'create_doc'];
export const inputsFormat = {
Simplified: 'simplified',
Legacy: 'legacy',
} as const;

View file

@ -22,18 +22,18 @@ Fleet API docs: https://www.elastic.co/guide/en/fleet/master/fleet-apis.html
It is possible to validate the docs before bundling them with the following command:
```shell
$ npx @redocly/cli lint entrypoint.yaml
npx @redocly/cli lint entrypoint.yaml
```
Then generate the `bundled` files with the following:
```shell
$ npx @redocly/openapi-cli bundle --ext yaml --output bundled.yaml entrypoint.yaml
$ npx @redocly/openapi-cli bundle --ext json --output bundled.json entrypoint.yaml
npx @redocly/openapi-cli bundle --ext yaml --output bundled.yaml entrypoint.yaml
npx @redocly/openapi-cli bundle --ext json --output bundled.json entrypoint.yaml
```
Validate the resulting bundle via
```shell
$ npx @redocly/cli lint bundled.json
npx @redocly/cli lint bundled.json
```

View file

@ -3901,7 +3901,20 @@
},
"operationId": "get-package-policies",
"security": [],
"parameters": []
"parameters": [
{
"$ref": "#/components/parameters/page_size"
},
{
"$ref": "#/components/parameters/page_index"
},
{
"$ref": "#/components/parameters/kuery"
},
{
"$ref": "#/components/parameters/format"
}
]
},
"parameters": [],
"post": {
@ -3949,6 +3962,9 @@
"parameters": [
{
"$ref": "#/components/parameters/kbn_xsrf"
},
{
"$ref": "#/components/parameters/format"
}
]
}
@ -4011,7 +4027,11 @@
},
"operationId": "bulk-get-package-policies",
"security": [],
"parameters": []
"parameters": [
{
"$ref": "#/components/parameters/format"
}
]
}
},
"/package_policies/delete": {
@ -4214,6 +4234,16 @@
}
},
"/package_policies/{packagePolicyId}": {
"parameters": [
{
"schema": {
"type": "string"
},
"name": "packagePolicyId",
"in": "path",
"required": true
}
],
"get": {
"summary": "Get package policy by ID",
"tags": [
@ -4242,18 +4272,13 @@
"$ref": "#/components/responses/error"
}
},
"operationId": "get-package-policy"
"operationId": "get-package-policy",
"parameters": [
{
"$ref": "#/components/parameters/format"
}
]
},
"parameters": [
{
"schema": {
"type": "string"
},
"name": "packagePolicyId",
"in": "path",
"required": true
}
],
"put": {
"summary": "Update package policy by ID",
"tags": [
@ -4299,6 +4324,9 @@
"parameters": [
{
"$ref": "#/components/parameters/kbn_xsrf"
},
{
"$ref": "#/components/parameters/format"
}
]
},
@ -5428,6 +5456,19 @@
"schema": {
"type": "boolean"
}
},
"format": {
"name": "format",
"in": "query",
"description": "Simplified or legacy format for package inputs",
"required": false,
"schema": {
"type": "string",
"enum": [
"simplified",
"legacy"
]
}
}
},
"responses": {
@ -6676,8 +6717,15 @@
"type": "number"
},
"inputs": {
"type": "array",
"items": {}
"oneOf": [
{
"type": "array",
"items": {}
},
{
"type": "object"
}
]
}
},
"required": [

View file

@ -2430,7 +2430,11 @@ paths:
$ref: '#/components/responses/error'
operationId: get-package-policies
security: []
parameters: []
parameters:
- $ref: '#/components/parameters/page_size'
- $ref: '#/components/parameters/page_index'
- $ref: '#/components/parameters/kuery'
- $ref: '#/components/parameters/format'
parameters: []
post:
summary: Create package policy
@ -2463,6 +2467,7 @@ paths:
$ref: '#/components/schemas/package_policy_request'
parameters:
- $ref: '#/components/parameters/kbn_xsrf'
- $ref: '#/components/parameters/format'
/package_policies/_bulk_get:
post:
summary: Bulk get package policies
@ -2501,7 +2506,8 @@ paths:
$ref: '#/components/responses/error'
operationId: bulk-get-package-policies
security: []
parameters: []
parameters:
- $ref: '#/components/parameters/format'
/package_policies/delete:
post:
summary: Delete package policy
@ -2627,6 +2633,12 @@ paths:
'400':
$ref: '#/components/responses/error'
/package_policies/{packagePolicyId}:
parameters:
- schema:
type: string
name: packagePolicyId
in: path
required: true
get:
summary: Get package policy by ID
tags:
@ -2646,12 +2658,8 @@ paths:
'400':
$ref: '#/components/responses/error'
operationId: get-package-policy
parameters:
- schema:
type: string
name: packagePolicyId
in: path
required: true
parameters:
- $ref: '#/components/parameters/format'
put:
summary: Update package policy by ID
tags:
@ -2681,6 +2689,7 @@ paths:
$ref: '#/components/responses/error'
parameters:
- $ref: '#/components/parameters/kbn_xsrf'
- $ref: '#/components/parameters/format'
delete:
summary: Delete package policy by ID
tags:
@ -3392,6 +3401,16 @@ components:
required: false
schema:
type: boolean
format:
name: format
in: query
description: Simplified or legacy format for package inputs
required: false
schema:
type: string
enum:
- simplified
- legacy
responses:
error:
description: Generic Error
@ -4263,8 +4282,10 @@ components:
revision:
type: number
inputs:
type: array
items: {}
oneOf:
- type: array
items: {}
- type: object
required:
- id
- revision

View file

@ -0,0 +1,8 @@
name: format
in: query
description: Simplified or legacy format for package inputs
required: false
schema:
type: string
enum: ['simplified', 'legacy']

View file

@ -7,8 +7,10 @@ allOf:
revision:
type: number
inputs:
type: array
items: {}
oneOf:
- type: array
items: {}
- type: object
required:
- id
- revision

View file

@ -26,7 +26,11 @@ get:
$ref: ../components/responses/error.yaml
operationId: get-package-policies
security: []
parameters: []
parameters:
- $ref: ../components/parameters/page_size.yaml
- $ref: ../components/parameters/page_index.yaml
- $ref: ../components/parameters/kuery.yaml
- $ref: ../components/parameters/format.yaml
parameters: []
post:
summary: Create package policy
@ -57,3 +61,4 @@ post:
$ref: ../components/schemas/package_policy_request.yaml
parameters:
- $ref: ../components/headers/kbn_xsrf.yaml
- $ref: ../components/parameters/format.yaml

View file

@ -35,4 +35,5 @@ post:
$ref: ../components/responses/error.yaml
operationId: bulk-get-package-policies
security: []
parameters: []
parameters:
- $ref: ../components/parameters/format.yaml

View file

@ -1,3 +1,9 @@
parameters:
- schema:
type: string
name: packagePolicyId
in: path
required: true
get:
summary: Get package policy by ID
tags:
@ -17,12 +23,8 @@ get:
'400':
$ref: ../components/responses/error.yaml
operationId: get-package-policy
parameters:
- schema:
type: string
name: packagePolicyId
in: path
required: true
parameters:
- $ref: ../components/parameters/format.yaml
put:
summary: Update package policy by ID
tags:
@ -52,6 +54,7 @@ put:
$ref: ../components/responses/error.yaml
parameters:
- $ref: ../components/headers/kbn_xsrf.yaml
- $ref: ../components/parameters/format.yaml
delete:
summary: Delete package policy by ID
tags:

View file

@ -9,10 +9,12 @@ import type {
NewPackagePolicyInput,
NewPackagePolicyInputStream,
PackagePolicyConfigRecord,
PackagePolicy,
NewPackagePolicy,
PackageInfo,
ExperimentalDataStreamFeature,
} from '../types';
import { DATASET_VAR_NAME } from '../constants';
import { PackagePolicyValidationError } from '../errors';
import { packageToPackagePolicy } from '.';
@ -27,6 +29,15 @@ export type SimplifiedPackagePolicyStreams = Record<
}
>;
export type SimplifiedInputs = Record<
string,
{
enabled?: boolean | undefined;
vars?: SimplifiedVars;
streams?: SimplifiedPackagePolicyStreams;
}
>;
export interface SimplifiedPackagePolicy {
id?: string;
policy_id: string;
@ -34,20 +45,74 @@ export interface SimplifiedPackagePolicy {
name: string;
description?: string;
vars?: SimplifiedVars;
inputs?: Record<
string,
{
enabled?: boolean | undefined;
vars?: SimplifiedVars;
streams?: SimplifiedPackagePolicyStreams;
}
>;
inputs?: SimplifiedInputs;
}
export interface FormattedPackagePolicy extends Omit<PackagePolicy, 'inputs'> {
inputs?: SimplifiedInputs;
}
export interface FormattedCreatePackagePolicyResponse {
item: FormattedPackagePolicy;
}
export function packagePolicyToSimplifiedPackagePolicy(packagePolicy: PackagePolicy) {
const formattedPackagePolicy = packagePolicy as unknown as FormattedPackagePolicy;
formattedPackagePolicy.inputs = formatInputs(packagePolicy.inputs);
return formattedPackagePolicy;
}
export function generateInputId(input: NewPackagePolicyInput) {
return `${input.policy_template ? `${input.policy_template}-` : ''}${input.type}`;
}
export function formatInputs(inputs: NewPackagePolicy['inputs']) {
return inputs.reduce((acc, input) => {
const inputId = generateInputId(input);
if (!acc) {
acc = {};
}
acc[inputId] = {
enabled: input.enabled,
vars: formatVars(input.vars),
streams: formatStreams(input.streams),
};
return acc;
}, {} as SimplifiedPackagePolicy['inputs']);
}
export function formatVars(vars: NewPackagePolicy['inputs'][number]['vars']) {
if (!vars) {
return;
}
return Object.entries(vars).reduce((acc, [varKey, varRecord]) => {
// the dataset var uses an internal format before we send it
if (varKey === DATASET_VAR_NAME && varRecord?.value?.dataset) {
acc[varKey] = varRecord?.value.dataset;
} else {
acc[varKey] = varRecord?.value;
}
return acc;
}, {} as SimplifiedVars);
}
function formatStreams(streams: NewPackagePolicy['inputs'][number]['streams']) {
return streams.reduce((acc, stream) => {
if (!acc) {
acc = {};
}
acc[stream.data_stream.dataset] = {
enabled: stream.enabled,
vars: formatVars(stream.vars),
};
return acc;
}, {} as SimplifiedPackagePolicyStreams);
}
function assignVariables(
userProvidedVars: SimplifiedVars,
varsRecord?: PackagePolicyConfigRecord,

View file

@ -84,7 +84,7 @@ Note that you can select the browser you want to use on the top right side of th
## Folder Structure
### integration/
### e2e/
Cypress convention. Contains the specs that are going to be executed.

View file

@ -7,14 +7,10 @@
import { omit } from 'lodash';
import { DATASET_VAR_NAME } from '../../../../../../common/constants';
import { agentPolicyRouteService, packagePolicyRouteService } from '../../../services';
import { generateInputId } from '../../../../../../common/services/simplified_package_policy_helper';
import type {
SimplifiedPackagePolicy,
SimplifiedVars,
SimplifiedPackagePolicyStreams,
import {
formatInputs,
formatVars,
} from '../../../../../../common/services/simplified_package_policy_helper';
import type {
NewAgentPolicy,
@ -101,53 +97,6 @@ export function generateUpdateAgentPolicyDevToolsRequest(
);
}
function formatVars(vars: NewPackagePolicy['inputs'][number]['vars']) {
if (!vars) {
return;
}
return Object.entries(vars).reduce((acc, [varKey, varRecord]) => {
// the dataset var uses an internal format before we send it
if (varKey === DATASET_VAR_NAME && varRecord?.value?.dataset) {
acc[varKey] = varRecord?.value.dataset;
} else {
acc[varKey] = varRecord?.value;
}
return acc;
}, {} as SimplifiedVars);
}
function formatInputs(inputs: NewPackagePolicy['inputs']) {
return inputs.reduce((acc, input) => {
const inputId = generateInputId(input);
if (!acc) {
acc = {};
}
acc[inputId] = {
enabled: input.enabled,
vars: formatVars(input.vars),
streams: formatStreams(input.streams),
};
return acc;
}, {} as SimplifiedPackagePolicy['inputs']);
}
function formatStreams(streams: NewPackagePolicy['inputs'][number]['streams']) {
return streams.reduce((acc, stream) => {
if (!acc) {
acc = {};
}
acc[stream.data_stream.dataset] = {
enabled: stream.enabled,
vars: formatVars(stream.vars),
};
return acc;
}, {} as SimplifiedPackagePolicyStreams);
}
function formatPackage(pkg: NewPackagePolicy['package']) {
return omit(pkg, 'title');
}

View file

@ -37,18 +37,19 @@ import type {
BulkGetPackagePoliciesRequestSchema,
} from '../../types';
import type {
BulkGetPackagePoliciesResponse,
CreatePackagePolicyResponse,
PostDeletePackagePoliciesResponse,
NewPackagePolicy,
UpgradePackagePolicyDryRunResponse,
UpgradePackagePolicyResponse,
} from '../../../common/types';
import { installationStatuses } from '../../../common/constants';
import { installationStatuses, inputsFormat } from '../../../common/constants';
import { defaultFleetErrorHandler, PackagePolicyNotFoundError } from '../../errors';
import { getInstallations, getPackageInfo } from '../../services/epm/packages';
import { PACKAGES_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../constants';
import { simplifiedPackagePolicytoNewPackagePolicy } from '../../../common/services/simplified_package_policy_helper';
import {
simplifiedPackagePolicytoNewPackagePolicy,
packagePolicyToSimplifiedPackagePolicy,
} from '../../../common/services/simplified_package_policy_helper';
import type { SimplifiedPackagePolicy } from '../../../common/services/simplified_package_policy_helper';
@ -78,7 +79,10 @@ export const getPackagePoliciesHandler: FleetRequestHandler<
// agnostic to package-level RBAC
return response.ok({
body: {
items,
items:
request.query.format === inputsFormat.Simplified
? items.map((item) => packagePolicyToSimplifiedPackagePolicy(item))
: items,
total,
page,
perPage,
@ -91,7 +95,7 @@ export const getPackagePoliciesHandler: FleetRequestHandler<
export const bulkGetPackagePoliciesHandler: FleetRequestHandler<
undefined,
undefined,
TypeOf<typeof BulkGetPackagePoliciesRequestSchema.query>,
TypeOf<typeof BulkGetPackagePoliciesRequestSchema.body>
> = async (context, request, response) => {
const fleetContext = await context.fleet;
@ -103,13 +107,17 @@ export const bulkGetPackagePoliciesHandler: FleetRequestHandler<
const items = await packagePolicyService.getByIDs(soClient, ids, {
ignoreMissing,
});
const responseItems = items ?? [];
const body: BulkGetPackagePoliciesResponse = { items: items ?? [] };
checkAllowedPackages(body.items, limitedToPackages, 'package.name');
checkAllowedPackages(responseItems, limitedToPackages, 'package.name');
return response.ok({
body,
body: {
items:
responseItems.length > 0 && request.query.format === inputsFormat.Simplified
? responseItems.map((item) => packagePolicyToSimplifiedPackagePolicy(item))
: responseItems,
},
});
} catch (error) {
if (error instanceof PackagePolicyNotFoundError) {
@ -123,7 +131,8 @@ export const bulkGetPackagePoliciesHandler: FleetRequestHandler<
};
export const getOnePackagePolicyHandler: FleetRequestHandler<
TypeOf<typeof GetOnePackagePolicyRequestSchema.params>
TypeOf<typeof GetOnePackagePolicyRequestSchema.params>,
TypeOf<typeof GetOnePackagePolicyRequestSchema.query>
> = async (context, request, response) => {
const fleetContext = await context.fleet;
const soClient = fleetContext.internalSoClient;
@ -140,7 +149,10 @@ export const getOnePackagePolicyHandler: FleetRequestHandler<
return response.ok({
body: {
item: packagePolicy,
item:
request.query.format === inputsFormat.Simplified
? packagePolicyToSimplifiedPackagePolicy(packagePolicy)
: packagePolicy,
},
});
} else {
@ -212,7 +224,7 @@ function isSimplifiedCreatePackagePolicyRequest(
export const createPackagePolicyHandler: FleetRequestHandler<
undefined,
undefined,
TypeOf<typeof CreatePackagePolicyRequestSchema.query>,
TypeOf<typeof CreatePackagePolicyRequestSchema.body>
> = async (context, request, response) => {
const coreContext = await context.core;
@ -266,10 +278,13 @@ export const createPackagePolicyHandler: FleetRequestHandler<
request
);
const body: CreatePackagePolicyResponse = { item: packagePolicy };
return response.ok({
body,
body: {
item:
request.query.format === inputsFormat.Simplified
? packagePolicyToSimplifiedPackagePolicy(packagePolicy)
: packagePolicy,
},
});
} catch (error) {
if (error.statusCode) {
@ -284,7 +299,7 @@ export const createPackagePolicyHandler: FleetRequestHandler<
export const updatePackagePolicyHandler: FleetRequestHandler<
TypeOf<typeof UpdatePackagePolicyRequestSchema.params>,
unknown,
TypeOf<typeof UpdatePackagePolicyRequestSchema.query>,
TypeOf<typeof UpdatePackagePolicyRequestSchema.body>
> = async (context, request, response) => {
const coreContext = await context.core;
@ -371,7 +386,12 @@ export const updatePackagePolicyHandler: FleetRequestHandler<
packagePolicy.package?.version
);
return response.ok({
body: { item: updatedPackagePolicy },
body: {
item:
request.query.format === inputsFormat.Simplified
? packagePolicyToSimplifiedPackagePolicy(updatedPackagePolicy)
: updatedPackagePolicy,
},
});
} catch (error) {
return defaultFleetErrorHandler({ error, response });

View file

@ -13,22 +13,40 @@ import {
UpdatePackagePolicyRequestBodySchema,
} from '../models';
import { ListWithKuerySchema, BulkRequestBodySchema } from './common';
import { inputsFormat } from '../../../common/constants';
import { BulkRequestBodySchema } from './common';
export const GetPackagePoliciesRequestSchema = {
query: ListWithKuerySchema.extends({
query: schema.object({
page: schema.number({ defaultValue: 1 }),
perPage: schema.number({ defaultValue: 20 }),
kuery: schema.maybe(schema.string()),
format: schema.maybe(
schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)])
),
withAgentCount: schema.maybe(schema.boolean()),
}),
};
export const BulkGetPackagePoliciesRequestSchema = {
body: BulkRequestBodySchema,
query: schema.object({
format: schema.maybe(
schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)])
),
}),
};
export const GetOnePackagePolicyRequestSchema = {
params: schema.object({
packagePolicyId: schema.string(),
}),
query: schema.object({
format: schema.maybe(
schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)])
),
}),
};
export const CreatePackagePolicyRequestSchema = {
@ -36,6 +54,11 @@ export const CreatePackagePolicyRequestSchema = {
CreatePackagePolicyRequestBodySchema,
SimplifiedCreatePackagePolicyRequestBodySchema,
]),
query: schema.object({
format: schema.maybe(
schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)])
),
}),
};
export const UpdatePackagePolicyRequestSchema = {
@ -44,6 +67,11 @@ export const UpdatePackagePolicyRequestSchema = {
UpdatePackagePolicyRequestBodySchema,
SimplifiedCreatePackagePolicyRequestBodySchema,
]),
query: schema.object({
format: schema.maybe(
schema.oneOf([schema.literal(inputsFormat.Simplified), schema.literal(inputsFormat.Legacy)])
),
}),
};
export const DeletePackagePoliciesRequestSchema = {

View file

@ -472,6 +472,89 @@ export default function (providerContext: FtrProviderContext) {
.expect(200);
});
it('should return 200 and formatted inputs when the format=simplified query param is passed', async function () {
const { body } = await supertest
.post(`/api/fleet/package_policies?format=simplified`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'filetest-3',
description: '',
namespace: 'default',
policy_id: agentPolicyId,
enabled: true,
inputs: [
{
enabled: true,
streams: [],
type: 'single_input',
},
],
package: {
name: 'filetest',
title: 'For File Tests',
version: '0.1.0',
},
})
.expect(200);
expect(body.item.inputs).to.eql({ single_input: { enabled: true, streams: {} } });
});
it('should return 200 and arrayed inputs when the format=legacy query param is passed', async function () {
const inputs = [
{
enabled: true,
streams: [],
type: 'single_input',
},
];
const { body } = await supertest
.post(`/api/fleet/package_policies?format=legacy`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'filetest-4',
description: '',
namespace: 'default',
policy_id: agentPolicyId,
enabled: true,
inputs,
package: {
name: 'filetest',
title: 'For File Tests',
version: '0.1.0',
},
})
.expect(200);
expect(body.item.inputs).to.eql(inputs);
});
it('should return 400 if an invalid format query param is passed', async function () {
await supertest
.post(`/api/fleet/package_policies?format=foo`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'filetest-2',
description: '',
namespace: 'default',
policy_id: agentPolicyId,
enabled: true,
inputs: [
{
enabled: true,
streams: [],
type: 'single_input',
},
],
package: {
name: 'filetest',
title: 'For File Tests',
version: '0.1.0',
},
})
.expect(400);
});
describe('input only packages', () => {
it('should default dataset if not provided for input only pkg', async function () {
await supertest
@ -671,6 +754,98 @@ export default function (providerContext: FtrProviderContext) {
})
.expect(400);
});
it('should return 200 and formatted inputs when the format=simplified query param is passed', async () => {
const { body } = await supertest
.post(`/api/fleet/package_policies?format=simplified`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'create-simplified-package-policy-required-variables-1',
description: '',
namespace: 'default',
policy_id: agentPolicyId,
inputs: {
'with_required_variables-test_input': {
streams: {
'with_required_variables.log': {
vars: { test_var_required: 'I am required' },
},
},
},
},
package: {
name: 'with_required_variables',
version: '0.1.0',
},
})
.expect(200);
expect(body.item.inputs).to.eql({
'with_required_variables-test_input': {
enabled: true,
streams: {
'with_required_variables.log': {
enabled: true,
vars: { test_var_required: 'I am required' },
},
},
},
});
});
it('should return 200 and arrayed inputs when the format=legacy query param is passed', async () => {
const { body } = await supertest
.post(`/api/fleet/package_policies?format=legacy`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'create-simplified-package-policy-required-variables-2',
description: '',
namespace: 'default',
policy_id: agentPolicyId,
inputs: {
'with_required_variables-test_input': {
streams: {
'with_required_variables.log': {
vars: { test_var_required: 'I am required' },
},
},
},
},
package: {
name: 'with_required_variables',
version: '0.1.0',
},
})
.expect(200);
expect(Array.isArray(body.item.inputs));
});
it('should return 400 if an invalid format query param is passed', async function () {
await supertest
.post(`/api/fleet/package_policies?format=foo`)
.set('kbn-xsrf', 'xxxx')
.send({
name: `create-simplified-package-policy-required-variables-${Date.now()}`,
description: '',
namespace: 'default',
policy_id: agentPolicyId,
inputs: {
'with_required_variables-test_input': {
streams: {
'with_required_variables.log': {
vars: { test_var_required: 'I am required' },
},
},
},
},
package: {
name: 'with_required_variables',
version: '0.1.0',
},
})
.expect(400);
});
});
describe('Package verification', () => {

View file

@ -152,6 +152,32 @@ export default function (providerContext: FtrProviderContext) {
it('should return a 404 with an invalid id', async function () {
await supertest.get(`/api/fleet/package_policies/IS_NOT_PRESENT`).expect(404);
});
it('should succeed and return formatted inputs when the format=simplified query param is passed', async function () {
const {
body: { item },
} = await supertest
.get(`/api/fleet/package_policies/${packagePolicyId}?format=simplified`)
.expect(200);
expect(Array.isArray(item.inputs)).to.be(false);
});
it('should succeed and return arrayed inputs when the format=legacy query param is passed', async function () {
const {
body: { item },
} = await supertest
.get(`/api/fleet/package_policies/${packagePolicyId}?format=legacy`)
.expect(200);
expect(Array.isArray(item.inputs));
});
it('should return 400 if an invalid format query param is passed', async function () {
await supertest
.get(`/api/fleet/package_policies/${packagePolicyId}?format=foo`)
.expect(400);
});
});
describe('POST /api/fleet/package_policies/_bulk_get', async function () {
@ -246,9 +272,10 @@ export default function (providerContext: FtrProviderContext) {
.expect(200);
expect(items.length).eql(1);
expect(Array.isArray(items[0]));
});
it('should return 404 with invvalid ids', async function () {
it('should return 404 with invalid ids', async function () {
await supertest
.post(`/api/fleet/package_policies/_bulk_get`)
.set('kbn-xsrf', 'xxxx')
@ -295,6 +322,40 @@ export default function (providerContext: FtrProviderContext) {
expect(items.length).eql(1);
});
it('should succeed and return formatted inputs when the format=simplified query param is passed', async function () {
const {
body: { items },
} = await supertest
.post(`/api/fleet/package_policies/_bulk_get?format=simplified`)
.set('kbn-xsrf', 'xxxx')
.send({ ids: [packagePolicyId] })
.expect(200);
expect(items.length).eql(1);
expect(Array.isArray(items[0])).to.be(false);
});
it('should succeed and return arrayed inputs when the format=legacy query param is passed', async function () {
const {
body: { items },
} = await supertest
.post(`/api/fleet/package_policies/_bulk_get?format=legacy`)
.set('kbn-xsrf', 'xxxx')
.send({ ids: [packagePolicyId] })
.expect(200);
expect(items.length).eql(1);
expect(Array.isArray(items[0]));
});
it('should return 400 if an invalid format query param is passed', async function () {
await supertest
.post(`/api/fleet/package_policies/_bulk_get?format=foo`)
.set('kbn-xsrf', 'xxxx')
.send({ ids: [packagePolicyId] })
.expect(400);
});
});
describe('get orphaned policies', () => {

View file

@ -453,6 +453,74 @@ export default function (providerContext: FtrProviderContext) {
});
});
it('should succeed and return formatted inputs when the format=simplified query param is passed', async function () {
const {
body: { item },
} = await supertest
.put(`/api/fleet/package_policies/${packagePolicyId}?format=simplified`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'filetest-1',
description: '',
namespace: 'updated_namespace',
policy_id: agentPolicyId,
enabled: true,
inputs: [],
package: {
name: 'filetest',
title: 'For File Tests',
version: '0.1.0',
},
})
.expect(200);
expect(Array.isArray(item.inputs)).to.be(false);
});
it('should succeed and return arrayed inputs when the format=legacy query param is passed', async function () {
const {
body: { item },
} = await supertest
.put(`/api/fleet/package_policies/${packagePolicyId}?format=legacy`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'filetest-1',
description: '',
namespace: 'updated_namespace',
policy_id: agentPolicyId,
enabled: true,
inputs: [],
package: {
name: 'filetest',
title: 'For File Tests',
version: '0.1.0',
},
})
.expect(200);
expect(Array.isArray(item.inputs));
});
it('should return 400 if an invalid format query param is passed', async function () {
await supertest
.put(`/api/fleet/package_policies/${packagePolicyId}?format=foo`)
.set('kbn-xsrf', 'xxxx')
.send({
name: 'filetest-1',
description: '',
namespace: 'updated_namespace',
policy_id: agentPolicyId,
enabled: true,
inputs: [],
package: {
name: 'filetest',
title: 'For File Tests',
version: '0.1.0',
},
})
.expect(400);
});
describe('Simplified package policy', async () => {
it('should work with valid values', async function () {
await supertest
@ -514,6 +582,7 @@ export default function (providerContext: FtrProviderContext) {
'Package policy namespace cannot be modified for input only packages, please create a new package policy.'
);
});
it('should return a 400 if dataset is edited on input only package policy', async function () {
const updatedPolicy = JSON.parse(JSON.stringify(inputOnlyBasePackagePolicy));
@ -528,6 +597,92 @@ export default function (providerContext: FtrProviderContext) {
'Package policy dataset cannot be modified for input only packages, please create a new package policy.'
);
});
it('should succeed and return formatted inputs when the format=simplified query param is passed', async function () {
const {
body: { item },
} = await supertest
.put(`/api/fleet/package_policies/${packagePolicyId}?format=simplified`)
.set('kbn-xsrf', 'xxxx')
.send({
name: `update-simplified-package-policy-with_required_variables-${Date.now()}`,
description: '',
namespace: 'default',
policy_id: agentPolicyId,
inputs: {
'with_required_variables-test_input': {
streams: {
'with_required_variables.log': {
vars: { test_var_required: 'I am required' },
},
},
},
},
package: {
name: 'with_required_variables',
version: '0.1.0',
},
})
.expect(200);
expect(Array.isArray(item.inputs)).to.be(false);
});
it('should succeed and return arrayed inputs when the format=legacy query param is passed', async function () {
const {
body: { item },
} = await supertest
.put(`/api/fleet/package_policies/${packagePolicyId}?format=legacy`)
.set('kbn-xsrf', 'xxxx')
.send({
name: `update-simplified-package-policy-with_required_variables-${Date.now()}`,
description: '',
namespace: 'default',
policy_id: agentPolicyId,
inputs: {
'with_required_variables-test_input': {
streams: {
'with_required_variables.log': {
vars: { test_var_required: 'I am required' },
},
},
},
},
package: {
name: 'with_required_variables',
version: '0.1.0',
},
})
.expect(200);
expect(Array.isArray(item.inputs));
});
it('should return 400 if an invalid format query param is passed', async function () {
await supertest
.put(`/api/fleet/package_policies/${packagePolicyId}?format=foo`)
.set('kbn-xsrf', 'xxxx')
.send({
name: `update-simplified-package-policy-with_required_variables-${Date.now()}`,
description: '',
namespace: 'default',
policy_id: agentPolicyId,
inputs: {
'with_required_variables-test_input': {
streams: {
'with_required_variables.log': {
vars: { test_var_required: 'I am required' },
},
},
},
},
package: {
name: 'with_required_variables',
version: '0.1.0',
},
})
.expect(400);
});
});
describe('Input Packages', () => {