diff --git a/ProcessMaker/Bpmn/MustacheOptions.php b/ProcessMaker/Bpmn/MustacheOptions.php index 846ba30195..a3d0507b0c 100644 --- a/ProcessMaker/Bpmn/MustacheOptions.php +++ b/ProcessMaker/Bpmn/MustacheOptions.php @@ -18,36 +18,76 @@ public function __construct() 'json' => [$this, 'json'], 'serialize' => [$this, 'serialize'], 'xml' => [$this, 'xml'], + 'urlencode' => [$this, 'urlencode'], ]; } - public function html64($text, Mustache_LambdaHelper $helper) + public function html64($text, ?Mustache_LambdaHelper $helper = null) { + if (!$helper) { + // Support for PRAGMA_FILTERS + return base64_encode('' . $text . ''); + } return base64_encode('' . $helper->render($text) . ''); } - public function base64($text, Mustache_LambdaHelper $helper) + public function base64($text, ?Mustache_LambdaHelper $helper = null) { + if (!$helper) { + // Support for PRAGMA_FILTERS + return base64_encode($text); + } return base64_encode($helper->render($text)); } - public function key($text, Mustache_LambdaHelper $helper) + public function key($text, ?Mustache_LambdaHelper $helper = null) { + if (!$helper) { + // Support for PRAGMA_FILTERS + return urlencode(Hash::make($text)); + } return urlencode(Hash::make($helper->render($text))); } - public function json($text, Mustache_LambdaHelper $helper) + public function json($text, ?Mustache_LambdaHelper $helper = null) { + if (!$helper) { + // Support for PRAGMA_FILTERS + return json_encode($text); + } return json_encode($helper->render($text)); } - public function serialize($text, Mustache_LambdaHelper $helper) + public function serialize($text, ?Mustache_LambdaHelper $helper = null) { + if (!$helper) { + // Support for PRAGMA_FILTERS + return serialize($text); + } return serialize($helper->render($text)); } - public function xml($text, Mustache_LambdaHelper $helper) + public function xml($text, ?Mustache_LambdaHelper $helper = null) { + if (!$helper) { + // Support for PRAGMA_FILTERS + return xmlrpc_encode($text); + } return xmlrpc_encode($helper->render($text)); } + + /** + * URL encode a string + * + * @param string $text + * @param Mustache_LambdaHelper|null $helper + * @return string + */ + public function urlencode($text, ?Mustache_LambdaHelper $helper = null) + { + if (!$helper) { + return urlencode($text); + } + return urlencode($helper->render($text)); + } } diff --git a/ProcessMaker/Console/Commands/Api2TypescriptCommand.php b/ProcessMaker/Console/Commands/Api2TypescriptCommand.php new file mode 100644 index 0000000000..9a46e98138 --- /dev/null +++ b/ProcessMaker/Console/Commands/Api2TypescriptCommand.php @@ -0,0 +1,950 @@ +files = $files; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + $outputDirectory = $this->argument('output'); + + // update the api-docs.json file + Artisan::call('l5-swagger:generate'); + + // Parse JSON file + $openapi = $this->parseJsonFile(); + if (!$openapi) { + $this->error("Failed to parse OpenAPI JSON file."); + return 1; + } + $this->openapi = $openapi; + + // Create output directory if it doesn't exist + if (!$this->files->exists($outputDirectory)) { + $this->files->makeDirectory($outputDirectory, 0755, true); + } + + // Create subdirectories + $typesDir = "$outputDirectory/types"; + $apiDir = "$outputDirectory/api"; + $composablesDir = "$outputDirectory/composables"; + + foreach ([$typesDir, $apiDir, $composablesDir] as $dir) { + if (!$this->files->exists($dir)) { + $this->files->makeDirectory($dir, 0755, true); + } + } + + // Extract API info + $apiTitle = $openapi['info']['title'] ?? 'API'; + $apiDescription = $openapi['info']['description'] ?? 'API Description'; + $apiVersion = $openapi['info']['version'] ?? '1.0.0'; + + // Extract tag names + $tags = []; + foreach ($openapi['paths'] as $path => $methods) { + foreach ($methods as $method => $details) { + if (isset($details['tags']) && is_array($details['tags'])) { + foreach ($details['tags'] as $tag) { + $tags[$tag] = true; + } + } + } + } + $tags = array_keys($tags); + // sort tags alphabetically ascending + sort($tags, SORT_FLAG_CASE | SORT_STRING); + + $this->generateTypesForTag($openapi, 'types', $typesDir); + // Process each tag as a separate API client + foreach ($tags as $tag) { + $this->generateApiClassForTag($openapi, $tag, $apiDir); + $this->generateComposableForTag($tag, $composablesDir); + } + + // Generate index files + $this->generateIndexFiles($tags, $apiDir, $composablesDir, $typesDir, $outputDirectory); + + $this->info("TypeScript SDK generated successfully in $outputDirectory"); + return 0; + } + + /** + * Parse a JSON file + */ + protected function parseJsonFile() + { + $content = file_get_contents(storage_path('api-docs/api-docs.json')); + $data = json_decode($content, true); + if (json_last_error() !== JSON_ERROR_NONE) { + $this->error("JSON parse error: " . json_last_error_msg()); + return null; + } + + return $data; + } + + /** + * Map OpenAPI type to TypeScript type + */ + protected function mapSwaggerTypeToTypescript($type) + { + switch ($type) { + case 'integer': + return 'number'; + case 'number': + return 'number'; + case 'boolean': + return 'boolean'; + case 'array': + return 'any[]'; + case 'object': + return 'Record'; + default: + return 'string'; + } + } + + /** + * Generate TypeScript interfaces for schemas related to a tag + */ + protected function generateTypesForTag($openapi, $tag, $outputDir) + { + $tagLower = strtolower($tag); + $interfaces = []; + $queryParamInterfaces = []; + + // Get all schemas from components + $schemas = $openapi['components']['schemas'] ?? []; + + // Process paths to find related schemas and generate query param interfaces + foreach ($openapi['paths'] as $path => $methods) { + foreach ($methods as $method => $details) { + // Generate query param interfaces for GET methods + if (isset($details['parameters'])) { + $queryParamInterface = $this->generateQueryParamInterface($details['parameters'], $tagLower, $details['operationId']); + if ($queryParamInterface) { + $queryParamInterfaces[] = $queryParamInterface; + } + } + + // Check request body schema reference + if (isset($details['requestBody']['content']['application/json']['schema']['$ref'])) { + $schemaRef = $details['requestBody']['content']['application/json']['schema']['$ref']; + $this->collectSchemaFromRef($schemaRef, $schemas); + } + + // Generate response type for operationId + $responseSchema = $details['responses']['200']['content']['application/json']['schema'] ?? + $details['responses']['201']['content']['application/json']['schema'] ?? null; + if ($responseSchema && isset($responseSchema['properties'])) { + $queryParamInterfaces[] = $this->generateResponseType($details['operationId'], $responseSchema, $tagLower); + } + + // Check response schema references + foreach ($details['responses'] ?? [] as $response) { + if (isset($response['content']['application/json']['schema']['$ref'])) { + $schemaRef = $response['content']['application/json']['schema']['$ref']; + $this->collectSchemaFromRef($schemaRef, $schemas); + } + + // Check for arrays with items having refs + if (isset($response['content']['application/json']['schema']['properties']['data']['items']['$ref'])) { + $schemaRef = $response['content']['application/json']['schema']['properties']['data']['items']['$ref']; + $this->collectSchemaFromRef($schemaRef, $schemas); + } + } + } + } + + // Generate TypeScript interfaces from schemas + foreach ($schemas as $name => $schema) { + $interfaces[] = $this->generateInterface($name, $schema); + } + + // Write interfaces to file + $data = [ + 'interfaces' => array_filter($interfaces), + 'queryParamInterfaces' => array_filter($queryParamInterfaces), + ]; + + $content = Blade::render($this->getStub('types'), $data); + $this->files->put("$outputDir/types.ts", $content); + } + + /** + * Generate TypeScript API class for a tag + */ + protected function generateApiClassForTag($openapi, $tag, $outputDir) + { + $tagLower = lcfirst(Str::camel($tag)); + $className = "ProcessMaker" . ucfirst($tagLower) . "Api"; + + // Import interfaces + $imports = []; + + // Add query param interfaces to imports + foreach ($openapi['paths'] as $path => $methods) { + foreach ($methods as $method => $details) { + if (!isset($details['tags']) || !in_array($tag, $details['tags'])) { + continue; + } + + $operationId = $details['operationId'] ?? ''; + if ($operationId) { + if (isset($details['parameters'])) { + // Import the query param interface + $interfaceName = ucfirst($this->camelCase($operationId)) . "QueryParams"; + $interfaceCode = $this->generateQueryParamInterface($details['parameters'], $tagLower, $details['operationId']); + if ($interfaceCode) { + $imports[] = $interfaceName; + } + } + + // Import the paginated response type and its type + $reference = $this->getResponseReference($operationId, $details['responses']); + if ($reference) { + $imports[] = $reference; + } + } + } + } + + // Generate methods for each path + $methods = []; + foreach ($openapi['paths'] as $path => $pathMethods) { + foreach ($pathMethods as $method => $details) { + // Skip if not related to current tag + if (!isset($details['tags']) || !in_array($tag, $details['tags'])) { + continue; + } + + $operationId = $details['operationId'] ?? ''; + if ($operationId) { + $methods[] = $this->generateMethod($method, $path, $details, $imports); + } + } + } + + $imports = array_unique($imports); + // sort imports alphabetically ascending + sort($imports, SORT_FLAG_CASE | SORT_STRING); + // sort methods alphabetically ascending by methodName + usort($methods, function($a, $b) { + return strcasecmp($a['methodName'], $b['methodName']); + }); + $data = [ + 'tagLower' => $tagLower, + 'className' => $className, + 'imports' => $imports, + 'methods' => $methods, + 'helper' => $this, + ]; + + // Generate the api.ts file + $content = Blade::render($this->getStub('api'), $data); + $this->files->put("$outputDir/$tagLower.api.ts", $content); + + // Generate the api.spec.ts file + $content = Blade::render($this->getStub('api-spec'), $data); + $this->files->put("$outputDir/$tagLower.api.spec.ts", $content); + } + + public function mockResponse(array $method) + { + return $method['responseExample']; + } + + public function mockParamsArray(array $method, bool $camelCase): array + { + $params = []; + // Add path parameters + foreach ($method['pathParams'] as $param) { + $key = Str::camel($param['name']); + $params[$key] = $this->mockValue($param); + } + // Add request body parameter for POST/PUT methods + if (!empty($method['requestBody'])) { + if (isset($method['requestBody']['content']['multipart/form-data']['schema'])) { + $params['$body'] = $this->mockFromSchema($method['requestBody']['content']['multipart/form-data']['schema']); + } elseif (isset($method['requestBody']['content']['application/json']['schema'])) { + $schema = $method['requestBody']['content']['application/json']['schema']; + $params['$body'] = $this->mockFromSchema($schema); + } else { + throw new Exception("Failed to mock request body for " . $method['operationId']); + } + } elseif ($method['httpMethod'] === 'post' || $method['httpMethod'] === 'put' || $method['httpMethod'] === 'patch') { + $params['$body'] = (object) []; + } + $queryParams = []; + foreach ($method['queryParams'] as $param) { + $key = $camelCase ? Str::camel($param['name']) : $param['name']; + if (isset($param['$ref'])) { + $queryParams[$key] = $this->mockValue($this->getSchemaByRef($param['$ref'])); + } else { + $queryParams[$key] = $this->mockValue($param); + } + } + if (!empty($queryParams)) { + $params['$queryParams'] = $queryParams; + } + return $params; + } + + public function mockParams(array $method) + { + $params = $this->mockParamsArray($method, true); + foreach ($params as $key => $value) { + $params[$key] = $this->json($value, 6); + } + return implode(', ', $params); + } + + private function getSchemaByRef(string $ref) + { + $refs = str_replace('/', '.', substr($ref, 2)); + return Arr::get($this->openapi, $refs, null); + } + + public function mockUrl(array $method) + { + $params = $this->mockParamsArray($method, false); + // replace ${param} with the value + $path = $method['apiPath']; + foreach ($params as $key => $value) { + if (is_array($value) || is_object($value)) { + continue; + } + $path = str_replace('${' . $key . '}', urlencode($value), $path); + } + // Add the query params to the path from $queryParams + if (isset($params['$queryParams'])) { + $first = true; + foreach ($params['$queryParams'] as $key => $value) { + if (is_array($value)) { + $value = implode(',', $value); + } + if (is_object($value)) { + $value = json_encode($value); + } + $path .= ($first ? '?' : '&') . urlencode($key) . '=' . urlencode($value); + $first = false; + } + } + $arguments = "'{$path}'"; + if (isset($params['$body'])) { + $arguments .= ", " . $this->json($params['$body'], 6); + } + return $arguments; + } + + public function json($value, int $leftMargin = 0) + { + if ($leftMargin > 0) { + $json = json_encode($value, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + // change the indent to 2 spaces + $json = preg_replace_callback( + '/^(?: {4})+/m', + function($m) { + return str_repeat(' ', 2 * (strlen($m[0]) / 4)); + }, + $json + ); + // add the left margin + $json = str_replace("\n", "\n" . str_repeat(' ', $leftMargin), $json); + return $json; + } + return json_encode($value, JSON_UNESCAPED_SLASHES); + } + + private function getResponseReference(string $operationId, array $responses) + { + $responseSchema = $responses['200']['content']['application/json']['schema'] ?? + $responses['201']['content']['application/json']['schema'] ?? + $responses['202']['content']['application/json']['schema'] ?? + $responses['203']['content']['application/json']['schema'] ?? + $responses['204']['content']['application/json']['schema'] ?? null; + + if (!$responseSchema) { + return null; + } + if ($responseSchema && isset($responseSchema['properties'])) { + $interfaceName = ucfirst($this->camelCase($operationId)) . "Response"; + return $interfaceName; + } elseif ($responseSchema) { + return $this->getSchemaNameFromRef($responseSchema['$ref']); + } + + throw new \Exception("Failed to find response schema for $operationId"); + } + + private function getResponseExample(string $operationId, array $responses) + { + $responseSchema = $responses['200']['content']['application/json']['schema'] ?? + $responses['201']['content']['application/json']['schema'] ?? + $responses['202']['content']['application/json']['schema'] ?? null; + + if (empty($responseSchema)) { + return (object) []; + } + + return $this->mockFromSchema($responseSchema); + } + + private function mockFromSchema(array $responseSchema) + { + $response = []; + if (isset($responseSchema['properties'])) { + foreach ($responseSchema['properties'] as $key => $value) { + if (empty($value)) { + throw new Exception("Empty value for " . $key); + } + $response[$key] = $this->mockValue($value); + } + } elseif (isset($responseSchema['$ref'])) { + return $this->mockFromSchema($this->getSchemaByRef($responseSchema['$ref'])); + } + return (object) $response; + } + + private function mockValue(array $value) + { + if (!isset($value['type']) && isset($value['schema'])) { + $value = $value['schema']; + } + if (!isset($value['type']) && isset($value['allOf'])) { + return $this->mockValueFromObject($value); + } + if (!isset($value['type']) && isset($value['$ref'])) { + return $this->mockValueFromObject($value); + } + if (!isset($value['type'])) { + throw new \Exception("Failed to mock value for " . json_encode($value)); + } + switch ($value['type']) { + case 'string': + switch ($value['format'] ?? null) { + case 'id': + return '1'; + case 'date-time': + return '2025-04-23T00:00:00Z'; + default: + return 'foo'; + } + case 'integer': + case 'number': + return 1; + case 'boolean': + return true; + case 'array': + if (isset($value['items']['$ref'])) { + return [$this->mockValue($this->getSchemaByRef($value['items']['$ref']))]; + $ref = explode('/', $value['items']['$ref']); + $ref = end($ref); + return [$this->mockValue($this->openapi['components']['schemas'][$ref])]; + } + return [1, 2, 3]; + case 'object': + return $this->mockValueFromObject($value); + default: + return $value; + } + } + + private function mockValueFromObject(array $value) + { + $withProperties = $this->findItemWithKeyInArray($value['allOf'] ?? [], 'properties'); + $withRef = $this->findItemWithKeyInArray($value['allOf'] ?? [], '$ref'); + if (isset($value['allOf']) && $withProperties) { + return $this->mockFromSchema($withProperties); + } elseif (isset($value['allOf']) && $withRef) { + $ref = explode('/', $withRef['$ref']); + $ref = end($ref); + return $this->mockValue($this->openapi['components']['schemas'][$ref]); + } elseif (isset($value['properties'])) { + return $this->mockFromSchema($value); + } + return (object) ['foo' => $value]; + } + + private function findItemWithKeyInArray(array $array, string $key) + { + foreach ($array as $item) { + if (isset($item[$key])) { + return $item; + } + } + return null; + } + + private function findResponseSchema(string $operationId, array $responseSchema, array $imports) + { + $responseProperties = $responseSchema['properties'] ?? null; + if ($responseProperties) { + $refs = $this->findRefs($responseProperties); + foreach ($refs as $ref) { + $imports[] = $this->getSchemaNameFromRef($ref); + } + } elseif (isset($responseSchema['$ref'])) { + $imports[] = $this->getSchemaNameFromRef($responseSchema['$ref']); + } else { + throw new \Exception("Failed to find response schema for $operationId"); + } + + return $imports; + } + + private function findRefs(array $schema) + { + $refs = []; + foreach ($schema as $key => $value) { + if (isset($value['$ref'])) { + $refs[] = $value['$ref']; + } + if (is_array($value)) { + $refs = array_merge($refs, $this->findRefs($value)); + } + } + + return $refs; + } + + /** + * Generate a TypeScript method for an API endpoint + */ + protected function generateMethod($httpMethod, $path, $details, &$imports) + { + $operationId = $details['operationId']; + $methodName = $this->camelCase($operationId); + $summary = $details['summary'] ?? ''; + $parameters = $details['parameters'] ?? []; + $requestBody = $details['requestBody'] ?? null; + $responses = $details['responses'] ?? []; + + $parameters = $this->filterValidParameters($parameters); + // Determine method signature based on parameters and request body + $paramList = []; + $pathParams = []; + $queryParams = []; + + // Process path parameters + foreach ($parameters as $param) { + if ($param['in'] === 'path') { + $pathParams[] = $param; + $paramType = $this->mapSwaggerTypeToTypescript($param['schema']['type'] ?? 'string'); + $paramList[] = $this->camelCase($param['name']) . ": " . $paramType; + } elseif ($param['in'] === 'query') { + $queryParams[] = $param; + } + } + + // Add request body parameter for POST/PUT methods + if (($httpMethod === 'post' || $httpMethod === 'put' || $httpMethod === 'patch')) { + if (isset($requestBody['content']['application/json']['schema']['$ref'])) { + $schemaRef = $requestBody['content']['application/json']['schema']['$ref']; + $schemaName = $this->getSchemaNameFromRef($schemaRef); + $imports[] = $schemaName; + $paramList[] = "data: " . $schemaName; + } else { + $paramList[] = "data: Record"; + } + } + + // Handle GET methods with query parameters + if (!empty($queryParams)) { + $queryParamTypeName = ucfirst($this->camelCase($operationId)) . "QueryParams"; + $paramList[] = "params?: " . $queryParamTypeName; + } + + // Determine return type + /*$returnType = 'void'; + foreach ($responses as $code => $response) { + if ("$code"[0] === '2') { // 2xx response + if (isset($response['content']['application/json']['schema'])) { + $schema = $response['content']['application/json']['schema']; + + if (isset($schema['$ref'])) { + $returnType = $this->getSchemaNameFromRef($schema['$ref']); + } elseif (isset($schema['type']) && $schema['type'] === 'object' && isset($schema['properties']['data']['type']) && $schema['properties']['data']['type'] === 'array') { + // Handle paginated response + if (isset($schema['properties']['data']['items']['$ref'])) { + $itemType = $this->getSchemaNameFromRef($schema['properties']['data']['items']['$ref']); + $returnType = "PaginatedResponse<" . $itemType . ">"; + } else { + $returnType = "PaginatedResponse"; + } + } else { + $returnType = "any"; + } + } + break; + } + }*/ + + $returnType = $this->getResponseReference($operationId, $responses); + $responseExample = $this->getResponseExample($operationId, $responses); + + + // Build path with parameters + $apiPath = $path; + foreach ($pathParams as $param) { + $apiPath = str_replace('{' . $param['name'] . '}', '${' . $this->camelCase($param['name']) . '}', $apiPath); + } + + return [ + 'methodName' => $methodName, + 'summary' => $summary, + 'httpMethod' => $httpMethod, + 'paramList' => $paramList, + 'returnType' => $returnType, + 'responseExample' => $responseExample, + 'apiPath' => $apiPath, + 'queryParams' => $queryParams, + 'pathParams' => $pathParams, + 'requestBody' => $requestBody, + ]; + } + + private function filterValidParameters(array $parameters) + { + // add in=query to the parameters if it is not set + foreach ($parameters as $i => $param) { + if (isset($param['ref']) || isset($param['$ref'])) { + $ref = $param['ref'] ?? $param['$ref']; + $refParts = explode('/', $ref); + $refName = end($refParts); + $parameters[$i]['in'] = 'query'; + $parameters[$i]['name'] = $refName; + } elseif (!isset($param['in'])) { + $parameters[$i]['in'] = 'query'; + dd($parameters[$i]); + } + } + return $parameters; + return array_filter($parameters, function ($param) { + return isset($param['in']); + }); + } + + /** + * Generate a TypeScript composable for a tag + */ + protected function generateComposableForTag($tag, $outputDir) + { + $tagLower = Str::camel($tag); + $className = "ProcessMaker" . ucfirst($tagLower) . "Api"; + $hookName = "useProcessMaker" . ucfirst($tagLower); + + $data = [ + 'tagLower' => $tagLower, + 'className' => $className, + 'hookName' => $hookName + ]; + + $content = Blade::render($this->getStub('composable'), $data); + $this->files->put("$outputDir/" . $hookName . ".ts", $content); + } + + /** + * Generate index files for the SDK + */ + protected function generateIndexFiles($tags, $apiDir, $composablesDir, $typesDir, $outputDir) + { + // API index + $apiClasses = []; + foreach ($tags as $tag) { + $tagLower = Str::camel($tag); + $className = "ProcessMaker" . ucfirst($tagLower) . "Api"; + $apiClasses[] = [ + 'className' => $className, + 'tagLower' => $tagLower + ]; + } + // sort apiClasses alphabetically ascending by className + usort($apiClasses, function($a, $b) { + return strcasecmp($a['className'], $b['className']); + }); + + $data = ['apiClasses' => $apiClasses]; + $content = Blade::render($this->getStub('api-index'), $data); + $this->files->put("$apiDir/index.ts", $content); + + // Composables index + $hooks = []; + foreach ($tags as $tag) { + $tagLower = Str::camel($tag); + $hookName = "useProcessMaker" . ucfirst($tagLower); + $hooks[] = $hookName; + } + + $data = ['hooks' => $hooks]; + $content = Blade::render($this->getStub('composables-index'), $data); + $this->files->put("$composablesDir/index.ts", $content); + + // Main SDK index + $data = ['tags' => $tags]; + $content = Blade::render($this->getStub('main-index'), $data); + $this->files->put("$outputDir/index.ts", $content); + + // API Response type + $content = Blade::render($this->getStub('api-response'), []); + $this->files->put("$typesDir/api.ts", $content); + + // README + $data = [ + 'tags' => $tags, + 'firstTag' => !empty($tags) ? $tags[0] : null + ]; + $content = Blade::render($this->getStub('readme'), $data); + $this->files->put("$outputDir/README.md", $content); + } + + /** + * Generate interface from OpenAPI schema + */ + protected function generateInterface($name, $schema) + { + $properties = $schema['properties'] ?? []; + + $interfaceName = $this->formatInterfaceName($name); + + if (empty($properties)) { + return "export interface $interfaceName {\n [key: string]: any;\n}"; + } + + $interface = "export interface $interfaceName {\n"; + + foreach ($properties as $propName => $propDetails) { + $interface .= " " . $propName; + + // Check if property is required + $required = $schema['required'] ?? []; + if (!in_array($propName, $required)) { + $interface .= "?"; + } + + $interface .= ": " . $this->getTypescriptType($propDetails) . ";\n"; + } + + $interface .= "}"; + + return $interface; + } + + /** + * Generate query param interface for GET endpoints + */ + protected function generateQueryParamInterface($parameters, $tagLower, $operationId) + { + $parameters = $this->filterValidParameters($parameters); + + $queryParams = array_filter($parameters, function ($param) { + return $param['in'] === 'query'; + }); + + if (empty($queryParams)) { + return ""; + } + + $interfaceName = ucfirst($this->camelCase($operationId)) . "QueryParams"; + + $interface = "export interface " . $interfaceName . " {\n"; + + foreach ($queryParams as $param) { + $refType = $this->getTypeOf($param); + $interface .= " " . $this->camelCase($param['name']) . "?: " . $this->mapSwaggerTypeToTypescript($refType) . ";\n"; + } + + $interface .= "}"; + + return $interface; + } + + private function getTypeOf(array $definition) + { + if (isset($definition['schema']['type'])) { + return $definition['schema']['type']; + } + if (isset($definition['$ref'])) { + return $this->getTypeOf($this->getSchemaByRef($definition['$ref'])); + } + + throw new Exception("Failed to get type of " . json_encode($definition)); + } + + private function generateResponseType($operationId, $responseSchema, $outputDir) + { + $interfaceName = ucfirst($this->camelCase($operationId)) . "Response"; + + $code = "export interface $interfaceName {\n"; + + foreach ($responseSchema['properties'] as $propName => $propDetails) { + $code .= " " . $propName . ": " . $this->getTypescriptType($propDetails) . ";\n"; + } + + $code .= "}"; + + return $code; + } + + /** + * Format interface name to PascalCase + */ + protected function formatInterfaceName($name) + { + $name = str_replace('#/components/schemas/', '', $name); + + // Convert snake_case to PascalCase + return Str::studly($name); + } + + /** + * Convert swagger type to TypeScript type + */ + public function getTypescriptType($property) + { + if ($property === 'true' || $property === true) { + return 'any'; + } + if (isset($property['$ref'])) { + $schema = $this->getSchemaByRef($property['$ref']); + if (isset($schema['schema'])) { + return $this->getTypescriptType($schema['schema']); + } + return $this->getSchemaNameFromRef($property['$ref']); + } + if (!isset($property['type']) && isset($property['schema']['type'])) { + return $this->getTypescriptType($property['schema']); + } + if (!isset($property['type'])) { + throw new Exception("Failed to get type of " . json_encode($property)); + } + $type = $property['type']; + + switch ($type) { + case 'integer': + return 'number'; + case 'number': + return 'number'; + case 'boolean': + return 'boolean'; + case 'array': + if (isset($property['items']['$ref'])) { + $itemType = $this->getSchemaNameFromRef($property['items']['$ref']); + return $itemType . '[]'; + } else { + $itemType = $this->getTypescriptType($property['items'] ?? ['type' => 'string']); + return $itemType . '[]'; + } + case 'object': + if (isset($property['additionalProperties'])) { + $valueType = $this->getTypescriptType($property['additionalProperties']); + return "Record"; + } + return 'Record'; + default: + return 'string'; + } + } + + /** + * Get schema name from reference + */ + protected function getSchemaNameFromRef($ref) + { + $parts = explode('/', $ref); + return $this->formatInterfaceName(end($parts)); + } + + /** + * Collect schema from reference for later processing + */ + protected function collectSchemaFromRef($ref, &$schemas) + { + return; + // $schemaName = $this->getSchemaNameFromRef($ref); + // if ($schemaName !== 'UpdateUserGroups') { + // return; + // } + // dump($schemaName, $ref); + } + + /** + * Get parameter value with appropriate conversion for TypeScript + */ + protected function getParamValueConversion($paramName, $param) + { + $type = $param['schema']['type'] ?? 'string'; + + if ($type === 'integer' || $type === 'number') { + return "params.$paramName.toString()"; + } + + return "params.$paramName"; + } + + /** + * Convert string to camelCase + */ + protected function camelCase($str) + { + return Str::camel($str); + } + + /** + * Get stub template content + */ + protected function getStub($type) + { + return $this->files->get(resource_path("stubs/api2typescript/{$type}.blade.php")); + } +} diff --git a/ProcessMaker/Events/BaseEmailEvent.php b/ProcessMaker/Events/BaseEmailEvent.php new file mode 100644 index 0000000000..d70747ed32 --- /dev/null +++ b/ProcessMaker/Events/BaseEmailEvent.php @@ -0,0 +1,86 @@ +sent_email; + $emailIdentifier = $tracker->getHeader('X-ProcessMaker-Email-ID'); + $messageEventId = $tracker->getHeader($this->getMessageEventIdHeader()); + $requestIdentifier = $tracker->getHeader('X-ProcessMaker-Request-ID'); + + // Log the email event for debugging + Log::debug($this->getEventType() . ' email event', [ + 'email_id' => $emailIdentifier, + 'message_event_id' => $messageEventId, + 'request_id' => $requestIdentifier, + ]); + + if ($requestIdentifier && $messageEventId) { + $request = ProcessRequest::where('id', $requestIdentifier)->first(); + if ($request) { + $data = [ + 'email_id' => $emailIdentifier, + ]; + WorkflowManager::triggerMessageEvent($request, $messageEventId, $data); + } else { + Log::warning($this->getEventType() . ' but process request not found', [ + 'request_id' => $requestIdentifier, + ]); + } + } else { + // Only log a warning if email tracking was explicitly enabled for this email + if ($emailIdentifier) { + Log::warning($this->getEventType() . ' but missing required headers', [ + 'email_id' => $emailIdentifier, + 'message_event_id' => $messageEventId, + 'request_id' => $requestIdentifier, + ]); + } + } + } catch (\Exception $e) { + Log::error('Error handling ' . $this->getEventType() . ' event', [ + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + } + } +} diff --git a/ProcessMaker/Events/BouncedEmail.php b/ProcessMaker/Events/BouncedEmail.php new file mode 100644 index 0000000000..55b3266e7a --- /dev/null +++ b/ProcessMaker/Events/BouncedEmail.php @@ -0,0 +1,38 @@ +sent_email; + $emailIdentifier = $tracker->getHeader('X-ProcessMaker-Email-ID'); + $messageEventId = $tracker->getHeader('X-ProcessMaker-Sent-Message-Event-ID'); + $requestIdentifier = $tracker->getHeader('X-ProcessMaker-Request-ID'); + + if ($requestIdentifier && $messageEventId) { + $request = ProcessRequest::where('id', $requestIdentifier)->first(); + if ($request) { + $data = [ + 'email_id' => $emailIdentifier, + ]; + WorkflowManager::triggerMessageEvent($request, $messageEventId, $data); + } + } + } +} diff --git a/ProcessMaker/Events/EmailComplaint.php b/ProcessMaker/Events/EmailComplaint.php new file mode 100644 index 0000000000..9ae74f0a79 --- /dev/null +++ b/ProcessMaker/Events/EmailComplaint.php @@ -0,0 +1,41 @@ +processEmailEvent($event); + } + + /** + * @inheritdoc + */ + protected function getEventType(): string + { + return 'Complaint'; + } + + /** + * @inheritdoc + */ + protected function getMessageEventIdHeader(): string + { + return 'X-ProcessMaker-Complaint-Message-Event-ID'; + } +} diff --git a/ProcessMaker/Events/EmailDelivered.php b/ProcessMaker/Events/EmailDelivered.php new file mode 100644 index 0000000000..8ea95fde4d --- /dev/null +++ b/ProcessMaker/Events/EmailDelivered.php @@ -0,0 +1,41 @@ +processEmailEvent($event); + } + + /** + * @inheritdoc + */ + protected function getEventType(): string + { + return 'Delivered'; + } + + /** + * @inheritdoc + */ + protected function getMessageEventIdHeader(): string + { + return 'X-ProcessMaker-Delivered-Message-Event-ID'; + } +} diff --git a/ProcessMaker/Events/EmailLinkClicked.php b/ProcessMaker/Events/EmailLinkClicked.php new file mode 100644 index 0000000000..640ce2568e --- /dev/null +++ b/ProcessMaker/Events/EmailLinkClicked.php @@ -0,0 +1,41 @@ +processEmailEvent($event); + } + + /** + * @inheritdoc + */ + protected function getEventType(): string + { + return 'Link clicked'; + } + + /** + * @inheritdoc + */ + protected function getMessageEventIdHeader(): string + { + return 'X-ProcessMaker-Link-Clicked-Message-Event-ID'; + } +} diff --git a/ProcessMaker/Events/EmailSent.php b/ProcessMaker/Events/EmailSent.php new file mode 100644 index 0000000000..a993329962 --- /dev/null +++ b/ProcessMaker/Events/EmailSent.php @@ -0,0 +1,41 @@ +processEmailEvent($event); + } + + /** + * @inheritdoc + */ + protected function getEventType(): string + { + return 'Sent'; + } + + /** + * @inheritdoc + */ + protected function getMessageEventIdHeader(): string + { + return 'X-ProcessMaker-Sent-Message-Event-ID'; + } +} diff --git a/ProcessMaker/Events/EmailViewed.php b/ProcessMaker/Events/EmailViewed.php new file mode 100644 index 0000000000..6e258facea --- /dev/null +++ b/ProcessMaker/Events/EmailViewed.php @@ -0,0 +1,41 @@ +processEmailEvent($event); + } + + /** + * @inheritdoc + */ + protected function getEventType(): string + { + return 'Viewed'; + } + + /** + * @inheritdoc + */ + protected function getMessageEventIdHeader(): string + { + return 'X-ProcessMaker-Viewed-Message-Event-ID'; + } +} diff --git a/ProcessMaker/Events/MessageEventCaught.php b/ProcessMaker/Events/MessageEventCaught.php new file mode 100644 index 0000000000..1eb2ae3bac --- /dev/null +++ b/ProcessMaker/Events/MessageEventCaught.php @@ -0,0 +1,73 @@ + + */ + public function broadcastOn(): array + { + $channels = [ + new PrivateChannel('ProcessMaker.Models.ProcessRequest.' . $this->processRequest->getKey()), + ]; + $hasParent = $this->processRequest->parent_request_id; + if ($hasParent) { + $channels[] = new PrivateChannel('ProcessMaker.Models.ProcessRequest.' . $this->processRequest->parent_request_id . '.SubProcesses'); + } + return $channels; + } + + /** + * Set the event name + * + * @return string + */ + public function broadcastAs() + { + return 'MessageEventCaught'; + } + + /** + * Get the data to broadcast. + * + * @return array + */ + public function broadcastWith(): array + { + return [ + 'event' => [ + 'id' => $this->message->getId(), + 'name' => $this->message->getName(), + ], + 'element' => [ + 'id' => $this->node->getId(), + 'name' => $this->node->getName(), + ], + 'data' => (object) $this->message->getData($this->processRequest), + ]; + } +} diff --git a/ProcessMaker/Events/MessageEventThrown.php b/ProcessMaker/Events/MessageEventThrown.php new file mode 100644 index 0000000000..7416deb04a --- /dev/null +++ b/ProcessMaker/Events/MessageEventThrown.php @@ -0,0 +1,73 @@ + + */ + public function broadcastOn(): array + { + $channels = [ + new PrivateChannel('ProcessMaker.Models.ProcessRequest.' . $this->processRequest->getKey()), + ]; + $hasParent = $this->processRequest->parent_request_id; + if ($hasParent) { + $channels[] = new PrivateChannel('ProcessMaker.Models.ProcessRequest.' . $this->processRequest->parent_request_id . '.SubProcesses'); + } + return $channels; + } + + /** + * Set the event name + * + * @return string + */ + public function broadcastAs() + { + return 'MessageEventThrown'; + } + + /** + * Get the data to broadcast. + * + * @return array + */ + public function broadcastWith(): array + { + return [ + 'event' => [ + 'id' => $this->message->getId(), + 'name' => $this->message->getName(), + ], + 'element' => [ + 'id' => $this->node->getId(), + 'name' => $this->node->getName(), + ], + 'data' => (object) $this->message->getData($this->processRequest), + ]; + } +} diff --git a/ProcessMaker/Facades/WorkflowManager.php b/ProcessMaker/Facades/WorkflowManager.php index 8ae67c78d0..a26df908f6 100644 --- a/ProcessMaker/Facades/WorkflowManager.php +++ b/ProcessMaker/Facades/WorkflowManager.php @@ -25,6 +25,8 @@ * @method static bool registerServiceImplementation($implementation, $class) * @method static bool existsServiceImplementation($implementation) * @method static bool runServiceImplementation($implementation, array $data, array $config, $tokenId = '') + * @method static void triggerBoundaryEvent(\ProcessMaker\Models\Process $process, \ProcessMaker\Models\ProcessRequest $request, \ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface $token, \ProcessMaker\Nayra\Contracts\Bpmn\BoundaryEventInterface $boundaryEvent) + * @method static void triggerMessageEvent(\ProcessMaker\Models\ProcessRequest $request, string $messageEventId, array $data) */ class WorkflowManager extends Facade { diff --git a/ProcessMaker/Http/Controllers/Api/NotificationController.php b/ProcessMaker/Http/Controllers/Api/NotificationController.php index c94d72c1e3..16374aedb9 100644 --- a/ProcessMaker/Http/Controllers/Api/NotificationController.php +++ b/ProcessMaker/Http/Controllers/Api/NotificationController.php @@ -64,6 +64,7 @@ class NotificationController extends Controller * ), * @OA\Property( * property="meta", + * type="object", * @OA\Schema(ref="#/components/schemas/metadata"), * ), * ), diff --git a/ProcessMaker/Http/Controllers/Api/OpenApiSpec.php b/ProcessMaker/Http/Controllers/Api/OpenApiSpec.php index f99e69b916..17b48e71b1 100644 --- a/ProcessMaker/Http/Controllers/Api/OpenApiSpec.php +++ b/ProcessMaker/Http/Controllers/Api/OpenApiSpec.php @@ -44,6 +44,12 @@ * @OA\Schema(type="string", enum={"asc", "desc"}, default="asc"), * ), * @OA\Parameter( + * parameter="page", + * name="page", + * in="query", + * @OA\Schema(type="integer", default="1"), + * ), + * @OA\Parameter( * parameter="per_page", * name="per_page", * in="query", @@ -57,6 +63,13 @@ * @OA\Schema(type="string", default=""), * ), * @OA\Parameter( + * parameter="fields", + * name="fields", + * in="query", + * description="Fields to map the response object.", + * @OA\Schema(type="object"), + * ), + * @OA\Parameter( * parameter="member_id", * name="member_id", * in="query", @@ -74,6 +87,13 @@ * in="query", * @OA\Schema(type="string", default=""), * ), + * @OA\Parameter( + * parameter="pmql", + * name="pmql", + * in="query", + * description="PMQL query to filter results", + * @OA\Schema(type="string"), + * ), * @OA\Schema( * schema="DateTime", * @OA\Property(property="date", type="string"), diff --git a/ProcessMaker/Http/Controllers/Api/PermissionController.php b/ProcessMaker/Http/Controllers/Api/PermissionController.php index 03b468fa22..f5636d846d 100644 --- a/ProcessMaker/Http/Controllers/Api/PermissionController.php +++ b/ProcessMaker/Http/Controllers/Api/PermissionController.php @@ -48,6 +48,7 @@ public function index(Request $request) * @OA\Put( * path="/permissions", * summary="Update the permissions of a user", + * operationId="updatePermissions", * tags={"Permissions"}, * * @OA\RequestBody( diff --git a/ProcessMaker/Http/Controllers/Api/ProcessController.php b/ProcessMaker/Http/Controllers/Api/ProcessController.php index b782c2cf0b..1b9e6dc39c 100644 --- a/ProcessMaker/Http/Controllers/Api/ProcessController.php +++ b/ProcessMaker/Http/Controllers/Api/ProcessController.php @@ -87,6 +87,7 @@ class ProcessController extends Controller * @OA\Parameter(ref="#/components/parameters/per_page"), * @OA\Parameter(ref="#/components/parameters/status"), * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter(ref="#/components/parameters/pmql"), * @OA\Parameter( * name="simplified_data_for_selector", * in="query", @@ -1310,6 +1311,7 @@ public function downloadBpmn(Request $request, Process $process) * @OA\Head( * path="/processes/import/{code}/is_ready", * summary="Check if the import is ready", + * operationId="importReady", * tags={"Processes"}, * * @OA\Parameter( diff --git a/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php b/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php index a183a22f9e..cc59e829fe 100644 --- a/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php +++ b/ProcessMaker/Http/Controllers/Api/ProcessRequestController.php @@ -547,6 +547,65 @@ public function activateIntermediateEvent(ProcessRequest $request, $event) return response([]); } + /** + * Trigger a boundary event + * + * @param ProcessRequest $request + * @param string $event + * @return void + * + * @OA\Post( + * path="/requests/{process_request_id}/boundary/{boundary_event_id}", + * summary="Trigger a boundary event", + * operationId="triggerBoundaryEvent", + * tags={"Process Requests"}, + * @OA\Parameter( + * description="ID of process request", + * in="path", + * name="process_request_id", + * required=true, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * description="ID of boundary event", + * in="path", + * name="boundary_event_id", + * required=true, + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\RequestBody( + * @OA\JsonContent( + * required={"data"}, + * @OA\Property(property="data", type="object"), + * ) + * ), + * @OA\Response( + * response=204, + * description="success", + * ), + * ) + */ + public function triggerBoundaryEvent(ProcessRequest $request, $boundaryEventId) + { + $data = (array) request()->json()->all(); + // Get the process definition + $definitions = $request->getVersionDefinitions(); + $boundaryEvent = $definitions->getBoundaryEvent($boundaryEventId); + $activity = $boundaryEvent->getAttachedTo(); + $tokens = $request->tokens() + ->where('element_id', $activity->getId()) + ->where('status', 'ACTIVE')->get(); + foreach ($tokens as $token) { + WorkflowManager::triggerBoundaryEvent($request->process, $request, $token, $boundaryEvent, $data); + } + + return response([], 204); + } + /** * Parse the whitelist parameter * diff --git a/ProcessMaker/Http/Controllers/Api/TaskAssignmentController.php b/ProcessMaker/Http/Controllers/Api/TaskAssignmentController.php index 05568ee8c5..7d35cf5c5e 100644 --- a/ProcessMaker/Http/Controllers/Api/TaskAssignmentController.php +++ b/ProcessMaker/Http/Controllers/Api/TaskAssignmentController.php @@ -176,10 +176,6 @@ public function update(ProcessTaskAssignment $task_assignment, Request $request) * type="integer", * ) * ), - * @OA\RequestBody( - * required=true, - * @OA\JsonContent(ref="#/components/schemas/taskAssignmentsEditable") - * ), * @OA\Response( * response=200, * description="success", diff --git a/ProcessMaker/Http/Controllers/Api/TaskController.php b/ProcessMaker/Http/Controllers/Api/TaskController.php index 2d691af0ee..789c16b30f 100644 --- a/ProcessMaker/Http/Controllers/Api/TaskController.php +++ b/ProcessMaker/Http/Controllers/Api/TaskController.php @@ -101,9 +101,12 @@ class TaskController extends Controller * ) * ), * @OA\Parameter(ref="#/components/parameters/filter"), + * @OA\Parameter(ref="#/components/parameters/pmql"), * @OA\Parameter(ref="#/components/parameters/order_by"), * @OA\Parameter(ref="#/components/parameters/order_direction"), * @OA\Parameter(ref="#/components/parameters/include"), + * @OA\Parameter(ref="#/components/parameters/page"), + * @OA\Parameter(ref="#/components/parameters/per_page"), * * @OA\Response( * response=200, diff --git a/ProcessMaker/Http/Controllers/Api/UserController.php b/ProcessMaker/Http/Controllers/Api/UserController.php index 0ecbf49fe0..b26e26b2a1 100644 --- a/ProcessMaker/Http/Controllers/Api/UserController.php +++ b/ProcessMaker/Http/Controllers/Api/UserController.php @@ -946,4 +946,18 @@ public function updateLanguage(Request $request) return response([], 204); } + + public function current() + { + return response([ + 'data' => Auth::user(), + ], 200); + } + + public function logout(Request $request) + { + $request->user()->token()->revoke(); + + return response()->json(['message' => 'OK']); + } } diff --git a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php index d57af422fb..ec45f6fb7d 100644 --- a/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php +++ b/ProcessMaker/Http/Controllers/Api/V1_1/ProcessVariableController.php @@ -66,6 +66,7 @@ class ProcessVariableController extends Controller * ) * @OA\Get( * path="/processes/variables", + * operationId="getProcessesVariables", * summary="Get variables for multiple processes with pagination", * servers={ * @OA\Server(url=L5_SWAGGER_API_V1_1, description="API v1.1 Server") diff --git a/ProcessMaker/Http/Controllers/Auth/LoginController.php b/ProcessMaker/Http/Controllers/Auth/LoginController.php index 190475ec1b..a223b6853b 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -288,6 +288,10 @@ private function forgetUserSession() public function loggedOut(Request $request) { + if ($request->has('redirectTo')) { + return redirect($request->get('redirectTo')); + } + $response = redirect(route('login')); if ($request->has('timeout')) { $response->with('timeout', true); diff --git a/ProcessMaker/Http/Controllers/Process/ScreenBuilderController.php b/ProcessMaker/Http/Controllers/Process/ScreenBuilderController.php index c356f4c27b..d5bc676b4d 100644 --- a/ProcessMaker/Http/Controllers/Process/ScreenBuilderController.php +++ b/ProcessMaker/Http/Controllers/Process/ScreenBuilderController.php @@ -22,6 +22,11 @@ class ScreenBuilderController extends Controller */ public function edit(ScreenBuilderManager $manager, Screen $screen, $processId = null) { + // Check if the screen is external url + if ($screen->config[0]['url'] ?? null) { + return redirect($screen->config[0]['url']); + } + /** * Emit the ModelerStarting event, passing in our ModelerManager instance. This will * allow packages to add additional javascript for modeler initialization which diff --git a/ProcessMaker/Http/Kernel.php b/ProcessMaker/Http/Kernel.php index 97ffae0d6c..c083dbee34 100644 --- a/ProcessMaker/Http/Kernel.php +++ b/ProcessMaker/Http/Kernel.php @@ -24,6 +24,7 @@ class Kernel extends HttpKernel Middleware\BrowserCache::class, ServerTimingMiddleware::class, Middleware\FileSizeCheck::class, + \Illuminate\Http\Middleware\HandleCors::class, ]; /** @@ -89,7 +90,7 @@ class Kernel extends HttpKernel 'no-cache' => Middleware\NoCache::class, 'admin' => Middleware\IsAdmin::class, 'etag' => Middleware\Etag\HandleEtag::class, - 'file_size_check' => Middleware\FileSizeCheck::class + 'file_size_check' => Middleware\FileSizeCheck::class, ]; /** diff --git a/ProcessMaker/Listeners/BpmnSubscriber.php b/ProcessMaker/Listeners/BpmnSubscriber.php index c7f0f7f810..0a862a0bca 100644 --- a/ProcessMaker/Listeners/BpmnSubscriber.php +++ b/ProcessMaker/Listeners/BpmnSubscriber.php @@ -3,16 +3,21 @@ namespace ProcessMaker\Listeners; use Exception; +use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Notification; use ProcessMaker\Events\ActivityAssigned; use ProcessMaker\Events\ActivityCompleted; +use ProcessMaker\Events\MessageEventCaught; +use ProcessMaker\Events\MessageEventThrown; use ProcessMaker\Events\ProcessCompleted; use ProcessMaker\Facades\Metrics; use ProcessMaker\Facades\WorkflowManager; use ProcessMaker\Jobs\TerminateRequestEndEvent; +use ProcessMaker\Managers\DataManager; use ProcessMaker\Models\Comment; use ProcessMaker\Models\FormalExpression; +use ProcessMaker\Models\Message; use ProcessMaker\Models\ProcessRequest; use ProcessMaker\Models\ProcessRequestToken; use ProcessMaker\Nayra\Bpmn\Events\ActivityActivatedEvent; @@ -21,7 +26,9 @@ use ProcessMaker\Nayra\Bpmn\Events\ProcessInstanceCompletedEvent; use ProcessMaker\Nayra\Bpmn\Events\ProcessInstanceCreatedEvent; use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface; +use ProcessMaker\Nayra\Contracts\Bpmn\CatchEventInterface; use ProcessMaker\Nayra\Contracts\Bpmn\ConditionalEventDefinitionInterface; +use ProcessMaker\Nayra\Contracts\Bpmn\EntityInterface; use ProcessMaker\Nayra\Contracts\Bpmn\ErrorInterface; use ProcessMaker\Nayra\Contracts\Bpmn\IntermediateCatchEventInterface; use ProcessMaker\Nayra\Contracts\Bpmn\MessageEventDefinitionInterface; @@ -29,6 +36,7 @@ use ProcessMaker\Nayra\Contracts\Bpmn\ScriptTaskInterface; use ProcessMaker\Nayra\Contracts\Bpmn\ServiceTaskInterface; use ProcessMaker\Nayra\Contracts\Bpmn\TerminateEventDefinitionInterface; +use ProcessMaker\Nayra\Contracts\Bpmn\ThrowEventInterface; use ProcessMaker\Nayra\Contracts\Bpmn\TimerEventDefinitionInterface; use ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface; use ProcessMaker\Nayra\Contracts\Bpmn\TransitionInterface; @@ -247,6 +255,21 @@ public function onIntermediateCatchEventActivated(IntermediateCatchEventInterfac foreach ($event->getEventDefinitions() as $eventDefinition) { foreach ($messages as $interface => $message) { if (is_subclass_of($eventDefinition, $interface)) { + $bpmnMessage = $eventDefinition->getPayload(); + if ($bpmnMessage) { + event(new MessageEventCaught($token->getInstance(), $token->getOwnerElement(), $bpmnMessage)); + devtools( + '▶︎ MessageEventCaught', + $bpmnMessage->getName() ?? '(null)', + $bpmnMessage->getId() ?? '(null)', + [ + 'Payload' => json_encode( + (object) $bpmnMessage->getData($token->getInstance()), + JSON_PRETTY_PRINT + ) + ] + ); + } $comment = new Comment([ 'user_id' => null, 'commentable_type' => ProcessRequest::class, @@ -279,7 +302,8 @@ public function updateDataWithFlowTransition($transition, $flow, $instance) // Exit if no variable or expression is set $config = json_decode($flow->getProperties()['config'], true); - if (empty($config['update_data']) + if ( + empty($config['update_data']) || empty($config['update_data']['variable']) || empty($config['update_data']['expression']) ) { @@ -316,6 +340,45 @@ public function onTerminateEndEvent($event) }); } + public function onMessageEventDefinition(ThrowEventInterface $node, TokenInterface $token, Message $message) + { + event(new MessageEventThrown($token->getInstance(), $node, $message)); + devtools( + '▶︎ MessageEventThrown', + $message->getName() ?? '(null)', + $message->getId() ?? '(null)', + [ + 'Payload' => json_encode( + (object) $message->getData($token->getInstance()), + JSON_PRETTY_PRINT + ) + ] + ); + } + + public function onMessageEventCaught(CatchEventInterface $node, TokenInterface $token) + { + foreach ($node->getEventDefinitions() as $eventDefinition) { + if ($eventDefinition instanceof MessageEventDefinitionInterface) { + $bpmnMessage = $eventDefinition->getPayload(); + if ($bpmnMessage) { + event(new MessageEventCaught($token->getInstance(), $token->getOwnerElement(), $bpmnMessage)); + devtools( + '▶︎ MessageEventCaught', + $bpmnMessage->getName() ?? '(null)', + $bpmnMessage->getId() ?? '(null)', + [ + 'Payload' => json_encode( + (object) $bpmnMessage->getData($token->getInstance()), + JSON_PRETTY_PRINT + ) + ] + ); + } + } + } + } + /** * Subscription. * @@ -340,5 +403,45 @@ public function subscribe($events) $events->listen(IntermediateCatchEventInterface::EVENT_CATCH_TOKEN_ARRIVES, static::class . '@onIntermediateCatchEventActivated'); $events->listen(TerminateEventDefinitionInterface::EVENT_THROW_EVENT_DEFINITION, static::class . '@onTerminateEndEvent'); + $events->listen(MessageEventDefinitionInterface::EVENT_THROW_EVENT_DEFINITION, static::class . '@onMessageEventDefinition'); + $events->listen(IntermediateCatchEventInterface::EVENT_CATCH_MESSAGE_CATCH, static::class . '@onMessageEventCaught'); + + // Log devtools events for local and testing environments + if (App::environment('local') || App::environment('testing')) { + $events->listen('*', function ($event, $args) { + $hasBackslash = strpos($event, '\\') !== false; + if (!$hasBackslash && count($args) > 0) { + $token = isset($args[1]) && ($args[1] instanceof ProcessRequestToken) ? $args[1] : null; + $payload = $args[0]; + if ( + $payload instanceof ActivityCompletedEvent + || $payload instanceof ActivityActivatedEvent + || $payload instanceof ActivityClosedEvent + ) { + $token = $payload->token; + $payload = $payload->activity; + } + + if ($payload instanceof EntityInterface) { + $element = $payload->getBpmnElement(); + $document = $payload->getOwnerDocument(); + $xml = $element ? $document->saveXML($element) : ''; + $details = [ + 'Node' => $xml + ]; + if ($token) { + $dm = new DataManager(); + $details['Data'] = json_encode((object) $dm->getData($token), JSON_PRETTY_PRINT); + } + devtools( + $event, + $payload->getName() ?? '(null)', + $payload->getId() ?? '(null)', + $details + ); + } + } + }); + } } } diff --git a/ProcessMaker/Managers/DataManager.php b/ProcessMaker/Managers/DataManager.php index b3566d7509..167584603e 100644 --- a/ProcessMaker/Managers/DataManager.php +++ b/ProcessMaker/Managers/DataManager.php @@ -127,7 +127,7 @@ private function loadTokenData(array $data = [], ProcessRequestToken $token = nu } // Magic Variable: _user - $data = $this->loadUserData($data, $token); + $data = $this->loadUserData($data ?? [], $token); // Magic Variable: _request $request = $token->getInstance() ?: $token->processRequest; diff --git a/ProcessMaker/Models/ProcessRequest.php b/ProcessMaker/Models/ProcessRequest.php index aee92ce58b..ad10c5e0a2 100644 --- a/ProcessMaker/Models/ProcessRequest.php +++ b/ProcessMaker/Models/ProcessRequest.php @@ -88,7 +88,7 @@ * @OA\Property(property="process_category_id", type="string", format="id"), * @OA\Property(property="created_at", type="string", format="date-time"), * @OA\Property(property="updated_at", type="string", format="date-time"), - * @OA\Property(property="user", @OA\Schema(ref="#/components/schemas/users")), + * @OA\Property(property="user", type="object", @OA\Schema(ref="#/components/schemas/users")), * @OA\Property(property="participants", type="array", @OA\Items(ref="#/components/schemas/users")), * ) * }, diff --git a/ProcessMaker/Models/TimerExpression.php b/ProcessMaker/Models/TimerExpression.php index af70744e23..069fed3bd6 100644 --- a/ProcessMaker/Models/TimerExpression.php +++ b/ProcessMaker/Models/TimerExpression.php @@ -87,7 +87,13 @@ public function getLanguage() public function __invoke($data) { $expression = $this->getProperty(FormalExpressionInterface::BPMN_PROPERTY_BODY); - $expression = $this->mustacheTimerExpression($expression, $data); + $isFeel = str_contains($this->getProperty('type'), 'tFormalExpression'); + if ($isFeel) { + $feel = new FeelExpressionEvaluator(); + $expression = $feel->render($expression, $data); + } else { + $expression = $this->mustacheTimerExpression($expression, $data); + } return $this->getDateExpression($expression) ?: $this->getCycleExpression($expression) diff --git a/ProcessMaker/Nayra/Managers/WorkflowManagerDefault.php b/ProcessMaker/Nayra/Managers/WorkflowManagerDefault.php index 0f757aa7e2..f1f9772215 100644 --- a/ProcessMaker/Nayra/Managers/WorkflowManagerDefault.php +++ b/ProcessMaker/Nayra/Managers/WorkflowManagerDefault.php @@ -18,13 +18,17 @@ use ProcessMaker\Jobs\StartEvent; use ProcessMaker\Jobs\ThrowMessageEvent; use ProcessMaker\Jobs\ThrowSignalEvent; +use ProcessMaker\Managers\DataManager; use ProcessMaker\Models\FormalExpression; use ProcessMaker\Models\Process as Definitions; use ProcessMaker\Models\ProcessRequest; use ProcessMaker\Models\ProcessRequestToken as Token; +use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface; use ProcessMaker\Nayra\Contracts\Bpmn\BoundaryEventInterface; use ProcessMaker\Nayra\Contracts\Bpmn\EntityInterface; use ProcessMaker\Nayra\Contracts\Bpmn\EventDefinitionInterface; +use ProcessMaker\Nayra\Contracts\Bpmn\IntermediateCatchEventInterface; +use ProcessMaker\Nayra\Contracts\Bpmn\MessageEventDefinitionInterface; use ProcessMaker\Nayra\Contracts\Bpmn\ProcessInterface; use ProcessMaker\Nayra\Contracts\Bpmn\ScriptTaskInterface; use ProcessMaker\Nayra\Contracts\Bpmn\ServiceTaskInterface; @@ -137,6 +141,42 @@ public function triggerBoundaryEvent( BoundaryEvent::dispatchSync($definitions, $instance, $token, $boundaryEvent, $data); } + /** + * Trigger a message event + * + * @param ProcessRequest $request + * @param string $messageEventId + * @param array $data + */ + public function triggerMessageEvent(ProcessRequest $request, string $messageEventId, array $data) + { + // find active tokens that are waiting for this message event + $tokens = $request->tokens()->where('status', ActivityInterface::TOKEN_STATE_ACTIVE)->get(); + foreach ($tokens as $token) { + $element = $token->getDefinition(true); + $isIntermediateCatchEvent = $element instanceof IntermediateCatchEventInterface; + $isTask = $element instanceof ActivityInterface; + if ($isIntermediateCatchEvent) { + foreach ($element->getEventDefinitions() as $eventDefinition) { + if ($eventDefinition instanceof MessageEventDefinitionInterface && $eventDefinition->getPayload()->getId() === $messageEventId) { + CatchEvent::dispatchSync($request->process, $request, $token, $data); + } + } + } elseif ($isTask) { + $hasBoundaryEvent = $element->getBoundaryEvents()->count() > 0; + if ($hasBoundaryEvent) { + foreach ($element->getBoundaryEvents() as $boundaryEvent) { + foreach ($boundaryEvent->getEventDefinitions() as $eventDefinition) { + if ($eventDefinition instanceof MessageEventDefinitionInterface && $eventDefinition->getPayload()?->getId() === $messageEventId) { + BoundaryEvent::dispatchSync($request->process, $request, $token, $boundaryEvent, $data); + } + } + } + } + } + } + } + /** * Trigger an start event and return the instance. * diff --git a/ProcessMaker/Providers/BroadcastServiceProvider.php b/ProcessMaker/Providers/BroadcastServiceProvider.php index 233252f282..2bf84a0200 100644 --- a/ProcessMaker/Providers/BroadcastServiceProvider.php +++ b/ProcessMaker/Providers/BroadcastServiceProvider.php @@ -15,6 +15,11 @@ class BroadcastServiceProvider extends ServiceProvider public function boot() { Broadcast::routes(['middleware'=>['web', 'auth:anon']]); + Broadcast::routes([ + 'middleware' => ['auth:api'], + 'prefix' => 'api', + ]); + require base_path('routes/channels.php'); } } diff --git a/ProcessMaker/Providers/EventServiceProvider.php b/ProcessMaker/Providers/EventServiceProvider.php index 5e7a07ed53..9a6f0690df 100644 --- a/ProcessMaker/Providers/EventServiceProvider.php +++ b/ProcessMaker/Providers/EventServiceProvider.php @@ -3,16 +3,28 @@ namespace ProcessMaker\Providers; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; +use jdavidbakr\MailTracker\Events\ComplaintMessageEvent; +use jdavidbakr\MailTracker\Events\EmailDeliveredEvent; +use jdavidbakr\MailTracker\Events\EmailSentEvent; +use jdavidbakr\MailTracker\Events\LinkClickedEvent; +use jdavidbakr\MailTracker\Events\PermanentBouncedMessageEvent; +use jdavidbakr\MailTracker\Events\ViewEmailEvent; use ProcessMaker\Events\ActivityAssigned; use ProcessMaker\Events\ActivityCompleted; use ProcessMaker\Events\ActivityReassignment; use ProcessMaker\Events\AuthClientCreated; use ProcessMaker\Events\AuthClientDeleted; use ProcessMaker\Events\AuthClientUpdated; +use ProcessMaker\Events\BouncedEmail; use ProcessMaker\Events\CategoryCreated; use ProcessMaker\Events\CategoryDeleted; use ProcessMaker\Events\CategoryUpdated; use ProcessMaker\Events\CustomizeUiUpdated; +use ProcessMaker\Events\EmailComplaint; +use ProcessMaker\Events\EmailDelivered; +use ProcessMaker\Events\EmailLinkClicked; +use ProcessMaker\Events\EmailSent; +use ProcessMaker\Events\EmailViewed; use ProcessMaker\Events\EnvironmentVariablesCreated; use ProcessMaker\Events\EnvironmentVariablesDeleted; use ProcessMaker\Events\EnvironmentVariablesUpdated; @@ -111,6 +123,25 @@ class EventServiceProvider extends ServiceProvider TranslationChanged::class => [ InvalidateScreenCacheOnTranslationChange::class, ], + // Email Tracker + EmailSentEvent::class => [ + EmailSent::class, + ], + ViewEmailEvent::class => [ + EmailViewed::class, + ], + LinkClickedEvent::class => [ + EmailLinkClicked::class, + ], + EmailDeliveredEvent::class => [ + EmailDelivered::class, + ], + ComplaintMessageEvent::class => [ + EmailComplaint::class, + ], + PermanentBouncedMessageEvent::class => [ + BouncedEmail::class, + ], ]; /** diff --git a/ProcessMaker/Traits/MakeHttpRequests.php b/ProcessMaker/Traits/MakeHttpRequests.php index 26da4cecd3..53003e619b 100644 --- a/ProcessMaker/Traits/MakeHttpRequests.php +++ b/ProcessMaker/Traits/MakeHttpRequests.php @@ -27,6 +27,14 @@ trait MakeHttpRequests 'OAUTH2_PASSWORD' => 'passwordAuthorization', ]; + private $debugData = [ + 'method' => '', + 'url' => '', + 'headers' => [], + 'body' => '', + 'bodyType' => '', + ]; + /** * Verify certificate ssl * @@ -316,9 +324,12 @@ private function response($response, array $data = [], array $config = []) $status = $response->getStatusCode(); $bodyContent = $response->getBody()->getContents(); if (!$this->isJson($bodyContent)) { + devtools($this->debugData['method'], $this->debugData['url'], $status, ['Response' => $bodyContent, 'Request' => $this->debugData['body']]); return ['response' => $bodyContent, 'status' => $status]; } + devtools($this->debugData['method'], $this->debugData['url'], $status, ['Response' => $bodyContent, 'Request' => $this->debugData['body']]); + switch (true) { case $status == 200: $content = json_decode($bodyContent, true); @@ -345,6 +356,7 @@ private function response($response, array $data = [], array $config = []) } } + devtools('', 'MAPPED RESPONSE', '', ['Output' => json_encode($mapped, JSON_PRETTY_PRINT)]); return $mapped; } @@ -353,9 +365,12 @@ private function responseWithHeaderData($response, array $data = [], array $conf $status = $response->getStatusCode(); $bodyContent = $response->getBody()->getContents(); if (!$this->isJson($bodyContent)) { + devtools($this->debugData['method'], $this->debugData['url'], $status, ['Response' => $bodyContent, 'Request' => $this->debugData['body']]); return ['response' => $bodyContent, 'status' => $status]; } + devtools($this->debugData['method'], $this->debugData['url'], $status, ['Response' => $bodyContent, 'Request' => $this->debugData['body']]); + switch (true) { case $status == 200: $content = json_decode($bodyContent, true); @@ -416,6 +431,7 @@ private function responseWithHeaderData($response, array $data = [], array $conf $mapped[$processVar] = $evaluatedApiVar; } + devtools('', 'MAPPED RESPONSE', '', ['Output' => json_encode($mapped, JSON_PRETTY_PRINT)]); return $mapped; } @@ -436,7 +452,8 @@ private function call($method, $url, array $headers, $body, $bodyType) { $client = $this->client ?? app()->make(Client::class, [ 'config' => [ - 'verify' => $this->verifySsl, + // Disable SSL verification in local development environment + 'verify' => $this->verifySsl && (config('app.env') !== 'local'), 'timeout' => $this->timeout, ], ]); @@ -446,6 +463,11 @@ private function call($method, $url, array $headers, $body, $bodyType) } $request = new Request($method, $url, $headers, $body); + $this->debugData['method'] = $method; + $this->debugData['url'] = $url; + $this->debugData['headers'] = $headers; + $this->debugData['body'] = $body; + $this->debugData['bodyType'] = $bodyType; if ($this->debug_mode) { $this->log('Request: ', var_export(compact('method', 'url', 'body', 'bodyType'), true)); diff --git a/composer.json b/composer.json index 907fe921eb..ac8f2eefa4 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "google/apiclient": "^2.18", "guzzlehttp/guzzle": "^7.9", "igaster/laravel-theme": "^2.0", + "jdavidbakr/mail-tracker": "^7.18", "jenssegers/agent": "^2.6", "laravel/framework": "^11.0", "laravel/horizon": "^5.30", @@ -179,6 +180,7 @@ "package-webentry": "2.29.0", "package-api-testing": "1.3.0", "package-variable-finder": "1.0.3", + "package-private-equity": "dev-main", "packages": "^0" }, "docker-executors": { diff --git a/composer.lock b/composer.lock index 6b8bedc206..f83d9697e4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "79f6d31afab1b6fd6e8e9cb5e0174daf", + "content-hash": "4390c170cc7fac64f61278f1b7d4dc48", "packages": [ { "name": "aws/aws-crt-php", @@ -60,6 +60,65 @@ }, "time": "2024-10-18T22:15:13+00:00" }, + { + "name": "aws/aws-php-sns-message-validator", + "version": "1.9.1", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-php-sns-message-validator.git", + "reference": "80850e8d93fb57889ce609e113895dd2f1d7eed6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-php-sns-message-validator/zipball/80850e8d93fb57889ce609e113895dd2f1d7eed6", + "reference": "80850e8d93fb57889ce609e113895dd2f1d7eed6", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=7.2.5", + "psr/http-message": "^1.0 || ^2.0" + }, + "require-dev": { + "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", + "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", + "squizlabs/php_codesniffer": "^2.3", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Aws\\Sns\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "http://aws.amazon.com" + } + ], + "description": "Amazon SNS message validation for PHP", + "homepage": "http://aws.amazon.com/sdkforphp", + "keywords": [ + "SNS", + "amazon", + "aws", + "cloud", + "message", + "sdk", + "webhooks" + ], + "support": { + "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "issues": "https://github.com/aws/aws-sns-message-validator/issues", + "source": "https://github.com/aws/aws-php-sns-message-validator/tree/1.9.1" + }, + "time": "2025-02-25T22:19:51+00:00" + }, { "name": "aws/aws-sdk-php", "version": "3.337.3", @@ -2721,6 +2780,72 @@ }, "time": "2025-02-06T18:54:20+00:00" }, + { + "name": "jdavidbakr/mail-tracker", + "version": "7.18", + "source": { + "type": "git", + "url": "https://github.com/jdavidbakr/mail-tracker.git", + "reference": "1e5d135840769ad8573591e3619810b39da05bba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdavidbakr/mail-tracker/zipball/1e5d135840769ad8573591e3619810b39da05bba", + "reference": "1e5d135840769ad8573591e3619810b39da05bba", + "shasum": "" + }, + "require": { + "aws/aws-php-sns-message-validator": "^1.8", + "aws/aws-sdk-php": "^3.258", + "guzzlehttp/guzzle": "^7.2", + "illuminate/support": "^10.0|^11.0|^12.0", + "php": "^8.1" + }, + "require-dev": { + "mockery/mockery": "^1.4.4", + "orchestra/testbench": "^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.5.10|^10.5|^11.5.3" + }, + "suggest": { + "fedeisas/laravel-mail-css-inliner": "Automatically inlines CSS into all outgoing mail." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "jdavidbakr\\MailTracker\\MailTrackerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "jdavidbakr\\MailTracker\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "J David Baker", + "email": "me@jdavidbaker.com", + "homepage": "http://www.jdavidbaker.com", + "role": "Developer" + } + ], + "description": "Logs and tracks all outgoing emails from Laravel", + "homepage": "https://github.com/jdavidbakr/MailTracker", + "keywords": [ + "MailTracker", + "jdavidbakr" + ], + "support": { + "issues": "https://github.com/jdavidbakr/mail-tracker/issues", + "source": "https://github.com/jdavidbakr/mail-tracker/tree/7.18" + }, + "time": "2025-02-25T17:58:11+00:00" + }, { "name": "jenssegers/agent", "version": "v2.6.4", @@ -15846,6 +15971,6 @@ "platform": { "php": "^8.3" }, - "platform-dev": {}, + "platform-dev": [], "plugin-api-version": "2.6.0" } diff --git a/config/cors.php b/config/cors.php index 8a39e6daa6..6d0570dac6 100644 --- a/config/cors.php +++ b/config/cors.php @@ -15,11 +15,15 @@ | */ - 'paths' => ['api/*', 'sanctum/csrf-cookie'], + 'paths' => ['api/*', 'sanctum/csrf-cookie', 'oauth/*'], 'allowed_methods' => ['*'], - 'allowed_origins' => ['*'], + 'allowed_origins' => [ + 'http://localhost:4200', + 'https://legendary-adventure-2n21ppv.pages.github.io', + 'https://processmaker.github.io', + ], 'allowed_origins_patterns' => [], @@ -29,6 +33,6 @@ 'max_age' => 0, - 'supports_credentials' => false, + 'supports_credentials' => true, ]; diff --git a/config/horizon.php b/config/horizon.php index 7b1a5f8d87..ab9e7e9dad 100644 --- a/config/horizon.php +++ b/config/horizon.php @@ -148,7 +148,7 @@ 'balance' => env('PM4_HORIZON_SUPERVISOR_BPMN_BALANCE', 'simple'), 'tries' => env('PM4_HORIZON_SUPERVISOR_BPMN_TRIES', 3), 'timeout' => env('PM4_HORIZON_SUPERVISOR_BPMN_TIMEOUT', 3600), - 'sleep' => intval(env('BPMN_QUEUE_INTERVAL', 3000)) * 0.001, + 'sleep' => intval(env('BPMN_QUEUE_INTERVAL', 1000)) * 0.001, 'minProcesses' => env('PM4_HORIZON_SUPERVISOR_BPMN_MIN_PROCESSES', 1), 'maxProcesses' => env('PM4_HORIZON_SUPERVISOR_BPMN_MAX_PROCESSES', 1), ], @@ -170,7 +170,7 @@ 'balance' => env('PM4_HORIZON_SUPERVISOR_BPMN_BALANCE', 'false'), 'tries' => env('PM4_HORIZON_SUPERVISOR_BPMN_TRIES', 3), 'timeout' => env('PM4_HORIZON_SUPERVISOR_BPMN_TIMEOUT', 3600), - 'sleep' => intval(env('BPMN_QUEUE_INTERVAL', 3000)) * 0.001, + 'sleep' => intval(env('BPMN_QUEUE_INTERVAL', 1000)) * 0.001, 'minProcesses' => env('PM4_HORIZON_SUPERVISOR_BPMN_MIN_PROCESSES', 1), 'maxProcesses' => env('PM4_HORIZON_SUPERVISOR_BPMN_MAX_PROCESSES', 1), ], @@ -192,7 +192,7 @@ 'balance' => env('PM4_HORIZON_SUPERVISOR_BPMN_BALANCE', 'false'), 'tries' => env('PM4_HORIZON_SUPERVISOR_BPMN_TRIES', 3), 'timeout' => env('PM4_HORIZON_SUPERVISOR_BPMN_TIMEOUT', 3600), - 'sleep' => intval(env('BPMN_QUEUE_INTERVAL', 3000)) * 0.001, + 'sleep' => intval(env('BPMN_QUEUE_INTERVAL', 1000)) * 0.001, 'minProcesses' => env('PM4_HORIZON_SUPERVISOR_BPMN_MIN_PROCESSES', 1), 'maxProcesses' => env('PM4_HORIZON_SUPERVISOR_BPMN_MAX_PROCESSES', 1), ], diff --git a/config/mail-tracker.php b/config/mail-tracker.php new file mode 100644 index 0000000000..595c4d93ca --- /dev/null +++ b/config/mail-tracker.php @@ -0,0 +1,107 @@ + true, + + /** + * To disable injecting tracking links, set this to false. + */ + 'track-links' => true, + + /** + * Optionally expire old emails, set to 0 to keep forever. + */ + 'expire-days' => 60, + + /** + * Where should the pingback URL route be? + */ + 'route' => [ + 'prefix' => 'email', + 'middleware' => ['api'], + ], + + /** + * If we get a link click without a URL, where should we send it to? + */ + 'redirect-missing-links-to' => '/', + + /** + * Where should the admin route be? + */ + 'admin-route' => [ + 'enabled' => true, // Should the admin routes be enabled? + 'prefix' => 'email-manager', + 'middleware' => [ + 'web', + 'can:see-sent-emails' + ], + ], + + /** + * Admin Template + * example + * 'name' => 'layouts.app' for Default emailTraking use 'emailTrakingViews::layouts.app' + * 'section' => 'content' for Default emailTraking use 'content' + * 'styles_section' => 'styles' for Default emailTraking use 'styles' + */ + 'admin-template' => [ + 'name' => 'emailTrakingViews::layouts.app', + 'section' => 'content', + ], + + /** + * Number of emails per page in the admin view + */ + 'emails-per-page' => 30, + + /** + * Date Format + */ + 'date-format' => 'Y-m-d H:i:s', + + /** + * Default database connection name (optional - use null for default) + */ + 'connection' => null, + + /** + * The SNS notification topic - if set, discard all notifications not in this topic. + */ + 'sns-topic' => null, + + /** + * Determines whether the body of the email is logged in the sent_emails table + */ + 'log-content' => true, + + /** + * Determines whether the body should be stored in a file instead of database + * Can be either 'database' or 'filesystem' + */ + 'log-content-strategy' => 'database', + + /** + * What filesystem we use for storing content html files + */ + 'tracker-filesystem' => null, + 'tracker-filesystem-folder' => 'mail-tracker', + + /** + * What queue should we dispatch our tracking jobs to? Null will use the default queue. + */ + 'tracker-queue' => null, + + /** + * Size limit for content length stored in database + */ + 'content-max-size' => 65535, + + /** + * Length of time to default past email search - if set, will set the default past limit to the amount of days below (Ex: => 356) + */ + 'search-date-start' => null, +]; diff --git a/database/migrations/2025_04_17_005609_update_oauth_clients_secret_nullable.php b/database/migrations/2025_04_17_005609_update_oauth_clients_secret_nullable.php new file mode 100644 index 0000000000..08e05d6987 --- /dev/null +++ b/database/migrations/2025_04_17_005609_update_oauth_clients_secret_nullable.php @@ -0,0 +1,27 @@ +string('secret', 100)->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('oauth_clients', function (Blueprint $table) { + $table->string('secret', 100)->nullable(false)->change(); + }); + } +}; diff --git a/database/processes/templates/IntermediateTimerEventFEEL.bpmn b/database/processes/templates/IntermediateTimerEventFEEL.bpmn new file mode 100644 index 0000000000..097eb4f440 --- /dev/null +++ b/database/processes/templates/IntermediateTimerEventFEEL.bpmn @@ -0,0 +1,100 @@ + + + + + + + + + + _8 + + + + _8 + _9 + + + _9 + _10 + + + date ~ "T" ~ time + + + + _10 + _11 + + + + + + + _11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/helpers.php b/helpers.php index 18d71f72e0..8ae1486ae3 100644 --- a/helpers.php +++ b/helpers.php @@ -1,6 +1,7 @@ $method, + 'url' => $url, + 'time' => date('Y-m-d H:i:s'), + 'status' => $status, + 'details' => $details, + ]; + + Cache::put($key, $entries, 60 * 5); +} diff --git a/resources/stubs/api2typescript/api-index.blade.php b/resources/stubs/api2typescript/api-index.blade.php new file mode 100644 index 0000000000..565d644764 --- /dev/null +++ b/resources/stubs/api2typescript/api-index.blade.php @@ -0,0 +1,9 @@ +@foreach ($apiClasses as $class) +import { {{ $class['className'] }} } from './{{ $class['tagLower'] }}.api'; +@endforeach + +export { +@foreach ($apiClasses as $class) + {{ $class['className'] }}, +@endforeach +}; \ No newline at end of file diff --git a/resources/stubs/api2typescript/api-response.blade.php b/resources/stubs/api2typescript/api-response.blade.php new file mode 100644 index 0000000000..94f8936f7f --- /dev/null +++ b/resources/stubs/api2typescript/api-response.blade.php @@ -0,0 +1,7 @@ +export interface ApiResponse { + data?: T; + status?: number; + statusText?: string; + error?: string; + message?: string; +} diff --git a/resources/stubs/api2typescript/api-spec.blade.php b/resources/stubs/api2typescript/api-spec.blade.php new file mode 100644 index 0000000000..fcbd47fc20 --- /dev/null +++ b/resources/stubs/api2typescript/api-spec.blade.php @@ -0,0 +1,37 @@ +import { {{ $className }} } from './{{ $tagLower }}.api'; + +describe('{{ $className }}', () => { + // Mock API client + const mockApiClient = { + head: vi.fn(), + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + delete: vi.fn(), + patch: vi.fn(), + }; + + // Create instance + let api: {{ $className }}; + + beforeEach(() => { + api = new {{ $className }}(mockApiClient); + vi.resetAllMocks(); + }); + +@foreach ($methods as $method) + + describe('{{ $method['methodName'] }}', () => { + it('{{ $method['summary'] }}', async () => { + const mockResponse = {!! $helper->json($helper->mockResponse($method), 6) !!}; + mockApiClient.{{ $method['httpMethod'] }}.mockResolvedValue(mockResponse); + + const result = await api.{{ $method['methodName'] }}({!! $helper->mockParams($method) !!}); + + expect(mockApiClient.{{ $method['httpMethod'] }}).toHaveBeenCalledWith({!! $helper->mockUrl($method) !!}); + expect(result).toEqual(mockResponse); + }); + }); + +@endforeach +}); diff --git a/resources/stubs/api2typescript/api.blade.php b/resources/stubs/api2typescript/api.blade.php new file mode 100644 index 0000000000..ec4972cd80 --- /dev/null +++ b/resources/stubs/api2typescript/api.blade.php @@ -0,0 +1,69 @@ +@if (!empty($imports)) +import { +@foreach ($imports as $import) + {{ $import }}, +@endforeach +} from '../types/types'; +@endif + +export class {{ $className }} { + constructor(private apiClient: { + head: (endpoint: string) => Promise; + get: (endpoint: string) => Promise; + post: (endpoint: string, data: Record) => Promise; + put: (endpoint: string, data: Record) => Promise; + delete: (endpoint: string) => Promise; + patch: (endpoint: string, data: Record) => Promise; + }) {} + +@foreach ($methods as $method) + /** + * {{ $method['summary'] }} + */ + {!! $method['methodName'] !!}({!! implode(', ', $method['paramList']) !!}): Promise<{!! $method['returnType'] ?: 'void' !!}> { +@if (!empty($method['queryParams'])) + const queryParams = new URLSearchParams(); + + if (params) { +@foreach ($method['queryParams'] as $param) +@php +$paramName = Illuminate\Support\Str::camel($param['name']); +$paramType = $helper->getTypescriptType($param); +if ($paramType === 'boolean') { + $paramValue = "params.{$paramName} ? '1' : '0'"; +} elseif ($paramType === 'integer' || $paramType === 'number') { + $paramValue = "String(params.{$paramName})"; +} elseif ($paramType === 'array' || $paramType === 'string[]') { + $paramValue = "params.{$paramName}.join(',')"; +} elseif ($paramType === 'string') { + $paramValue = "params.{$paramName}"; +} else { + $paramValue = "JSON.stringify(params.{$paramName})"; +} +@endphp + if (params.{{ $paramName }}) queryParams.append('{{ $param['name'] }}', {!! $paramValue !!}); // {!! $paramType !!} +@endforeach + } + + const queryString = queryParams.toString() ? `?${queryParams.toString()}` : ''; +@endif + +@if ($method['httpMethod'] === 'get' || $method['httpMethod'] === 'head') +@if (!empty($method['queryParams'])) + return this.apiClient.{{ $method['httpMethod'] }}<{!! $method['returnType'] ?: 'void' !!}>(`{{ $method['apiPath'] }}${queryString}`); +@else + return this.apiClient.{{ $method['httpMethod'] }}<{!! $method['returnType'] ?: 'void' !!}>(`{{ $method['apiPath'] }}`); +@endif +@elseif ($method['httpMethod'] === 'post' ||$method['httpMethod'] === 'put' || $method['httpMethod'] === 'patch') +@if (!empty($method['queryParams'])) + return this.apiClient.{{$method['httpMethod']}}<{!! $method['returnType'] ?: 'void' !!}>(`{{ $method['apiPath'] }}${queryString}`, data as unknown as Record); +@else + return this.apiClient.{{$method['httpMethod']}}<{!! $method['returnType'] ?: 'void' !!}>(`{{ $method['apiPath'] }}`, data as unknown as Record); +@endif +@elseif ($method['httpMethod'] === 'delete') + return this.apiClient.delete<{!! $method['returnType'] ?: 'void' !!}>(`{{ $method['apiPath'] }}`); +@endif + } + +@endforeach +} diff --git a/resources/stubs/api2typescript/composable.blade.php b/resources/stubs/api2typescript/composable.blade.php new file mode 100644 index 0000000000..eb2d32ce01 --- /dev/null +++ b/resources/stubs/api2typescript/composable.blade.php @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { {{ $className }} } from '../api/{{ $tagLower }}.api'; + +/** + * Hook to access ProcessMaker {{ ucfirst($tagLower) }} API + * @param apiClient - API client with authentication handling + * @returns {{ $className }} instance + */ +export const {{ $hookName }} = (apiClient: { + head: (endpoint: string) => Promise; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + get: (endpoint: string) => Promise; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + post: (endpoint: string, data: Record) => Promise; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + put: (endpoint: string, data: Record) => Promise; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + delete: (endpoint: string) => Promise; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + patch: (endpoint: string, data: Record) => Promise; +}) => { + return new {{ $className }}(apiClient); +}; diff --git a/resources/stubs/api2typescript/composables-index.blade.php b/resources/stubs/api2typescript/composables-index.blade.php new file mode 100644 index 0000000000..65423275ed --- /dev/null +++ b/resources/stubs/api2typescript/composables-index.blade.php @@ -0,0 +1,9 @@ +@foreach ($hooks as $hook) +import { {{ $hook }} } from './{{ $hook }}'; +@endforeach + +export { +@foreach ($hooks as $hook) + {{ $hook }}, +@endforeach +}; diff --git a/resources/stubs/api2typescript/main-index.blade.php b/resources/stubs/api2typescript/main-index.blade.php new file mode 100644 index 0000000000..fb1cae5fa9 --- /dev/null +++ b/resources/stubs/api2typescript/main-index.blade.php @@ -0,0 +1,6 @@ +// Generated ProcessMaker SDK +export * from './lib/processmaker-sdk' +export * from './composables'; +export * from './engine'; +export * from './types/types'; +export * from './api'; diff --git a/resources/stubs/api2typescript/readme.blade.php b/resources/stubs/api2typescript/readme.blade.php new file mode 100644 index 0000000000..dfd538da67 --- /dev/null +++ b/resources/stubs/api2typescript/readme.blade.php @@ -0,0 +1,45 @@ +# ProcessMaker SDK + +This SDK provides typed access to the ProcessMaker API. + +## Features + +- Fully typed API interfaces +- Composable hooks for easy integration +@foreach ($tags as $tag) +- Complete implementation of the ProcessMaker {{ ucfirst(strtolower($tag)) }} API +@endforeach + +## Usage + +@if ($firstTag) +### {{ ucfirst(strtolower($firstTag)) }} API + +```typescript +import { useProcessMakerApi } from '~/composables/useProcessMakerApi'; +import { useProcessMaker{{ ucfirst(strtolower($firstTag)) }} } from 'shared'; + +// In your component or service +const api = useProcessMakerApi(); +const {{ strtolower($firstTag) }}Api = useProcessMaker{{ ucfirst(strtolower($firstTag)) }}(api); + +// Example: Get all {{ strtolower($firstTag) }} +const get{{ ucfirst(strtolower($firstTag)) }} = async () => { + try { + const response = await {{ strtolower($firstTag) }}Api.get{{ ucfirst(strtolower($firstTag)) }}(); + return response.data; + } catch (error) { + console.error('Error fetching {{ strtolower($firstTag) }}:', error); + throw error; + } +}; +``` +@endif + +## Available APIs + +Currently, the SDK includes: + +@foreach ($tags as $tag) +- **{{ ucfirst(strtolower($tag)) }} API**: Complete implementation of the ProcessMaker {{ ucfirst(strtolower($tag)) }} API +@endforeach diff --git a/resources/stubs/api2typescript/types.blade.php b/resources/stubs/api2typescript/types.blade.php new file mode 100644 index 0000000000..fc7c32125c --- /dev/null +++ b/resources/stubs/api2typescript/types.blade.php @@ -0,0 +1,10 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +@foreach ($interfaces as $interface) +{!! $interface !!} + +@endforeach + +@foreach ($queryParamInterfaces as $interface) +{!! $interface !!} + +@endforeach diff --git a/resources/views/vendor/emailTrakingViews/emails/mensaje.blade.php b/resources/views/vendor/emailTrakingViews/emails/mensaje.blade.php new file mode 100644 index 0000000000..1e4dd69edc --- /dev/null +++ b/resources/views/vendor/emailTrakingViews/emails/mensaje.blade.php @@ -0,0 +1,30 @@ +@extends('emailTrakingViews::emails/mensaje_layout') +@section('title') + Message from {{config('mail-tracker.name')}} +@endsection + +@section('preheader') + Message from {{config('mail-tracker.name')}}
+@endsection +@section('nombre_destinatario') + {{ $data['name'] }} +@endsection +@section('mensaje') +

Static Email Title

+

+ Static Email Content +

+ {{ $data['message'] }} +@endsection +@section('href_call_to_action') + {{env('APP_URL')}} +@endsection +@section('txt_call_to_action') + Call To Action +@endsection +@section('txt_extra') + This email comes from {{config('mail-tracker.name')}} +@endsection +@section('saludo_final') + Regards +@endsection diff --git a/resources/views/vendor/emailTrakingViews/emails/mensaje_layout.blade.php b/resources/views/vendor/emailTrakingViews/emails/mensaje_layout.blade.php new file mode 100644 index 0000000000..1d1f0c4259 --- /dev/null +++ b/resources/views/vendor/emailTrakingViews/emails/mensaje_layout.blade.php @@ -0,0 +1,195 @@ + + + + + + @yield('title') + + + + + + + + + +
  +
+ + + + + + + + + + + +
+ + + + + + + +
+ + {{config('mail-tracker.name')}} + +
+

Hi @yield('nombre_destinatario')

+

@yield('mensaje')

+ + + + + + +
+ + + + + + +
+ @yield('txt_call_to_action') + +
+
+

@yield('txt_extra')

+

@yield('saludo_final')

+
+
+ + + + + + +
+
 
+ + diff --git a/resources/views/vendor/emailTrakingViews/index.blade.php b/resources/views/vendor/emailTrakingViews/index.blade.php new file mode 100644 index 0000000000..5de646dcce --- /dev/null +++ b/resources/views/vendor/emailTrakingViews/index.blade.php @@ -0,0 +1,112 @@ +@extends(config('mail-tracker.admin-template.name')) +@section(config('mail-tracker.admin-template.section')) +
+
+
+

Mail Tracker

+
+
+
+
+

+ SNS Endpoint: {{ route('mailTracker_SNS') }} +

+
+
+
+
+
+ {!! csrf_field() !!} +
+
+ + +
+
+ @if( !is_null(config('mail-tracker.search-date-start')) ) +
+
+ + +
+
+
+
+ + +
+
+ @endif +
+ + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + @foreach($emails as $email) + + + + + + + + + + + + + @endforeach +
SMTPRecipientSubjectFirst ViewOpensFirst ClickClicksSent AtView EmailClick Report
+ + {{ Str::limit($email->smtp_info, 20) }} + + {{$email->recipient}}{{$email->subject}}{{$email->opened_at}}{{$email->opens}}{{$email->clicked_at}}{{$email->clicks}}{{$email->created_at->format(config('mail-tracker.date-format'))}} + + View + + + @if($email->clicks > 0) + Url Report + @else + No Clicks + @endif +
+
+
+
+
+ {!! $emails->render() !!} +
+
+
+@endsection diff --git a/resources/views/vendor/emailTrakingViews/layouts/app.blade.php b/resources/views/vendor/emailTrakingViews/layouts/app.blade.php new file mode 100644 index 0000000000..9d55d24bb7 --- /dev/null +++ b/resources/views/vendor/emailTrakingViews/layouts/app.blade.php @@ -0,0 +1,13 @@ + + + + + + + Mail Tracker + + + + @yield('content') + + \ No newline at end of file diff --git a/resources/views/vendor/emailTrakingViews/show.blade.php b/resources/views/vendor/emailTrakingViews/show.blade.php new file mode 100644 index 0000000000..9c4e553dc5 --- /dev/null +++ b/resources/views/vendor/emailTrakingViews/show.blade.php @@ -0,0 +1 @@ +{!!$email->content!!} diff --git a/resources/views/vendor/emailTrakingViews/smtp_detail.blade.php b/resources/views/vendor/emailTrakingViews/smtp_detail.blade.php new file mode 100644 index 0000000000..2989f94107 --- /dev/null +++ b/resources/views/vendor/emailTrakingViews/smtp_detail.blade.php @@ -0,0 +1,33 @@ +@extends(config('mail-tracker.admin-template.name')) +@section(config('mail-tracker.admin-template.section')) +
+
+
+

Mail Tracker

+
+
+

+ All Sent Emails +

+
+
+
+
+

+ SMTP detail for Email ID {{$details->id}} +

+ + View Message + +
+
+
+
+ Recipient: {{$details->recipient}}
+ Subject: {{$details->subject}}
+ Sent At: {{$details->created_at->format(config('mail-tracker.date-format'))}}
+ SMTP Details: {{ $details->smtp_info }} +
+
+
+@endsection diff --git a/resources/views/vendor/emailTrakingViews/url_detail.blade.php b/resources/views/vendor/emailTrakingViews/url_detail.blade.php new file mode 100644 index 0000000000..45e3449a39 --- /dev/null +++ b/resources/views/vendor/emailTrakingViews/url_detail.blade.php @@ -0,0 +1,50 @@ +@extends(config('mail-tracker.admin-template.name')) +@section(config('mail-tracker.admin-template.section')) +
+
+
+

Mail Tracker

+
+
+

+ All Sent Emails +

+
+
+
+
+

+ Clicked URLs for Email ID {{$details->first()->email->id}} +

+ + View Message + +
+
+
+
+ Recipient: {{$details->first()->email->recipient}}
+ Subject: {{$details->first()->email->subject}}
+ Sent At: {{$details->first()->email->created_at->format(config('mail-tracker.date-format'))}} +
+
+
+
+ + + + + + @foreach($details as $detail) + + + + + + + @endforeach +
UrlClicksFirst Click AtLast Click At
{{$detail->url}}{{$detail->clicks}}{{$detail->created_at->format(config('mail-tracker.date-format'))}}{{$detail->updated_at->format(config('mail-tracker.date-format'))}}
+
+
+
+@endsection diff --git a/routes/api.php b/routes/api.php index 1d48e244e3..19e84ccde0 100644 --- a/routes/api.php +++ b/routes/api.php @@ -60,6 +60,8 @@ Route::put('users/update_language', [UserController::class, 'updateLanguage'])->name('users.updateLanguage'); Route::get('users_task_count', [UserController::class, 'getUsersTaskCount'])->name('users.users_task_count') ->middleware('can:view-users'); + Route::get('users/current', [UserController::class, 'current'])->name('users.current'); + Route::post('users/logout', [UserController::class, 'logout'])->name('users.logout'); // User Groups Route::put('users/{user}/groups', [UserController::class, 'updateGroups'])->name('users.groups.update')->middleware('can:edit-users'); @@ -240,7 +242,12 @@ Route::put('requests/{request}/retry', [ProcessRequestController::class, 'retry'])->name('requests.retry')->middleware('can:update,request'); Route::delete('requests/{request}', [ProcessRequestController::class, 'destroy'])->name('requests.destroy')->middleware('can:destroy,request'); Route::get('requests/{request}/tokens', [ProcessRequestController::class, 'getRequestToken'])->name('requests.getRequestToken')->middleware('can:view,request'); - Route::post('requests/{request}/events/{event}', [ProcessRequestController::class, 'activateIntermediateEvent'])->name('requests.update,request'); + Route::post('requests/{request}/events/{event}', [ProcessRequestController::class, 'activateIntermediateEvent']) + ->name('requests.update,request') + ->where('event', '[a-zA-Z0-9_\-\.]+'); + Route::post('requests/{request}/boundary/{boundary_event}', [ProcessRequestController::class, 'triggerBoundaryEvent']) + ->name('requests.triggerBoundaryEvent') + ->where('boundary_event', '[a-zA-Z0-9_\-\.]+'); Route::get('requests/{request}/details-screen-request', [ProcessRequestController::class, 'screenRequested'])->name('requests.detail.screen')->middleware('can:view,request'); Route::get('requests/{request}/end-event-destination', [ProcessRequestController::class, 'endEventDestination'])->name('requests.end_event_destination')->middleware('can:view,request'); diff --git a/routes/channels.php b/routes/channels.php index eb10a61a18..b3fdd35369 100644 --- a/routes/channels.php +++ b/routes/channels.php @@ -33,6 +33,22 @@ || $request->process?->manager_id === $user->id; }); +Broadcast::channel('ProcessMaker.Models.ProcessRequest.{id}.SubProcesses', function ($user, $id) { + if ($id === 'undefined' || $user === 'undefined') { + return; + } + + if ($user->is_administrator) { + return true; + } + + $request = ProcessRequest::find($id); + + return $request->user_id === $user->id + || !empty($request->participants()->where('users.id', $user->getKey())->first()) + || $request->process?->manager_id === $user->id; +}); + Broadcast::channel('ProcessMaker.Models.ProcessRequestToken.{id}', function ($user, $id) { if ($user->is_administrator) { return true; diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index 3755004f8f..9373a2878e 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -18,14 +18,14 @@ } ], "paths": { - "/collections": { + "/decision_table_categories": { "get": { "tags": [ - "Collections" + "DecisionTableCategories" ], - "summary": "Returns all collections that the user has access to", - "description": "Get a list of Collections.", - "operationId": "getCollections", + "summary": "Returns all Decision Tables categories that the user has access to", + "description": "Display a listing of the Decision Tables Categories.", + "operationId": "getDecisionTableCategories", "parameters": [ { "$ref": "#/components/parameters/filter" @@ -38,14 +38,11 @@ }, { "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "list of collections", + "description": "list of Decision Tables categories", "content": { "application/json": { "schema": { @@ -53,11 +50,16 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/DecisionTableCategory" } }, "meta": { - "$ref": "#/components/schemas/metadata" + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/metadata" + } + ] } }, "type": "object" @@ -69,17 +71,17 @@ }, "post": { "tags": [ - "Collections" + "DecisionTableCategories" ], - "summary": "Save a new collections", - "description": "Create a new Collection.", - "operationId": "createCollection", + "summary": "Save a new Decision Table Category", + "description": "Store a newly created Decision Tables Category in storage", + "operationId": "createDecisionTableCategory", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collectionsEditable" + "$ref": "#/components/schemas/decisionTableCategoryEditable" } } } @@ -90,7 +92,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/DecisionTableCategory" } } } @@ -98,19 +100,19 @@ } } }, - "/collections/{collection_id}": { + "/decision_table_categories/{decision_table_categories_id}": { "get": { "tags": [ - "Collections" + "DecisionTableCategories" ], - "summary": "Get single collections by ID", - "description": "Get a single Collection.", - "operationId": "getCollectionById", + "summary": "Get single Decision Table category by ID", + "description": "Display the specified decision Tables category.", + "operationId": "getDecisionTableCategoryById", "parameters": [ { - "name": "collection_id", + "name": "decision_table_categories_id", "in": "path", - "description": "ID of collection to return", + "description": "ID of Decision Table category to return", "required": true, "schema": { "type": "string" @@ -119,11 +121,11 @@ ], "responses": { "200": { - "description": "Successfully found the collections", + "description": "Successfully found the Decision Table", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/DecisionTableCategory" } } } @@ -132,16 +134,16 @@ }, "put": { "tags": [ - "Collections" + "DecisionTableCategories" ], - "summary": "Update a collection", - "description": "Update a Collection.", - "operationId": "updateCollection", + "summary": "Update a Decision Table Category", + "description": "Updates the current element", + "operationId": "updateDecisionTableCategory", "parameters": [ { - "name": "collection_id", + "name": "decision_table_categories_id", "in": "path", - "description": "ID of collection to update", + "description": "ID of Decision Table category to return", "required": true, "schema": { "type": "string" @@ -153,126 +155,36 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collectionsEditable" - } - } - } - }, - "responses": { - "204": { - "description": "success" - } - } - }, - "delete": { - "tags": [ - "Collections" - ], - "summary": "Delete a collection", - "description": "Delete a Collection.", - "operationId": "deleteCollection", - "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of collection to return", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "success" - } - } - } - }, - "/collections/{collection_id}/export": { - "post": { - "tags": [ - "Screens" - ], - "summary": "Trigger export collections job", - "description": "Export the specified collection.", - "operationId": "exportCollection", - "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of the collection to export", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "202": { - "description": "success" - } - } - } - }, - "/collections/import": { - "post": { - "tags": [ - "Collections" - ], - "summary": "Import a new collection", - "description": "Import the specified collection.", - "operationId": "importCollection", - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "required": [ - "file" - ], - "properties": { - "file": { - "description": "file to upload", - "type": "file", - "format": "file" - } - }, - "type": "object" + "$ref": "#/components/schemas/decisionTableCategoryEditable" } } } }, "responses": { - "201": { + "200": { "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/collections" + "$ref": "#/components/schemas/DecisionTableCategory" } } } - }, - "200": { - "description": "success" } } - } - }, - "/collections/{collection_id}/truncate": { + }, "delete": { "tags": [ - "Collections" + "DecisionTableCategories" ], - "summary": "Deletes all records in a collection", - "description": "Truncate a Collection.", - "operationId": "truncateCollection", + "summary": "Delete a Decision Table category", + "description": "Remove the specified resource from storage.", + "operationId": "deleteDecisionTableCategory", "parameters": [ { - "name": "collection_id", + "name": "decision_table_categories_id", "in": "path", - "description": "ID of collection to truncate", + "description": "ID of Decision Table category to return", "required": true, "schema": { "type": "string" @@ -286,34 +198,15 @@ } } }, - "/collections/{collection_id}/records": { + "/decision_tables": { "get": { "tags": [ - "Collections" + "DecisionTables" ], - "summary": "Returns all records", - "description": "Get the list of records of a collection.", - "operationId": "getRecords", + "summary": "Returns all Decision tables that the user has access to", + "description": "Display a listing of the resource.", + "operationId": "getDecisionTables", "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of collection to get records for", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "pmql", - "in": "query", - "schema": { - "type": "string" - } - }, - { - "$ref": "#/components/parameters/per_page" - }, { "$ref": "#/components/parameters/filter" }, @@ -323,13 +216,16 @@ { "$ref": "#/components/parameters/order_direction" }, + { + "$ref": "#/components/parameters/per_page" + }, { "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "list of records of a collection", + "description": "list of Decision Tables", "content": { "application/json": { "schema": { @@ -337,11 +233,16 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/records" + "$ref": "#/components/schemas/decisionTable" } }, "meta": { - "$ref": "#/components/schemas/metadata" + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/metadata" + } + ] } }, "type": "object" @@ -353,28 +254,17 @@ }, "post": { "tags": [ - "Collections" - ], - "summary": "Save a new record in a collection", - "description": "Create a new record in a Collection.", - "operationId": "createRecord", - "parameters": [ - { - "name": "collection_id", - "in": "path", - "description": "ID of the collection", - "required": true, - "schema": { - "type": "string" - } - } + "DecisionTables" ], + "summary": "Save a new Decision Table", + "description": "Store a newly created resource in storage.", + "operationId": "createDecisionTable", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/recordsEditable" + "$ref": "#/components/schemas/decisionTableEditable" } } } @@ -385,7 +275,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/records" + "$ref": "#/components/schemas/decisionTable" } } } @@ -393,28 +283,19 @@ } } }, - "/collections/{collection_id}/records/{record_id}": { + "/decision_tables/{decision_table_id}": { "get": { "tags": [ - "Collections" + "DecisionTables" ], - "summary": "Get single record of a collection", - "description": "Get a single record of a Collection.", - "operationId": "getRecordById", + "summary": "Get single Decision Table by ID", + "description": "Display the specified resource.", + "operationId": "getDecisionTableById", "parameters": [ { - "name": "collection_id", - "in": "path", - "description": "ID of the collection", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "record_id", + "name": "decision_table_id", "in": "path", - "description": "ID of the record to return", + "description": "ID of Decision Table to return", "required": true, "schema": { "type": "string" @@ -423,11 +304,11 @@ ], "responses": { "200": { - "description": "Successfully found the record", + "description": "Successfully found the Decision Table", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/records" + "$ref": "#/components/schemas/decisionTable" } } } @@ -436,25 +317,16 @@ }, "put": { "tags": [ - "Collections" + "DecisionTables" ], - "summary": "Update a record", - "description": "Update a record in a Collection.", - "operationId": "updateRecord", + "summary": "Update a Decision Table", + "description": "Update a Decision table", + "operationId": "updateDecisionTable", "parameters": [ { - "name": "collection_id", - "in": "path", - "description": "ID of collection", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "record_id", + "name": "decision_table_id", "in": "path", - "description": "ID of the record ", + "description": "ID of Decision Table to return", "required": true, "schema": { "type": "string" @@ -466,38 +338,36 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/recordsEditable" + "$ref": "#/components/schemas/decisionTableEditable" } } } }, "responses": { "204": { - "description": "success" + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/decisionTable" + } + } + } } } }, "delete": { "tags": [ - "Collections" + "DecisionTables" ], - "summary": "Delete a collection record", - "description": "Delete a record of a Collection.", - "operationId": "deleteRecord", + "summary": "Delete a Decision Table", + "description": "Delete a Decision tables", + "operationId": "deleteDecisionTable", "parameters": [ { - "name": "collection_id", - "in": "path", - "description": "ID of collection", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "record_id", + "name": "decision_table_id", "in": "path", - "description": "ID of record in collection", + "description": "ID of Decision Table to return", "required": true, "schema": { "type": "string" @@ -506,31 +376,74 @@ ], "responses": { "204": { - "description": "success" + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/decisionTable" + } + } + } } } - }, - "patch": { + } + }, + "/decision_tables/{decision_table_id}/duplicate": { + "put": { "tags": [ - "Collections" + "DecisionTables" ], - "summary": "Partial update of a record", - "description": "Implements a partial update of a record in a Collection.", - "operationId": "patchRecord", + "summary": "duplicate a Decision Table", + "description": "duplicate a Decision table.", + "operationId": "duplicateDecisionTable", "parameters": [ { - "name": "collection_id", + "name": "decision_table_id", "in": "path", - "description": "ID of collection ", + "description": "ID of Decision Table to return", "required": true, "schema": { "type": "string" } - }, + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/decisionTableEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/decisionTable" + } + } + } + } + } + } + }, + "/decision_tables/{decision_table_id}/excel-import": { + "post": { + "tags": [ + "DecisionTables" + ], + "summary": "Import a new decision table", + "description": "Import a Decision table from excel", + "operationId": "importExcel", + "parameters": [ { - "name": "record_id", + "name": "decision_table_id", "in": "path", - "description": "ID of the record ", + "description": "ID of Decision Table to return", "required": true, "schema": { "type": "string" @@ -540,16 +453,143 @@ "requestBody": { "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/collectionsEditable" + "properties": { + "file": { + "description": "file to import", + "type": "string", + "format": "binary" + } + }, + "type": "object" } } } }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "type": "object" + } + }, + "type": "object" + } + } + } + } + } + } + }, + "/decision_tables/{decision_table_id}/export": { + "post": { + "tags": [ + "DecisionTables" + ], + "summary": "Export a single Decision Table by ID", + "description": "Export the specified screen.", + "operationId": "exportDecisionTable", + "parameters": [ + { + "name": "decision_table_id", + "in": "path", + "description": "ID of Decision Table to return", + "required": true, + "schema": { + "type": "string" + } + } + ], "responses": { "200": { - "description": "success" + "description": "Successfully exported the decision table", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DecisionTableExported" + } + } + } + } + } + } + }, + "/decision_tables/import": { + "post": { + "tags": [ + "DecisionTables" + ], + "summary": "Import a new Decision Table", + "description": "Import the specified Decision Table.", + "operationId": "importDecisionTable", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "properties": { + "file": { + "description": "file to import", + "type": "string", + "format": "binary" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "properties": { + "status": { + "type": "object" + } + }, + "type": "object" + } + } + } + } + } + } + }, + "/decision_tables/{decision_table_id}/execute": { + "post": { + "tags": [ + "DecisionTables" + ], + "summary": "Execute a Decision Table definition", + "description": "Execute a Decision Table definition", + "operationId": "previewDecisionTable", + "parameters": [ + { + "name": "decision_table_id", + "in": "path", + "description": "Decision Table unique Identifier", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully executed", + "content": { + "application/json": { + "schema": {} + } + } } } } @@ -1349,14 +1389,54 @@ } } }, - "/version_histories": { + "/customize-ui": { + "post": { + "tags": [ + "CssSettings" + ], + "summary": "Create or update a new setting", + "description": "Create a new Settings css-override", + "operationId": "updateCssSetting", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "variables": { + "type": "string" + }, + "sansSerifFont": { + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/settings" + } + } + } + } + } + } + }, + "/environment_variables": { "get": { "tags": [ - "Version History" + "Environment Variables" ], - "summary": "Return all version History according to the model", - "description": "Get the list of records of Version History", - "operationId": "getVersionHistories", + "summary": "Returns all environmentVariables that the user has access to. For security, values are not included.", + "description": "Fetch a collection of variables based on paged request and filter if provided", + "operationId": "getEnvironmentVariables", "parameters": [ { "$ref": "#/components/parameters/filter" @@ -1376,7 +1456,7 @@ ], "responses": { "200": { - "description": "list of Version History", + "description": "list of environmentVariables", "content": { "application/json": { "schema": { @@ -1384,16 +1464,11 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/versionHistory" + "$ref": "#/components/schemas/EnvironmentVariable" } }, "meta": { - "type": "object", - "allOf": [ - { - "$ref": "#/components/schemas/metadata" - } - ] + "type": "object" } }, "type": "object" @@ -1405,17 +1480,17 @@ }, "post": { "tags": [ - "Version History" + "Environment Variables" ], - "summary": "Save a new Version History", - "description": "Create a new Version History.", - "operationId": "createVersion", + "summary": "Create a new environment variable", + "description": "Creates a new global Environment Variable in the system", + "operationId": "createEnvironmentVariable", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistoryEditable" + "$ref": "#/components/schemas/EnvironmentVariableEditable" } } } @@ -1426,7 +1501,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistory" + "$ref": "#/components/schemas/EnvironmentVariable" } } } @@ -1434,32 +1509,32 @@ } } }, - "/version_histories/{version_history_id}": { + "/environment_variables/{environment_variable_id}": { "get": { "tags": [ - "Version History" + "Environment Variables" ], - "summary": "Get single Version History by ID", - "description": "Get a single Version History.", - "operationId": "getVersionHistoryById", + "summary": "Get an environment variable by id. For security, the value is not included.", + "description": "Return an environment variable instance\nUsing implicit model binding, will automatically return 404 if variable now found", + "operationId": "getEnvironmentVariableById", "parameters": [ { - "name": "version_history_id", + "name": "environment_variable_id", "in": "path", - "description": "ID of Version History to return", + "description": "ID of environment_variables to return", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], "responses": { - "200": { - "description": "Successfully found the Version History", + "201": { + "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistory" + "$ref": "#/components/schemas/EnvironmentVariable" } } } @@ -1468,19 +1543,19 @@ }, "put": { "tags": [ - "Version History" + "Environment Variables" ], - "summary": "Update a Version History", - "description": "Update a Version History.", - "operationId": "updateVersion", + "summary": "Update an environment variable", + "description": "Update an environment variable", + "operationId": "updateEnvironmentVariable", "parameters": [ { - "name": "version_history_id", + "name": "environment_variable_id", "in": "path", - "description": "ID of Version History to return", + "description": "ID of environment variables to update", "required": true, "schema": { - "type": "string" + "type": "integer" } } ], @@ -1489,18 +1564,18 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistoryEditable" + "$ref": "#/components/schemas/EnvironmentVariableEditable" } } } }, "responses": { - "204": { + "200": { "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/versionHistory" + "$ref": "#/components/schemas/EnvironmentVariable" } } } @@ -1509,116 +1584,36 @@ }, "delete": { "tags": [ - "Version History" + "Environment Variables" ], - "summary": "Delete a Version History", - "description": "Delete a Version History.", - "operationId": "deleteVersion", + "summary": "Delete an environment variable", + "operationId": "deleteEnvironmentVariable", "parameters": [ { - "name": "version_history_id", + "name": "environment_variable_id", "in": "path", - "description": "ID of Version History to return", + "description": "ID of environment_variables to return", "required": true, "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/versionHistory" - } - } - } - } - } - } - }, - "/version_histories/clone": { - "post": { - "tags": [ - "Version History" - ], - "summary": "Clone a new Version History", - "description": "Clone a new Version History.", - "operationId": "cloneVersion", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/versionHistoryEditable" - } - } - } - }, - "responses": { - "201": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/versionHistory" - } - } + "type": "integer" } } - } - } - }, - "/customize-ui": { - "post": { - "tags": [ - "CssSettings" ], - "summary": "Create or update a new setting", - "description": "Create a new Settings css-override", - "operationId": "updateCssSetting", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "variables": { - "type": "string" - }, - "sansSerifFont": { - "type": "string" - } - }, - "type": "object" - } - } - } - }, "responses": { - "201": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/settings" - } - } - } + "200": { + "description": "success" } } } }, - "/environment_variables": { + "/files": { "get": { "tags": [ - "Environment Variables" + "Files" ], - "summary": "Returns all environmentVariables that the user has access to. For security, values are not included.", - "description": "Fetch a collection of variables based on paged request and filter if provided", - "operationId": "getEnvironmentVariables", + "summary": "Returns the list of files", + "description": "Display a listing of the resource.", + "operationId": "getFiles", "parameters": [ { "$ref": "#/components/parameters/filter" @@ -1631,14 +1626,11 @@ }, { "$ref": "#/components/parameters/per_page" - }, - { - "$ref": "#/components/parameters/include" } ], "responses": { "200": { - "description": "list of environmentVariables", + "description": "list of files", "content": { "application/json": { "schema": { @@ -1646,11 +1638,11 @@ "data": { "type": "array", "items": { - "$ref": "#/components/schemas/EnvironmentVariable" + "$ref": "#/components/schemas/media" } }, "meta": { - "type": "object" + "$ref": "#/components/schemas/metadata" } }, "type": "object" @@ -1662,28 +1654,87 @@ }, "post": { "tags": [ - "Environment Variables" + "Files" + ], + "summary": "Save a new media file. Note: To upload files to a request, use createRequestFile in the RequestFile API", + "description": "Store a newly created resource in storage.", + "operationId": "createFile", + "parameters": [ + { + "name": "model_id", + "in": "query", + "description": "ID of the model to which the file will be associated", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "model", + "in": "query", + "description": "Full namespaced class of the model to associate", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "data_name", + "in": "query", + "description": "Name of the variable used in a request", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "collection", + "in": "query", + "description": "Media collection name. For requests, use 'default'", + "required": false, + "schema": { + "type": "string" + } + } ], - "summary": "Create a new environment variable", - "description": "Creates a new global Environment Variable in the system", - "operationId": "createEnvironmentVariable", "requestBody": { "required": true, "content": { - "application/json": { + "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariableEditable" + "properties": { + "file": { + "description": "save a new media file", + "type": "string", + "format": "binary" + } + }, + "type": "object" } } } }, "responses": { - "201": { + "200": { "description": "success", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariable" + "properties": { + "id": { + "type": "string" + }, + "model_id": { + "type": "string" + }, + "file_name": { + "type": "string" + }, + "mime_type": { + "type": "string" + } + }, + "type": "object" } } } @@ -1691,19 +1742,19 @@ } } }, - "/environment_variables/{environment_variable_id}": { + "/files/{file_id}": { "get": { "tags": [ - "Environment Variables" + "Files" ], - "summary": "Get an environment variable by id. For security, the value is not included.", - "description": "Return an environment variable instance\nUsing implicit model binding, will automatically return 404 if variable now found", - "operationId": "getEnvironmentVariableById", + "summary": "Get the metadata of a file. To actually fetch the file see Get File Contents", + "description": "Get a single media file.", + "operationId": "getFileById", "parameters": [ { - "name": "environment_variable_id", + "name": "file_id", "in": "path", - "description": "ID of environment_variables to return", + "description": "ID of the file to return", "required": true, "schema": { "type": "integer" @@ -1711,266 +1762,33 @@ } ], "responses": { - "201": { - "description": "success", + "200": { + "description": "Successfully found the file", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnvironmentVariable" + "$ref": "#/components/schemas/media" } } } + }, + "404": { + "$ref": "#/components/responses/404" } } }, - "put": { + "delete": { "tags": [ - "Environment Variables" + "Files" ], - "summary": "Update an environment variable", - "description": "Update an environment variable", - "operationId": "updateEnvironmentVariable", + "summary": "Delete a media file", + "description": "Remove the specified resource from storage.", + "operationId": "deleteFile", "parameters": [ { - "name": "environment_variable_id", + "name": "file_id", "in": "path", - "description": "ID of environment variables to update", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EnvironmentVariableEditable" - } - } - } - }, - "responses": { - "200": { - "description": "success", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EnvironmentVariable" - } - } - } - } - } - }, - "delete": { - "tags": [ - "Environment Variables" - ], - "summary": "Delete an environment variable", - "operationId": "deleteEnvironmentVariable", - "parameters": [ - { - "name": "environment_variable_id", - "in": "path", - "description": "ID of environment_variables to return", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "success" - } - } - } - }, - "/files": { - "get": { - "tags": [ - "Files" - ], - "summary": "Returns the list of files", - "description": "Display a listing of the resource.", - "operationId": "getFiles", - "parameters": [ - { - "$ref": "#/components/parameters/filter" - }, - { - "$ref": "#/components/parameters/order_by" - }, - { - "$ref": "#/components/parameters/order_direction" - }, - { - "$ref": "#/components/parameters/per_page" - } - ], - "responses": { - "200": { - "description": "list of files", - "content": { - "application/json": { - "schema": { - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/media" - } - }, - "meta": { - "$ref": "#/components/schemas/metadata" - } - }, - "type": "object" - } - } - } - } - } - }, - "post": { - "tags": [ - "Files" - ], - "summary": "Save a new media file. Note: To upload files to a request, use createRequestFile in the RequestFile API", - "description": "Store a newly created resource in storage.", - "operationId": "createFile", - "parameters": [ - { - "name": "model_id", - "in": "query", - "description": "ID of the model to which the file will be associated", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "name": "model", - "in": "query", - "description": "Full namespaced class of the model to associate", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "data_name", - "in": "query", - "description": "Name of the variable used in a request", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "collection", - "in": "query", - "description": "Media collection name. For requests, use 'default'", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": true, - "content": { - "multipart/form-data": { - "schema": { - "properties": { - "file": { - "description": "save a new media file", - "type": "string", - "format": "binary" - } - }, - "type": "object" - } - } - } - }, - "responses": { - "200": { - "description": "success", - "content": { - "application/json": { - "schema": { - "properties": { - "id": { - "type": "string" - }, - "model_id": { - "type": "string" - }, - "file_name": { - "type": "string" - }, - "mime_type": { - "type": "string" - } - }, - "type": "object" - } - } - } - } - } - } - }, - "/files/{file_id}": { - "get": { - "tags": [ - "Files" - ], - "summary": "Get the metadata of a file. To actually fetch the file see Get File Contents", - "description": "Get a single media file.", - "operationId": "getFileById", - "parameters": [ - { - "name": "file_id", - "in": "path", - "description": "ID of the file to return", - "required": true, - "schema": { - "type": "integer" - } - } - ], - "responses": { - "200": { - "description": "Successfully found the file", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/media" - } - } - } - }, - "404": { - "$ref": "#/components/responses/404" - } - } - }, - "delete": { - "tags": [ - "Files" - ], - "summary": "Delete a media file", - "description": "Remove the specified resource from storage.", - "operationId": "deleteFile", - "parameters": [ - { - "name": "file_id", - "in": "path", - "description": "ID of the file", + "description": "ID of the file", "required": true, "schema": { "type": "integer" @@ -2878,7 +2696,7 @@ ], "summary": "Update the permissions of a user", "description": "Update permissions", - "operationId": "51b3555fb753f44324bf5c3880e01454", + "operationId": "updatePermissions", "requestBody": { "required": true, "content": { @@ -3131,6 +2949,9 @@ { "$ref": "#/components/parameters/include" }, + { + "$ref": "#/components/parameters/pmql" + }, { "name": "simplified_data_for_selector", "in": "query", @@ -3228,6 +3049,22 @@ } } } + }, + "204": { + "description": "Process not found", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "example": "The requested process was not found" + } + }, + "type": "object" + } + } + } } } }, @@ -3640,7 +3477,7 @@ ], "summary": "Check if the import is ready", "description": "Check if the import is ready", - "operationId": "6a131993b7c879ddcd3d3a291dd8380f", + "operationId": "importReady", "parameters": [ { "name": "code", @@ -5130,6 +4967,9 @@ "items": { "type": "object" } + }, + "sync": { + "type": "boolean" } }, "type": "object" @@ -6922,225 +6762,2307 @@ } } } - } - }, - "components": { - "schemas": { - "DateTime": { - "properties": { - "date": { - "type": "string" - } - }, - "type": "object" - }, - "collectionsEditable": { + }, + "/processes/variables": { + "get": { + "tags": [ + "Processes Variables" + ], + "summary": "Get variables for multiple processes with pagination", + "operationId": "getProcessesVariables", + "parameters": [ + { + "name": "processIds", + "in": "query", + "description": "Comma-separated list of process IDs", + "required": false, + "schema": { + "type": "string", + "example": "1,2,3", + "nullable": true + } + }, + { + "name": "page", + "in": "query", + "description": "Page number", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Items per page", + "required": false, + "schema": { + "type": "integer", + "default": 20 + } + } + ], + "responses": { + "200": { + "description": "Successful response", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Variable" + } + }, + "meta": { + "$ref": "#/components/schemas/PaginationMeta" + } + }, + "type": "object" + } + } + } + } + }, + "servers": [ + { + "url": "https://pm4.local:8092/api/1.1", + "description": "API v1.1 Server" + } + ] + } + }, + "/analytics-reporting": { + "get": { + "tags": [ + "AnalyticsReporting" + ], + "summary": "Returns all analytics reporting that the user has access to", + "description": "Get a list of Analytics Reporting.", + "operationId": "getAnalyticsReporting", + "parameters": [ + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" + }, + { + "$ref": "#/components/parameters/include" + } + ], + "responses": { + "200": { + "description": "list of analytics reporting", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/analyticsReporting" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "AnalyticsReporting" + ], + "summary": "Save a new Analytics Reporting", + "description": "Create a new Analytics Reporting.", + "operationId": "createAnalyticsReporting", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/analyticsReportingEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/analyticsReporting" + } + } + } + } + } + } + }, + "/analytics-reporting/{analytic_reporting_id}": { + "get": { + "tags": [ + "AnalyticsReporting" + ], + "summary": "Get single analytic reporting by ID", + "description": "Get a single Analytic Reporting.", + "operationId": "getAnalyticReportingById", + "parameters": [ + { + "name": "analytic_reporting_id", + "in": "path", + "description": "ID of analytic reporting to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully found the analytics reporting", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/analyticsReporting" + } + } + } + } + } + }, + "put": { + "tags": [ + "AnalyticsReporting" + ], + "summary": "Update a analytic reporting", + "description": "Update a Analytics Reporting.", + "operationId": "updateAnalyticReporting", + "parameters": [ + { + "name": "analytic_reporting_id", + "in": "path", + "description": "ID of analytic reporting to update", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/analyticsReportingEditable" + } + } + } + }, + "responses": { + "204": { + "description": "success" + } + } + }, + "delete": { + "tags": [ + "AnalyticsReporting" + ], + "summary": "Delete an analytic reporting", + "description": "Delete a Analytics Reporting.", + "operationId": "deleteAnalyticReporting", + "parameters": [ + { + "name": "analytic_reporting_id", + "in": "path", + "description": "ID of analytic reporting to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success" + } + } + } + }, + "/collections": { + "get": { + "tags": [ + "Collections" + ], + "summary": "Returns all collections that the user has access to", + "description": "Get a list of Collections.", + "operationId": "getCollections", + "parameters": [ + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" + }, + { + "$ref": "#/components/parameters/include" + } + ], + "responses": { + "200": { + "description": "list of collections", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/collections" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "Collections" + ], + "summary": "Save a new collections", + "description": "Create a new Collection.", + "operationId": "createCollection", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collectionsEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collections" + } + } + } + } + } + } + }, + "/collections/{collection_id}": { + "get": { + "tags": [ + "Collections" + ], + "summary": "Get single collections by ID", + "description": "Get a single Collection.", + "operationId": "getCollectionById", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully found the collections", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collections" + } + } + } + } + } + }, + "put": { + "tags": [ + "Collections" + ], + "summary": "Update a collection", + "description": "Update a Collection.", + "operationId": "updateCollection", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection to update", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collectionsEditable" + } + } + } + }, + "responses": { + "204": { + "description": "success" + } + } + }, + "delete": { + "tags": [ + "Collections" + ], + "summary": "Delete a collection", + "description": "Delete a Collection.", + "operationId": "deleteCollection", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success" + } + } + } + }, + "/collections/{collection_id}/export": { + "post": { + "tags": [ + "Screens" + ], + "summary": "Trigger export collections job", + "description": "Export the specified collection.", + "operationId": "exportCollection", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of the collection to export", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "202": { + "description": "success" + } + } + } + }, + "/collections/import": { + "post": { + "tags": [ + "Collections" + ], + "summary": "Import a new collection", + "description": "Import the specified collection.", + "operationId": "importCollection", + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "required": [ + "file" + ], + "properties": { + "file": { + "description": "file to upload", + "type": "file", + "format": "file" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collections" + } + } + } + }, + "200": { + "description": "success" + } + } + } + }, + "/collections/{collection_id}/truncate": { + "delete": { + "tags": [ + "Collections" + ], + "summary": "Deletes all records in a collection", + "description": "Truncate a Collection.", + "operationId": "truncateCollection", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection to truncate", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success" + } + } + } + }, + "/collections/{collection_id}/records": { + "get": { + "tags": [ + "Collections" + ], + "summary": "Returns all records", + "description": "Get the list of records of a collection.", + "operationId": "getRecords", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection to get records for", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "pmql", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/per_page" + }, + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/include" + } + ], + "responses": { + "200": { + "description": "list of records of a collection", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/records" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "Collections" + ], + "summary": "Save a new record in a collection", + "description": "Create a new record in a Collection.", + "operationId": "createRecord", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of the collection", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/recordsEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/records" + } + } + } + } + } + } + }, + "/collections/{collection_id}/records/{record_id}": { + "get": { + "tags": [ + "Collections" + ], + "summary": "Get single record of a collection", + "description": "Get a single record of a Collection.", + "operationId": "getRecordById", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of the collection", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "record_id", + "in": "path", + "description": "ID of the record to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully found the record", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/records" + } + } + } + } + } + }, + "put": { + "tags": [ + "Collections" + ], + "summary": "Update a record", + "description": "Update a record in a Collection.", + "operationId": "updateRecord", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "record_id", + "in": "path", + "description": "ID of the record ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/recordsEditable" + } + } + } + }, + "responses": { + "204": { + "description": "success" + } + } + }, + "delete": { + "tags": [ + "Collections" + ], + "summary": "Delete a collection record", + "description": "Delete a record of a Collection.", + "operationId": "deleteRecord", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "record_id", + "in": "path", + "description": "ID of record in collection", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success" + } + } + }, + "patch": { + "tags": [ + "Collections" + ], + "summary": "Partial update of a record", + "description": "Implements a partial update of a record in a Collection.", + "operationId": "patchRecord", + "parameters": [ + { + "name": "collection_id", + "in": "path", + "description": "ID of collection ", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "record_id", + "in": "path", + "description": "ID of the record ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collectionsEditable" + } + } + } + }, + "responses": { + "200": { + "description": "success" + } + } + } + }, + "/comments/tasks": { + "get": { + "tags": [ + "Comments" + ], + "summary": "Returns all the tasks that are active.", + "description": "Display a listing of the resource.", + "operationId": "getCommentTasks", + "parameters": [ + { + "name": "process_request_id", + "in": "query", + "description": "Process request id", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + } + ], + "responses": { + "200": { + "description": "list all tasks taht are active", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/processRequestToken" + } + }, + "meta": { + "$ref": "#/components/schemas/metadata" + } + }, + "type": "object" + } + } + } + } + } + } + }, + "/data_source_categories": { + "get": { + "tags": [ + "DataSourcesCategories" + ], + "summary": "Returns all Data Connectors categories that the user has access to", + "description": "Display a listing of the Data Connector Categories.", + "operationId": "getDataSourceCategories", + "parameters": [ + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" + } + ], + "responses": { + "200": { + "description": "list of Data Connectors categories", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataSourceCategory" + } + }, + "meta": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/metadata" + } + ] + } + }, + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "DataSourcesCategories" + ], + "summary": "Save a new Data Connector Category", + "description": "Store a newly created Data Connector Category in storage", + "operationId": "createDataSourceCategory", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSourceCategoryEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataSourceCategory" + } + } + } + } + } + } + }, + "/data_source_categories/{data_source_category_id}": { + "get": { + "tags": [ + "DataSourcesCategories" + ], + "summary": "Get single Data Connector category by ID", + "description": "Display the specified data Source category.", + "operationId": "getDatasourceCategoryById", + "parameters": [ + { + "name": "data_source_category_id", + "in": "path", + "description": "ID of Data Connector category to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully found the Data Connector", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataSourceCategory" + } + } + } + } + } + }, + "put": { + "tags": [ + "DataSourcesCategories" + ], + "summary": "Update a Data Connector Category", + "description": "Updates the current element", + "operationId": "updateDatasourceCategory", + "parameters": [ + { + "name": "data_source_category_id", + "in": "path", + "description": "ID of Data Connector category to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSourceCategoryEditable" + } + } + } + }, + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataSourceCategory" + } + } + } + } + } + }, + "delete": { + "tags": [ + "DataSourcesCategories" + ], + "summary": "Delete a Data Connector category", + "description": "Remove the specified resource from storage.", + "operationId": "deleteDataSourceCategory", + "parameters": [ + { + "name": "data_source_category_id", + "in": "path", + "description": "ID of Data Connector category to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success" + } + } + } + }, + "/data_sources": { + "get": { + "tags": [ + "DataSources" + ], + "summary": "Returns all Data Connectors that the user has access to", + "description": "Get the list of records of a Data Connector", + "operationId": "getDataSources", + "parameters": [ + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" + }, + { + "$ref": "#/components/parameters/include" + } + ], + "responses": { + "200": { + "description": "list of Data Connectors", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataSource" + } + }, + "meta": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/metadata" + } + ] + } + }, + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "DataSources" + ], + "summary": "Save a new Data Connector", + "description": "Create a new Data Connector.", + "operationId": "createDataSource", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSourceEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSource" + } + } + } + } + } + } + }, + "/data_sources/{data_source_id}": { + "get": { + "tags": [ + "DataSources" + ], + "summary": "Get single Data Connector by ID", + "description": "Get a single Data Connector.", + "operationId": "getDataSourceById", + "parameters": [ + { + "name": "data_source_id", + "in": "path", + "description": "ID of Data Connector to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully found the Data Connector", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSource" + } + } + } + } + } + }, + "put": { + "tags": [ + "DataSources" + ], + "summary": "Update a Data Connector", + "description": "Update a Data Connector.", + "operationId": "updateDataSource", + "parameters": [ + { + "name": "data_source_id", + "in": "path", + "description": "ID of Data Connector to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSourceEditable" + } + } + } + }, + "responses": { + "204": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSource" + } + } + } + } + } + }, + "delete": { + "tags": [ + "DataSources" + ], + "summary": "Delete a Data Connector", + "description": "Delete a Data Connector.", + "operationId": "deleteDataSource", + "parameters": [ + { + "name": "data_source_id", + "in": "path", + "description": "ID of Data Connector to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSource" + } + } + } + } + } + } + }, + "/data_sources/{data_source_id}/test": { + "post": { + "tags": [ + "DataSources" + ], + "summary": "Send a Data Connector request", + "description": "Send a Data Connector request.", + "operationId": "sendDataSource", + "parameters": [ + { + "name": "data_source_id", + "in": "path", + "description": "ID of Data Connector to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSourceEditable" + } + } + } + }, + "responses": { + "204": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/dataSource" + } + } + } + } + } + } + }, + "/requests/{request_id}/data_sources/{data_source_id}": { + "post": { + "tags": [ + "DataSources" + ], + "summary": "execute Data Source", + "description": "Execute a data Source endpoint", + "operationId": "executeDataSourceForRequest", + "parameters": [ + { + "name": "request_id", + "in": "path", + "description": "ID of the request in whose context the datasource will be executed", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "data_source_id", + "in": "path", + "description": "ID of DataSource to be run", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "config": { + "$ref": "#/components/schemas/DataSourceCallParameters" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataSourceResponse" + } + } + } + } + } + } + }, + "/requests/data_sources/{data_source_id}": { + "post": { + "tags": [ + "DataSources" + ], + "summary": "execute Data Source", + "operationId": "executeDataSource", + "parameters": [ + { + "name": "data_source_id", + "in": "path", + "description": "ID of DataSource to be run", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "properties": { + "config": { + "$ref": "#/components/schemas/DataSourceCallParameters" + }, + "data": { + "type": "object" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataSourceResponse" + } + } + } + } + } + } + }, + "/requests/data_sources/{data_source_id}/resources/{endpoint}/data": { + "post": { + "tags": [ + "DataSources" + ], + "summary": "Get Data from Data Source", + "operationId": "getDataFromDataSource", + "parameters": [ + { + "name": "data_source_id", + "in": "path", + "description": "ID of DataSource to be run", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "endpoint", + "in": "path", + "description": "Endpoint of the data source", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataSourceResponse" + } + } + } + } + } + } + }, + "/version_histories": { + "get": { + "tags": [ + "Version History" + ], + "summary": "Return all version History according to the model", + "description": "Get the list of records of Version History", + "operationId": "getVersionHistories", + "parameters": [ + { + "$ref": "#/components/parameters/filter" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/order_direction" + }, + { + "$ref": "#/components/parameters/per_page" + }, + { + "$ref": "#/components/parameters/include" + } + ], + "responses": { + "200": { + "description": "list of Version History", + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/versionHistory" + } + }, + "meta": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/metadata" + } + ] + } + }, + "type": "object" + } + } + } + } + } + }, + "post": { + "tags": [ + "Version History" + ], + "summary": "Save a new Version History", + "description": "Create a new Version History.", + "operationId": "createVersion", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistoryEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistory" + } + } + } + } + } + } + }, + "/version_histories/{version_history_id}": { + "get": { + "tags": [ + "Version History" + ], + "summary": "Get single Version History by ID", + "description": "Get a single Version History.", + "operationId": "getVersionHistoryById", + "parameters": [ + { + "name": "version_history_id", + "in": "path", + "description": "ID of Version History to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Successfully found the Version History", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistory" + } + } + } + } + } + }, + "put": { + "tags": [ + "Version History" + ], + "summary": "Update a Version History", + "description": "Update a Version History.", + "operationId": "updateVersion", + "parameters": [ + { + "name": "version_history_id", + "in": "path", + "description": "ID of Version History to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistoryEditable" + } + } + } + }, + "responses": { + "204": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistory" + } + } + } + } + } + }, + "delete": { + "tags": [ + "Version History" + ], + "summary": "Delete a Version History", + "description": "Delete a Version History.", + "operationId": "deleteVersion", + "parameters": [ + { + "name": "version_history_id", + "in": "path", + "description": "ID of Version History to return", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistory" + } + } + } + } + } + } + }, + "/version_histories/clone": { + "post": { + "tags": [ + "Version History" + ], + "summary": "Clone a new Version History", + "description": "Clone a new Version History.", + "operationId": "cloneVersion", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistoryEditable" + } + } + } + }, + "responses": { + "201": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/versionHistory" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "DateTime": { + "properties": { + "date": { + "type": "string" + } + }, + "type": "object" + }, + "decisionTableEditable": { + "properties": { + "id": { + "description": "Class Screen", + "type": "string", + "format": "id" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "definition": { + "type": "string" + }, + "decision_table_categories_id": { + "type": "string" + } + }, + "type": "object" + }, + "decisionTable": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/decisionTableEditable" + }, + { + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + } + ] + }, + "DecisionTableExported": { + "properties": { + "url": { + "type": "string" + } + }, + "type": "object" + }, + "decisionTableCategoryEditable": { + "properties": { + "name": { + "description": "Represents a business decision Table category definition.", + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "INACTIVE" + ] + } + }, + "type": "object" + }, + "DecisionTableCategory": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/decisionTableCategoryEditable" + }, + { + "properties": { + "id": { + "type": "string", + "format": "id" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + } + ] + }, + "SavedSearchEditable": { + "properties": { + "meta": { + "description": "Represents an Eloquent model of a Saved Search.", + "type": "object", + "additionalProperties": "true" + }, + "pmql": { + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "task", + "request" + ] + }, + "advanced_filter": { + "type": "object", + "additionalProperties": "true" + } + }, + "type": "object" + }, + "SavedSearch": { + "allOf": [ + { + "properties": { + "id": { + "type": "string", + "format": "id" + }, + "user_id": { + "type": "string", + "format": "id" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + }, + { + "$ref": "#/components/schemas/SavedSearchEditable" + } + ] + }, + "SavedSearchIcon": { + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "SavedSearchChartEditable": { + "properties": { + "title": { + "description": "Represents an Eloquent model of a Saved Search Chart.", + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "bar", + "bar-vertical", + "line", + "pie", + "doughnut" + ] + }, + "config": { + "type": "object", + "additionalProperties": "true" + }, + "sort": { + "type": "integer" + } + }, + "type": "object" + }, + "SavedSearchChart": { + "allOf": [ + { + "properties": { + "id": { + "type": "string", + "format": "id" + }, + "saved_search_id": { + "type": "string", + "format": "id" + }, + "user_id": { + "type": "string", + "format": "id" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + }, + { + "$ref": "#/components/schemas/SavedSearchChartEditable" + } + ] + }, + "ReportEditable": { + "properties": { + "type": { + "type": "string", + "enum": [ + "adhoc", + "scheduled" + ] + }, + "format": { + "type": "string", + "enum": [ + "csv", + "xlsx" + ] + }, + "saved_search_id": { + "type": "integer" + }, + "config": { + "type": "object", + "additionalProperties": "true" + }, + "to": { + "type": "array", + "items": { + "type": "string" + } + }, + "subject": { + "type": "string" + }, + "body": { + "type": "string" + } + }, + "type": "object" + }, + "Report": { + "allOf": [ + { + "properties": { + "id": { + "type": "string", + "format": "id" + }, + "user_id": { + "type": "string", + "format": "id" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + }, + { + "$ref": "#/components/schemas/SavedSearchEditable" + } + ] + }, + "updateUserGroups": { "properties": { - "name": { + "groups": { + "type": "array", + "items": { + "type": "integer", + "example": 1 + } + } + }, + "type": "object" + }, + "restoreUser": { + "properties": { + "username": { + "description": "Username to restore", "type": "string" + } + }, + "type": "object" + }, + "Variable": { + "properties": { + "id": { + "type": "integer", + "example": 1 }, - "description": { - "type": "string" + "process_id": { + "type": "integer", + "example": 1 }, - "custom_title": { - "type": "string" + "uuid": { + "type": "string", + "format": "uuid", + "example": "550e8400-e29b-41d4-a716-446655440000" }, - "create_screen_id": { + "field": { "type": "string", - "format": "id" + "enum": [ + "string", + "number", + "boolean", + "array" + ], + "example": "string" }, - "read_screen_id": { + "label": { "type": "string", - "format": "id" + "example": "Variable 1 for Process 1" }, - "update_screen_id": { + "name": { "type": "string", - "format": "id" + "example": "var_1_1" }, - "signal_create": { - "type": "boolean" + "asset": { + "properties": { + "id": { + "type": "string", + "example": "asset_1_1" + }, + "type": { + "type": "string", + "enum": [ + "sensor", + "actuator", + "controller", + "device" + ], + "example": "sensor" + }, + "name": { + "type": "string", + "example": "Asset 1 for Process 1" + }, + "uuid": { + "type": "string", + "format": "uuid", + "example": "550e8400-e29b-41d4-a716-446655440000" + } + }, + "type": "object" }, - "signal_update": { - "type": "boolean" + "created_at": { + "type": "string", + "format": "date-time" }, - "signal_delete": { - "type": "boolean" + "updated_at": { + "type": "string", + "format": "date-time" } }, "type": "object" }, - "collections": { - "allOf": [ - { - "$ref": "#/components/schemas/collectionsEditable" + "PaginationMeta": { + "properties": { + "current_page": { + "type": "integer", + "example": 1 }, - { + "from": { + "type": "integer", + "example": 1 + }, + "last_page": { + "type": "integer", + "example": 5 + }, + "path": { + "type": "string", + "example": "http://processmaker.com/processes/variables" + }, + "per_page": { + "type": "integer", + "example": 20 + }, + "to": { + "type": "integer", + "example": 20 + }, + "total": { + "type": "integer", + "example": 100 + }, + "links": { "properties": { - "id": { - "type": "integer" - }, - "created_at": { + "first": { "type": "string", - "format": "date-time" + "example": "http://processmaker.com/processes/variables?page=1" }, - "updated_at": { + "last": { "type": "string", - "format": "date-time" + "example": "http://processmaker.com/processes/variables?page=5" }, - "created_by_id": { + "prev": { "type": "string", - "format": "id" + "nullable": true }, - "updated_by_id": { + "next": { "type": "string", - "format": "id" - }, - "columns": { - "type": "array", - "items": { - "type": "object" - } + "example": "http://processmaker.com/processes/variables?page=2" } }, "type": "object" } - ] + }, + "type": "object" }, - "recordsEditable": { + "metadata": { "properties": { - "data": { - "type": "object" + "filter": { + "type": "string" + }, + "sort_by": { + "type": "string" + }, + "sort_order": { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + "count": { + "type": "integer" + }, + "total_pages": { + "type": "integer" + }, + "current_page": { + "type": "integer" + }, + "form": { + "type": "integer" + }, + "last_page": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "per_page": { + "type": "integer" + }, + "to": { + "type": "integer" + }, + "total": { + "type": "integer" } }, "type": "object" }, - "records": { + "taskMetadata": { + "type": "object", "allOf": [ { - "$ref": "#/components/schemas/recordsEditable" + "$ref": "#/components/schemas/metadata" }, { "properties": { - "id": { - "type": "integer" + "filter": { + "type": "string" }, - "collection_id": { + "sort_by": { + "type": "string" + }, + "sort_order": { "type": "string", - "format": "id" + "enum": [ + "asc", + "desc" + ] + }, + "count": { + "type": "integer" + }, + "total_pages": { + "type": "integer" + }, + "current_page": { + "type": "integer" + }, + "form": { + "type": "integer" + }, + "last_page": { + "type": "integer" + }, + "path": { + "type": "string" + }, + "per_page": { + "type": "integer" + }, + "to": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "in_overdue": { + "type": "integer" } }, "type": "object" } ] }, - "SavedSearchEditable": { + "signalsEditable": { "properties": { - "meta": { - "description": "Represents an Eloquent model of a Saved Search.", - "type": "object", - "additionalProperties": "true" + "id": { + "description": "Represents a business signal definition.", + "type": "string", + "format": "id" }, - "pmql": { + "name": { "type": "string" }, - "title": { + "detail": { "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "task", - "request" - ] - }, - "advanced_filter": { - "type": "object", - "additionalProperties": "true" } }, "type": "object" }, - "SavedSearch": { + "signals": { "allOf": [ + { + "$ref": "#/components/schemas/signalsEditable" + }, { "properties": { - "id": { - "type": "string", - "format": "id" - }, - "user_id": { - "type": "string", - "format": "id" - }, - "created_at": { - "type": "string", - "format": "date-time" + "type": { + "type": "string" }, - "updated_at": { - "type": "string", - "format": "date-time" + "processes": { + "type": "array", + "items": { + "properties": { + "id": { + "type": "integer", + "format": "id" + }, + "is_system": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "catches": { + "type": "array", + "items": { + "properties": { + "id": { + "type": "integer", + "format": "id" + }, + "name": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "type": "object" + } } }, "type": "object" - }, - { - "$ref": "#/components/schemas/SavedSearchEditable" } ] }, - "SavedSearchIcon": { + "columns": { "properties": { - "name": { + "label": { "type": "string" }, - "value": { + "field": { + "type": "string" + }, + "sortable": { + "type": "boolean" + }, + "default": { + "type": "boolean" + }, + "format": { + "type": "string" + }, + "mask": { "type": "string" } }, "type": "object" }, - "SavedSearchChartEditable": { + "commentsEditable": { "properties": { - "title": { - "description": "Represents an Eloquent model of a Saved Search Chart.", + "id": { + "description": "Represents a business process definition.", + "type": "string", + "format": "id" + }, + "user_id": { + "type": "string", + "format": "id" + }, + "commentable_id": { + "type": "string", + "format": "id" + }, + "commentable_type": { + "type": "string" + }, + "up": { + "type": "integer" + }, + "down": { + "type": "integer" + }, + "subject": { + "type": "string" + }, + "body": { "type": "string" }, + "hidden": { + "type": "boolean" + }, "type": { "type": "string", "enum": [ - "bar", - "bar-vertical", - "line", - "pie", - "doughnut" + "LOG", + "MESSAGE" ] - }, - "config": { - "type": "object", - "additionalProperties": "true" - }, - "sort": { - "type": "integer" } }, "type": "object" }, - "SavedSearchChart": { + "comments": { + "type": "object", "allOf": [ + { + "$ref": "#/components/schemas/commentsEditable" + }, { "properties": { - "id": { - "type": "string", - "format": "id" - }, - "saved_search_id": { - "type": "string", - "format": "id" - }, - "user_id": { - "type": "string", - "format": "id" - }, "created_at": { "type": "string", "format": "date-time" @@ -7148,67 +9070,35 @@ "updated_at": { "type": "string", "format": "date-time" - }, - "deleted_at": { - "type": "string", - "format": "date-time" } }, "type": "object" - }, - { - "$ref": "#/components/schemas/SavedSearchChartEditable" } ] }, - "ReportEditable": { + "EnvironmentVariableEditable": { "properties": { - "type": { - "type": "string", - "enum": [ - "adhoc", - "scheduled" - ] - }, - "format": { - "type": "string", - "enum": [ - "csv", - "xlsx" - ] - }, - "saved_search_id": { - "type": "integer" - }, - "config": { - "type": "object", - "additionalProperties": "true" - }, - "to": { - "type": "array", - "items": { - "type": "string" - } + "name": { + "type": "string" }, - "subject": { + "description": { "type": "string" }, - "body": { + "value": { "type": "string" } }, "type": "object" }, - "Report": { + "EnvironmentVariable": { "allOf": [ + { + "$ref": "#/components/schemas/EnvironmentVariableEditable" + }, { "properties": { "id": { - "type": "string", - "format": "id" - }, - "user_id": { - "type": "string", + "type": "integer", "format": "id" }, "created_at": { @@ -7221,30 +9111,22 @@ } }, "type": "object" - }, - { - "$ref": "#/components/schemas/SavedSearchEditable" } ] }, - "versionHistoryEditable": { + "groupsEditable": { "properties": { - "versionable_id": { - "description": "Class VersionHistoryCollection", - "type": "integer" - }, - "versionable_type": { - "type": "string" - }, "name": { - "type": "string" - }, - "subject": { + "description": "Represents a group definition.", "type": "string" }, "description": { "type": "string" }, + "manager_id": { + "type": "integer", + "format": "id" + }, "status": { "type": "string", "enum": [ @@ -7255,11 +9137,10 @@ }, "type": "object" }, - "versionHistory": { - "type": "object", + "groups": { "allOf": [ { - "$ref": "#/components/schemas/versionHistoryEditable" + "$ref": "#/components/schemas/groupsEditable" }, { "properties": { @@ -7270,272 +9151,209 @@ "updated_at": { "type": "string", "format": "date-time" + }, + "id": { + "type": "string", + "format": "id" } }, "type": "object" } ] }, - "updateUserGroups": { - "properties": { - "groups": { - "type": "array", - "items": { - "type": "integer", - "example": 1 - } - } - }, - "type": "object" - }, - "restoreUser": { - "properties": { - "username": { - "description": "Username to restore", - "type": "string" - } - }, - "type": "object" - }, - "metadata": { + "groupMembersEditable": { "properties": { - "filter": { - "type": "string" - }, - "sort_by": { - "type": "string" - }, - "sort_order": { + "group_id": { + "description": "Represents a group Members definition.", "type": "string", - "enum": [ - "asc", - "desc" - ] - }, - "count": { - "type": "integer" - }, - "total_pages": { - "type": "integer" - }, - "current_page": { - "type": "integer" - }, - "form": { - "type": "integer" - }, - "last_page": { - "type": "integer" - }, - "path": { - "type": "string" + "format": "id" }, - "per_page": { - "type": "integer" + "member_id": { + "type": "string", + "format": "id" }, - "to": { - "type": "integer" + "member_type": { + "type": "string" }, - "total": { - "type": "integer" + "description": { + "type": "string" } }, "type": "object" }, - "taskMetadata": { - "type": "object", + "groupMembers": { "allOf": [ { - "$ref": "#/components/schemas/metadata" + "$ref": "#/components/schemas/groupMembersEditable" }, { "properties": { - "filter": { - "type": "string" + "id": { + "type": "string", + "format": "id" }, - "sort_by": { - "type": "string" + "created_at": { + "type": "string", + "format": "date-time" }, - "sort_order": { + "updated_at": { "type": "string", - "enum": [ - "asc", - "desc" - ] + "format": "date-time" + } + }, + "type": "object" + } + ] + }, + "createGroupMembers": { + "allOf": [ + { + "$ref": "#/components/schemas/groupMembersEditable" + }, + { + "properties": { + "id": { + "type": "string", + "format": "id" }, - "count": { - "type": "integer" + "group": { + "type": "object" }, - "total_pages": { - "type": "integer" + "member": { + "type": "object" }, - "current_page": { - "type": "integer" + "created_at": { + "type": "string", + "format": "date-time" }, - "form": { - "type": "integer" + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + } + ] + }, + "getGroupMembersById": { + "allOf": [ + { + "properties": { + "group_id": { + "type": "string", + "format": "id" }, - "last_page": { - "type": "integer" + "member_id": { + "type": "string", + "format": "id" }, - "path": { + "member_type": { "type": "string" }, - "per_page": { - "type": "integer" - }, - "to": { - "type": "integer" + "id": { + "type": "string", + "format": "id" }, - "total": { - "type": "integer" + "created_at": { + "type": "string", + "format": "date-time" }, - "in_overdue": { - "type": "integer" + "updated_at": { + "type": "string", + "format": "date-time" } }, "type": "object" } ] }, - "signalsEditable": { - "properties": { - "id": { - "description": "Represents a business signal definition.", - "type": "string", - "format": "id" - }, - "name": { - "type": "string" - }, - "detail": { - "type": "string" - } - }, - "type": "object" - }, - "signals": { + "availableGroupMembers": { "allOf": [ - { - "$ref": "#/components/schemas/signalsEditable" - }, { "properties": { - "type": { + "id": { + "type": "string", + "format": "id" + }, + "description": { "type": "string" }, - "processes": { - "type": "array", - "items": { - "properties": { - "id": { - "type": "integer", - "format": "id" - }, - "is_system": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "catches": { - "type": "array", - "items": { - "properties": { - "id": { - "type": "integer", - "format": "id" - }, - "name": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - } - } - }, - "type": "object" - } + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "INACTIVE" + ] + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" } }, "type": "object" } ] }, - "columns": { - "properties": { - "label": { - "type": "string" - }, - "field": { - "type": "string" - }, - "sortable": { - "type": "boolean" - }, - "default": { - "type": "boolean" - }, - "format": { - "type": "string" - }, - "mask": { - "type": "string" - } - }, - "type": "object" - }, - "commentsEditable": { + "mediaEditable": { "properties": { "id": { - "description": "Represents a business process definition.", - "type": "string", + "description": "Represents media files stored in the database", + "type": "integer", "format": "id" }, - "user_id": { - "type": "string", + "model_id": { + "type": "integer", "format": "id" }, - "commentable_id": { + "model_type": { "type": "string", "format": "id" }, - "commentable_type": { + "collection_name": { "type": "string" }, - "up": { - "type": "integer" + "name": { + "type": "string" }, - "down": { - "type": "integer" + "file_name": { + "type": "string" }, - "subject": { + "mime_type": { "type": "string" }, - "body": { + "disk": { "type": "string" }, - "hidden": { - "type": "boolean" + "size": { + "type": "integer" }, - "type": { - "type": "string", - "enum": [ - "LOG", - "MESSAGE" - ] + "manipulations": { + "type": "object" + }, + "custom_properties": { + "type": "object" + }, + "responsive_images": { + "type": "object" + }, + "order_column": { + "type": "integer" } }, "type": "object" }, - "comments": { + "media": { "type": "object", "allOf": [ { - "$ref": "#/components/schemas/commentsEditable" + "$ref": "#/components/schemas/mediaEditable" }, { "properties": { @@ -7552,30 +9370,63 @@ } ] }, - "EnvironmentVariableEditable": { + "mediaExported": { + "properties": { + "url": { + "type": "string" + } + }, + "type": "object" + }, + "NotificationEditable": { "properties": { + "type": { + "description": "Represents a notification definition.", + "type": "string" + }, + "notifiable_type": { + "type": "string" + }, + "notifiable_id": { + "type": "integer" + }, + "data": { + "type": "string" + }, "name": { "type": "string" }, - "description": { + "message": { "type": "string" }, - "value": { + "processName": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "request_id": { + "type": "string" + }, + "url": { "type": "string" } }, "type": "object" }, - "EnvironmentVariable": { + "Notification": { "allOf": [ { - "$ref": "#/components/schemas/EnvironmentVariableEditable" + "$ref": "#/components/schemas/NotificationEditable" }, { "properties": { "id": { - "type": "integer", - "format": "id" + "type": "string" + }, + "read_at": { + "type": "string", + "format": "date-time" }, "created_at": { "type": "string", @@ -7590,36 +9441,96 @@ } ] }, - "groupsEditable": { + "ProcessEditable": { "properties": { + "process_category_id": { + "description": "Represents a business process definition.", + "type": "integer", + "format": "id" + }, "name": { - "description": "Represents a group definition.", "type": "string" }, - "description": { + "case_title": { "type": "string" }, - "manager_id": { - "type": "integer", - "format": "id" + "description": { + "type": "string" }, "status": { "type": "string", "enum": [ "ACTIVE", - "INACTIVE" + "INACTIVE", + "ARCHIVED" ] + }, + "pause_timer_start": { + "type": "integer" + }, + "cancel_screen_id": { + "type": "integer" + }, + "has_timer_start_events": { + "type": "boolean" + }, + "request_detail_screen_id": { + "type": "integer", + "format": "id" + }, + "is_valid": { + "type": "integer" + }, + "package_key": { + "type": "string" + }, + "start_events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProcessStartEvents" + } + }, + "warnings": { + "type": "string" + }, + "self_service_tasks": { + "type": "object" + }, + "signal_events": { + "type": "array", + "items": { + "type": "object" + } + }, + "category": { + "type": "object" + }, + "manager_id": { + "type": "integer", + "format": "id" } }, "type": "object" }, - "groups": { + "Process": { "allOf": [ { - "$ref": "#/components/schemas/groupsEditable" + "$ref": "#/components/schemas/ProcessEditable" }, { "properties": { + "user_id": { + "type": "integer", + "format": "id" + }, + "id": { + "type": "string", + "format": "id" + }, + "deleted_at": { + "type": "string", + "format": "date-time" + }, "created_at": { "type": "string", "format": "date-time" @@ -7628,104 +9539,123 @@ "type": "string", "format": "date-time" }, - "id": { - "type": "string", - "format": "id" + "notifications": { + "type": "object" + }, + "task_notifications": { + "type": "object" } }, "type": "object" } ] }, - "groupMembersEditable": { + "ProcessStartEvents": { "properties": { - "group_id": { - "description": "Represents a group Members definition.", - "type": "string", - "format": "id" + "eventDefinitions": { + "type": "object" }, - "member_id": { - "type": "string", - "format": "id" + "parallelMultiple": { + "type": "boolean" }, - "member_type": { + "outgoing": { + "type": "object" + }, + "incoming": { + "type": "object" + }, + "id": { "type": "string" }, - "description": { + "name": { "type": "string" } }, "type": "object" }, - "groupMembers": { + "ProcessWithStartEvents": { "allOf": [ { - "$ref": "#/components/schemas/groupMembersEditable" + "$ref": "#/components/schemas/Process" }, { "properties": { - "id": { - "type": "string", - "format": "id" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProcessStartEvents" + } } }, "type": "object" } ] }, - "createGroupMembers": { + "ProcessImport": { "allOf": [ { - "$ref": "#/components/schemas/groupMembersEditable" + "$ref": "#/components/schemas/ProcessEditable" }, { "properties": { - "id": { - "type": "string", - "format": "id" - }, - "group": { - "type": "object" - }, - "member": { - "type": "object" + "status": { + "type": "array", + "items": { + "type": "object" + } }, - "created_at": { - "type": "string", - "format": "date-time" + "assignable": { + "type": "array", + "items": { + "type": "object" + } }, - "updated_at": { - "type": "string", - "format": "date-time" - } + "process": {} }, "type": "object" } ] }, - "getGroupMembersById": { - "allOf": [ - { - "properties": { - "group_id": { - "type": "string", - "format": "id" - }, - "member_id": { - "type": "string", - "format": "id" - }, - "member_type": { - "type": "string" - }, + "ProcessAssignments": { + "properties": { + "assignable": { + "type": "array", + "items": { + "type": "object" + } + }, + "cancel_request": { + "type": "object" + }, + "edit_data": { + "type": "object" + } + }, + "type": "object" + }, + "ProcessCategoryEditable": { + "properties": { + "name": { + "description": "Represents a business process category definition.", + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "INACTIVE" + ] + } + }, + "type": "object" + }, + "ProcessCategory": { + "allOf": [ + { + "$ref": "#/components/schemas/ProcessCategoryEditable" + }, + { + "properties": { "id": { "type": "string", "format": "id" @@ -7743,27 +9673,39 @@ } ] }, - "availableGroupMembers": { + "processPermissionsEditable": { + "properties": { + "id": { + "description": "Represents a Process permission.", + "type": "integer", + "format": "id" + }, + "process_id": { + "type": "integer", + "format": "id" + }, + "permission_id": { + "type": "integer", + "format": "id" + }, + "assignable_id": { + "type": "integer", + "format": "id" + }, + "assignable_type": { + "type": "string" + } + }, + "type": "object" + }, + "processPermissions": { + "type": "object", "allOf": [ + { + "$ref": "#/components/schemas/processPermissionsEditable" + }, { "properties": { - "id": { - "type": "string", - "format": "id" - }, - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "INACTIVE" - ] - }, "created_at": { "type": "string", "format": "date-time" @@ -7777,62 +9719,80 @@ } ] }, - "mediaEditable": { + "processRequestEditable": { "properties": { - "id": { - "description": "Represents media files stored in the database", - "type": "integer", - "format": "id" - }, - "model_id": { - "type": "integer", + "user_id": { + "description": "Represents an Eloquent model of a Request which is an instance of a Process.", + "type": "string", "format": "id" }, - "model_type": { + "callable_id": { "type": "string", "format": "id" }, - "collection_name": { - "type": "string" + "data": { + "type": "object" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "COMPLETED", + "ERROR", + "CANCELED" + ] }, "name": { "type": "string" }, - "file_name": { + "case_title": { "type": "string" }, - "mime_type": { + "case_title_formatted": { "type": "string" }, - "disk": { + "user_viewed_at": { "type": "string" }, - "size": { + "case_number": { "type": "integer" }, - "manipulations": { - "type": "object" - }, - "custom_properties": { - "type": "object" + "process_id": { + "type": "integer" }, - "responsive_images": { + "process": { "type": "object" - }, - "order_column": { - "type": "integer" } }, "type": "object" }, - "media": { - "type": "object", + "processRequest": { "allOf": [ { - "$ref": "#/components/schemas/mediaEditable" + "$ref": "#/components/schemas/processRequestEditable" }, { "properties": { + "id": { + "type": "string", + "format": "id" + }, + "process_id": { + "type": "string", + "format": "id" + }, + "process_collaboration_id": { + "type": "string", + "format": "id" + }, + "participant_id": { + "type": "string", + "format": "id" + }, + "process_category_id": { + "type": "string", + "format": "id" + }, "created_at": { "type": "string", "format": "date-time" @@ -7840,69 +9800,151 @@ "updated_at": { "type": "string", "format": "date-time" + }, + "user": {}, + "participants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/users" + } } }, "type": "object" } ] }, - "mediaExported": { - "properties": { - "url": { - "type": "string" - } - }, - "type": "object" - }, - "NotificationEditable": { + "processRequestTokenEditable": { "properties": { - "type": { - "description": "Represents a notification definition.", - "type": "string" + "user_id": { + "description": "ProcessRequestToken is used to store the state of a token of the\nNayra engine", + "type": "string", + "format": "id" }, - "notifiable_type": { + "status": { "type": "string" }, - "notifiable_id": { - "type": "integer" + "due_at": { + "type": "string", + "format": "date-time" }, - "data": { - "type": "string" + "initiated_at": { + "type": "string", + "format": "date-time" }, - "name": { - "type": "string" + "riskchanges_at": { + "type": "string", + "format": "date-time" }, - "message": { + "subprocess_start_event_id": { "type": "string" }, - "processName": { - "type": "string" + "data": { + "type": "object" + } + }, + "type": "object" + }, + "processRequestToken": { + "allOf": [ + { + "$ref": "#/components/schemas/processRequestTokenEditable" }, - "userName": { - "type": "string" + { + "properties": { + "id": { + "type": "string", + "format": "id" + }, + "process_id": { + "type": "string", + "format": "id" + }, + "process_request_id": { + "type": "string", + "format": "id" + }, + "element_id": { + "type": "string", + "format": "id" + }, + "element_type": { + "type": "string", + "format": "id" + }, + "element_index": { + "type": "string" + }, + "element_name": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "initiated_at": { + "type": "string", + "format": "date-time" + }, + "advanceStatus": { + "type": "string" + }, + "due_notified": { + "type": "integer" + }, + "user": { + "type": "object" + }, + "process": { + "type": "object" + }, + "process_request": { + "type": "object" + } + }, + "type": "object" + } + ] + }, + "taskAssignmentsEditable": { + "properties": { + "process_id": { + "description": "Represents a business process task assignment definition.", + "type": "integer", + "format": "id" }, - "request_id": { - "type": "string" + "process_task_id": { + "type": "string", + "format": "id" + }, + "assignment_id": { + "type": "integer", + "format": "id" }, - "url": { - "type": "string" + "assignment_type": { + "type": "string", + "enum": [ + "ProcessMaker\\Models\\User", + "ProcessMaker\\Models\\Group" + ] } }, "type": "object" }, - "Notification": { + "taskAssignments": { + "type": "object", "allOf": [ { - "$ref": "#/components/schemas/NotificationEditable" + "$ref": "#/components/schemas/taskAssignmentsEditable" }, { "properties": { "id": { - "type": "string" - }, - "read_at": { - "type": "string", - "format": "date-time" + "type": "integer", + "format": "id" }, "created_at": { "type": "string", @@ -7917,96 +9959,56 @@ } ] }, - "ProcessEditable": { + "screensEditable": { "properties": { - "process_category_id": { - "description": "Represents a business process definition.", - "type": "integer", - "format": "id" - }, - "name": { + "title": { + "description": "Class Screen", "type": "string" }, - "case_title": { + "type": { "type": "string" }, "description": { "type": "string" }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "INACTIVE", - "ARCHIVED" - ] - }, - "pause_timer_start": { - "type": "integer" - }, - "cancel_screen_id": { - "type": "integer" - }, - "has_timer_start_events": { - "type": "boolean" - }, - "request_detail_screen_id": { - "type": "integer", - "format": "id" - }, - "is_valid": { - "type": "integer" - }, - "package_key": { - "type": "string" - }, - "start_events": { + "config": { "type": "array", "items": { - "$ref": "#/components/schemas/ProcessStartEvents" + "type": "object" } }, - "warnings": { - "type": "string" - }, - "self_service_tasks": { - "type": "object" + "computed": { + "type": "array", + "items": { + "type": "object" + } }, - "signal_events": { + "watchers": { "type": "array", "items": { "type": "object" } }, - "category": { - "type": "object" + "custom_css": { + "type": "string" }, - "manager_id": { - "type": "integer", - "format": "id" + "screen_category_id": { + "type": "string" } }, "type": "object" }, - "Process": { + "screens": { "allOf": [ { - "$ref": "#/components/schemas/ProcessEditable" + "$ref": "#/components/schemas/screensEditable" }, { "properties": { - "user_id": { - "type": "integer", - "format": "id" - }, "id": { "type": "string", "format": "id" }, - "deleted_at": { - "type": "string", - "format": "date-time" - }, "created_at": { "type": "string", "format": "date-time" @@ -8014,105 +10016,24 @@ "updated_at": { "type": "string", "format": "date-time" - }, - "notifications": { - "type": "object" - }, - "task_notifications": { - "type": "object" } }, "type": "object" } ] }, - "ProcessStartEvents": { + "screenExported": { "properties": { - "eventDefinitions": { - "type": "object" - }, - "parallelMultiple": { - "type": "boolean" - }, - "outgoing": { - "type": "object" - }, - "incoming": { - "type": "object" - }, - "id": { - "type": "string" - }, - "name": { + "url": { "type": "string" } }, "type": "object" }, - "ProcessWithStartEvents": { - "allOf": [ - { - "$ref": "#/components/schemas/Process" - }, - { - "properties": { - "events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProcessStartEvents" - } - } - }, - "type": "object" - } - ] - }, - "ProcessImport": { - "allOf": [ - { - "$ref": "#/components/schemas/ProcessEditable" - }, - { - "properties": { - "status": { - "type": "array", - "items": { - "type": "object" - } - }, - "assignable": { - "type": "array", - "items": { - "type": "object" - } - }, - "process": {} - }, - "type": "object" - } - ] - }, - "ProcessAssignments": { - "properties": { - "assignable": { - "type": "array", - "items": { - "type": "object" - } - }, - "cancel_request": { - "type": "object" - }, - "edit_data": { - "type": "object" - } - }, - "type": "object" - }, - "ProcessCategoryEditable": { + "ScreenCategoryEditable": { "properties": { "name": { - "description": "Represents a business process category definition.", + "description": "Represents a business screen category definition.", "type": "string" }, "status": { @@ -8125,10 +10046,10 @@ }, "type": "object" }, - "ProcessCategory": { + "ScreenCategory": { "allOf": [ { - "$ref": "#/components/schemas/ProcessCategoryEditable" + "$ref": "#/components/schemas/ScreenCategoryEditable" }, { "properties": { @@ -8149,124 +10070,70 @@ } ] }, - "processPermissionsEditable": { + "ScreenTypeEditable": { "properties": { - "id": { - "description": "Represents a Process permission.", - "type": "integer", - "format": "id" - }, - "process_id": { - "type": "integer", - "format": "id" - }, - "permission_id": { - "type": "integer", - "format": "id" - }, - "assignable_id": { - "type": "integer", - "format": "id" - }, - "assignable_type": { + "name": { + "description": "Represents a business screen Type definition.", "type": "string" } }, "type": "object" }, - "processPermissions": { - "type": "object", + "ScreenType": { "allOf": [ { - "$ref": "#/components/schemas/processPermissionsEditable" + "$ref": "#/components/schemas/ScreenTypeEditable" }, { "properties": { - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - }, - "type": "object" - } - ] - }, - "processRequestEditable": { - "properties": { - "user_id": { - "description": "Represents an Eloquent model of a Request which is an instance of a Process.", - "type": "string", - "format": "id" - }, - "callable_id": { - "type": "string", - "format": "id" - }, - "data": { - "type": "object" - }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "COMPLETED", - "ERROR", - "CANCELED" - ] - }, - "name": { + "id": { + "type": "string", + "format": "id" + } + }, + "type": "object" + } + ] + }, + "scriptsEditable": { + "properties": { + "title": { + "description": "Represents an Eloquent model of a Script", "type": "string" }, - "case_title": { + "description": { "type": "string" }, - "case_title_formatted": { + "language": { "type": "string" }, - "user_viewed_at": { + "code": { "type": "string" }, - "case_number": { + "timeout": { "type": "integer" }, - "process_id": { + "run_as_user_id": { "type": "integer" }, - "process": { - "type": "object" + "key": { + "type": "string" + }, + "script_category_id": { + "type": "integer" } }, "type": "object" }, - "processRequest": { + "scripts": { "allOf": [ { - "$ref": "#/components/schemas/processRequestEditable" + "$ref": "#/components/schemas/scriptsEditable" }, { "properties": { "id": { - "type": "string", - "format": "id" - }, - "process_id": { - "type": "string", - "format": "id" - }, - "process_collaboration_id": { - "type": "string", - "format": "id" - }, - "participant_id": { - "type": "string", - "format": "id" - }, - "process_category_id": { - "type": "string", + "type": "integer", "format": "id" }, "created_at": { @@ -8276,54 +10143,46 @@ "updated_at": { "type": "string", "format": "date-time" - }, - "user": {}, - "participants": { - "type": "array", - "items": { - "$ref": "#/components/schemas/users" - } } }, "type": "object" } ] }, - "processRequestTokenEditable": { + "scriptsPreview": { "properties": { - "user_id": { - "description": "ProcessRequestToken is used to store the state of a token of the\nNayra engine", - "type": "string", - "format": "id" - }, "status": { "type": "string" }, - "due_at": { - "type": "string", - "format": "date-time" - }, - "initiated_at": { - "type": "string", - "format": "date-time" - }, - "riskchanges_at": { - "type": "string", - "format": "date-time" - }, - "subprocess_start_event_id": { + "key": { "type": "string" }, - "data": { + "output": { "type": "object" } }, "type": "object" }, - "processRequestToken": { + "ScriptCategoryEditable": { + "properties": { + "name": { + "description": "Represents a business script category definition.", + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "INACTIVE" + ] + } + }, + "type": "object" + }, + "ScriptCategory": { "allOf": [ { - "$ref": "#/components/schemas/processRequestTokenEditable" + "$ref": "#/components/schemas/ScriptCategoryEditable" }, { "properties": { @@ -8331,28 +10190,6 @@ "type": "string", "format": "id" }, - "process_id": { - "type": "string", - "format": "id" - }, - "process_request_id": { - "type": "string", - "format": "id" - }, - "element_id": { - "type": "string", - "format": "id" - }, - "element_type": { - "type": "string", - "format": "id" - }, - "element_index": { - "type": "string" - }, - "element_name": { - "type": "string" - }, "created_at": { "type": "string", "format": "date-time" @@ -8360,61 +10197,37 @@ "updated_at": { "type": "string", "format": "date-time" - }, - "initiated_at": { - "type": "string", - "format": "date-time" - }, - "advanceStatus": { - "type": "string" - }, - "due_notified": { - "type": "integer" - }, - "user": { - "type": "object" - }, - "process": { - "type": "object" - }, - "process_request": { - "type": "object" } }, "type": "object" } ] }, - "taskAssignmentsEditable": { + "scriptExecutorsEditable": { "properties": { - "process_id": { - "description": "Represents a business process task assignment definition.", - "type": "integer", - "format": "id" + "title": { + "description": "Represents an Eloquent model of a Script Executor", + "type": "string" }, - "process_task_id": { - "type": "string", - "format": "id" + "description": { + "type": "string" }, - "assignment_id": { - "type": "integer", - "format": "id" + "language": { + "type": "string" }, - "assignment_type": { - "type": "string", - "enum": [ - "ProcessMaker\\Models\\User", - "ProcessMaker\\Models\\Group" - ] + "config": { + "type": "string" + }, + "is_system": { + "type": "boolean" } }, "type": "object" }, - "taskAssignments": { - "type": "object", + "scriptExecutors": { "allOf": [ { - "$ref": "#/components/schemas/taskAssignmentsEditable" + "$ref": "#/components/schemas/scriptExecutorsEditable" }, { "properties": { @@ -8435,97 +10248,123 @@ } ] }, - "screensEditable": { + "availableLanguages": { "properties": { - "title": { - "description": "Class Screen", + "text": { "type": "string" }, - "type": { + "value": { "type": "string" }, - "description": { + "initDockerFile": { + "type": "string" + } + }, + "type": "object" + }, + "securityLog": { + "properties": { + "id": { + "description": "Class SecurityLog", + "type": "integer" + }, + "event": { "type": "string" }, - "config": { - "type": "array", - "items": { - "type": "object" - } + "ip": { + "type": "string" }, - "computed": { + "meta": { "type": "array", "items": { + "properties": { + "os": { + "type": "array", + "items": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object" + } + }, + "browser": { + "type": "array", + "items": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object" + } + }, + "user_agent": { + "type": "string" + } + }, "type": "object" } }, - "watchers": { + "user_id": { + "type": "integer" + }, + "occured_at": { + "type": "string" + } + }, + "type": "object" + }, + "settingsEditable": { + "properties": { + "key": { + "description": "Class Settings", + "type": "string" + }, + "config": { "type": "array", "items": { "type": "object" } }, - "custom_css": { + "name": { "type": "string" }, - "screen_category_id": { + "helper": { "type": "string" - } - }, - "type": "object" - }, - "screens": { - "allOf": [ - { - "$ref": "#/components/schemas/screensEditable" }, - { - "properties": { - "id": { - "type": "string", - "format": "id" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - }, - "type": "object" - } - ] - }, - "screenExported": { - "properties": { - "url": { + "group": { "type": "string" - } - }, - "type": "object" - }, - "ScreenCategoryEditable": { - "properties": { - "name": { - "description": "Represents a business screen category definition.", + }, + "format": { "type": "string" }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "INACTIVE" - ] + "hidden": { + "type": "boolean" + }, + "readonly": { + "type": "boolean" + }, + "variables": { + "type": "string" + }, + "sansSerifFont": { + "type": "string" } }, "type": "object" }, - "ScreenCategory": { + "settings": { "allOf": [ { - "$ref": "#/components/schemas/ScreenCategoryEditable" + "$ref": "#/components/schemas/settingsEditable" }, { "properties": { @@ -8546,71 +10385,166 @@ } ] }, - "ScreenTypeEditable": { + "TokenClient": { "properties": { + "id": { + "type": "integer" + }, + "user_id": { + "type": "integer" + }, "name": { - "description": "Represents a business screen Type definition.", "type": "string" + }, + "provider": { + "type": "string" + }, + "redirect": { + "type": "string" + }, + "personal_access_client": { + "type": "boolean" + }, + "password_client": { + "type": "boolean" + }, + "revoked": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" } }, "type": "object" }, - "ScreenType": { - "allOf": [ - { - "$ref": "#/components/schemas/ScreenTypeEditable" - }, - { - "properties": { - "id": { - "type": "string", - "format": "id" - } - }, - "type": "object" - } - ] - }, - "scriptsEditable": { + "usersEditable": { "properties": { + "email": { + "description": "The attributes that are mass assignable.", + "type": "string", + "format": "email" + }, + "firstname": { + "type": "string" + }, + "lastname": { + "type": "string" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "address": { + "type": "string" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "postal": { + "type": "string" + }, + "country": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "fax": { + "type": "string" + }, + "cell": { + "type": "string" + }, "title": { - "description": "Represents an Eloquent model of a Script", "type": "string" }, - "description": { + "timezone": { + "type": "string" + }, + "datetime_format": { "type": "string" }, "language": { "type": "string" }, - "code": { + "is_administrator": { + "type": "boolean" + }, + "expires_at": { "type": "string" }, - "timeout": { - "type": "integer" + "loggedin_at": { + "type": "string" }, - "run_as_user_id": { - "type": "integer" + "remember_token": { + "type": "string" }, - "key": { + "status": { + "type": "string", + "enum": [ + "ACTIVE", + "INACTIVE", + "SCHEDULED", + "OUT_OF_OFFICE", + "BLOCKED" + ] + }, + "fullname": { "type": "string" }, - "script_category_id": { - "type": "integer" + "avatar": { + "type": "string" + }, + "media": { + "type": "array", + "items": { + "$ref": "#/components/schemas/media" + } + }, + "birthdate": { + "type": "string", + "format": "date" + }, + "delegation_user_id": { + "type": "string", + "format": "id" + }, + "manager_id": { + "type": "string", + "format": "id" + }, + "meta": { + "type": "object", + "additionalProperties": true + }, + "force_change_password": { + "type": "boolean" + }, + "email_task_notification": { + "type": "boolean" } }, "type": "object" }, - "scripts": { + "users": { "allOf": [ { - "$ref": "#/components/schemas/scriptsEditable" + "$ref": "#/components/schemas/usersEditable" }, { "properties": { "id": { - "type": "integer", - "format": "id" + "type": "integer" }, "created_at": { "type": "string", @@ -8619,49 +10553,77 @@ "updated_at": { "type": "string", "format": "date-time" + }, + "deleted_at": { + "type": "string", + "format": "date-time" } }, "type": "object" } ] }, - "scriptsPreview": { + "UserToken": { "properties": { - "status": { + "id": { + "type": "string" + }, + "user_id": { + "type": "integer" + }, + "client_id": { + "type": "integer" + }, + "name": { "type": "string" }, - "key": { - "type": "string" + "scopes": { + "type": "object" + }, + "revoked": { + "type": "boolean" + }, + "client": { + "$ref": "#/components/schemas/TokenClient" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" } }, "type": "object" }, - "ScriptCategoryEditable": { + "analyticsReportingEditable": { "properties": { "name": { - "description": "Represents a business script category definition.", "type": "string" }, - "status": { - "type": "string", - "enum": [ - "ACTIVE", - "INACTIVE" - ] + "description": { + "type": "string" + }, + "link": { + "type": "string" } }, "type": "object" }, - "ScriptCategory": { + "analyticsReporting": { "allOf": [ { - "$ref": "#/components/schemas/ScriptCategoryEditable" + "$ref": "#/components/schemas/analyticsReportingEditable" }, { "properties": { "id": { - "type": "string", - "format": "id" + "type": "integer" }, "created_at": { "type": "string", @@ -8670,43 +10632,64 @@ "updated_at": { "type": "string", "format": "date-time" + }, + "created_by_id": { + "type": "string", + "format": "id" + }, + "updated_by_id": { + "type": "string", + "format": "id" } }, "type": "object" } ] }, - "scriptExecutorsEditable": { + "collectionsEditable": { "properties": { - "title": { - "description": "Represents an Eloquent model of a Script Executor", + "name": { "type": "string" }, "description": { "type": "string" }, - "language": { + "custom_title": { "type": "string" }, - "config": { - "type": "string" + "create_screen_id": { + "type": "string", + "format": "id" }, - "is_system": { + "read_screen_id": { + "type": "string", + "format": "id" + }, + "update_screen_id": { + "type": "string", + "format": "id" + }, + "signal_create": { + "type": "boolean" + }, + "signal_update": { + "type": "boolean" + }, + "signal_delete": { "type": "boolean" } }, "type": "object" }, - "scriptExecutors": { + "collections": { "allOf": [ { - "$ref": "#/components/schemas/scriptExecutorsEditable" + "$ref": "#/components/schemas/collectionsEditable" }, { "properties": { "id": { - "type": "integer", - "format": "id" + "type": "integer" }, "created_at": { "type": "string", @@ -8715,306 +10698,182 @@ "updated_at": { "type": "string", "format": "date-time" + }, + "created_by_id": { + "type": "string", + "format": "id" + }, + "updated_by_id": { + "type": "string", + "format": "id" + }, + "columns": { + "type": "array", + "items": { + "type": "object" + } } }, "type": "object" } ] }, - "availableLanguages": { + "recordsEditable": { "properties": { - "text": { - "type": "string" - }, - "value": { - "type": "string" - }, - "initDockerFile": { - "type": "string" + "data": { + "type": "object" } }, "type": "object" }, - "securityLog": { - "properties": { - "id": { - "description": "Class SecurityLog", - "type": "integer" - }, - "event": { - "type": "string" + "records": { + "allOf": [ + { + "$ref": "#/components/schemas/recordsEditable" }, - "ip": { + { + "properties": { + "id": { + "type": "integer" + }, + "collection_id": { + "type": "string", + "format": "id" + } + }, + "type": "object" + } + ] + }, + "DataSourceCallParameters": { + "properties": { + "endpoint": { "type": "string" }, - "meta": { + "dataMapping": { "type": "array", "items": { "properties": { - "os": { - "type": "array", - "items": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "type": "object" - } - }, - "browser": { - "type": "array", - "items": { - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - } - }, - "type": "object" - } + "key": { + "type": "string" }, - "user_agent": { + "value": { "type": "string" } }, "type": "object" } }, - "user_id": { - "type": "integer" - }, - "occured_at": { - "type": "string" - } - }, - "type": "object" - }, - "settingsEditable": { - "properties": { - "key": { - "description": "Class Settings", - "type": "string" - }, - "config": { + "outboundConfig": { "type": "array", "items": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, "type": "object" } - }, - "name": { - "type": "string" - }, - "helper": { - "type": "string" - }, - "group": { - "type": "string" - }, - "format": { - "type": "string" - }, - "hidden": { - "type": "boolean" - }, - "readonly": { - "type": "boolean" - }, - "variables": { - "type": "string" - }, - "sansSerifFont": { - "type": "string" } }, "type": "object" }, - "settings": { - "allOf": [ - { - "$ref": "#/components/schemas/settingsEditable" - }, - { - "properties": { - "id": { - "type": "string", - "format": "id" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - }, - "type": "object" - } - ] - }, - "TokenClient": { + "DataSourceResponse": { "properties": { - "id": { - "type": "integer" - }, - "user_id": { + "status": { "type": "integer" }, - "name": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "redirect": { - "type": "string" - }, - "personal_access_client": { - "type": "boolean" - }, - "password_client": { - "type": "boolean" - }, - "revoked": { - "type": "boolean" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" + "response": { + "type": "object" } }, "type": "object" }, - "usersEditable": { + "dataSourceEditable": { "properties": { - "email": { - "description": "The attributes that are mass assignable.", + "id": { + "description": "Class DataSource", "type": "string", - "format": "email" - }, - "firstname": { - "type": "string" - }, - "lastname": { - "type": "string" - }, - "username": { - "type": "string" - }, - "password": { - "type": "string" - }, - "address": { - "type": "string" - }, - "city": { - "type": "string" - }, - "state": { - "type": "string" - }, - "postal": { - "type": "string" - }, - "country": { - "type": "string" - }, - "phone": { - "type": "string" + "format": "id" }, - "fax": { + "name": { "type": "string" }, - "cell": { + "description": { "type": "string" }, - "title": { + "endpoints": { "type": "string" }, - "timezone": { + "mappings": { "type": "string" }, - "datetime_format": { + "authtype": { "type": "string" }, - "language": { + "credentials": { "type": "string" }, - "is_administrator": { - "type": "boolean" - }, - "expires_at": { + "status": { "type": "string" }, - "loggedin_at": { + "data_source_category_id": { "type": "string" + } + }, + "type": "object" + }, + "dataSource": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/dataSourceEditable" }, - "remember_token": { + { + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + } + ] + }, + "dataSourceCategoryEditable": { + "properties": { + "name": { + "description": "Represents a business data Source category definition.", "type": "string" }, "status": { "type": "string", "enum": [ "ACTIVE", - "INACTIVE", - "SCHEDULED", - "OUT_OF_OFFICE", - "BLOCKED" + "INACTIVE" ] - }, - "fullname": { - "type": "string" - }, - "avatar": { - "type": "string" - }, - "media": { - "type": "array", - "items": { - "$ref": "#/components/schemas/media" - } - }, - "birthdate": { - "type": "string", - "format": "date" - }, - "delegation_user_id": { - "type": "string", - "format": "id" - }, - "manager_id": { - "type": "string", - "format": "id" - }, - "meta": { - "type": "object", - "additionalProperties": true - }, - "force_change_password": { - "type": "boolean" } }, "type": "object" }, - "users": { + "DataSourceCategory": { + "type": "object", "allOf": [ { - "$ref": "#/components/schemas/usersEditable" + "$ref": "#/components/schemas/dataSourceCategoryEditable" }, { "properties": { "id": { - "type": "integer" + "type": "string", + "format": "id" }, "created_at": { "type": "string", @@ -9023,53 +10882,60 @@ "updated_at": { "type": "string", "format": "date-time" - }, - "deleted_at": { - "type": "string", - "format": "date-time" } }, "type": "object" } ] }, - "UserToken": { + "versionHistoryEditable": { "properties": { - "id": { - "type": "string" - }, - "user_id": { + "versionable_id": { + "description": "Class VersionHistoryCollection", "type": "integer" }, - "client_id": { - "type": "integer" + "versionable_type": { + "type": "string" }, "name": { "type": "string" }, - "scopes": { - "type": "object" - }, - "revoked": { - "type": "boolean" - }, - "client": { - "$ref": "#/components/schemas/TokenClient" - }, - "created_at": { - "type": "string", - "format": "date-time" + "subject": { + "type": "string" }, - "updated_at": { - "type": "string", - "format": "date-time" + "description": { + "type": "string" }, - "expires_at": { + "status": { "type": "string", - "format": "date-time" + "enum": [ + "ACTIVE", + "INACTIVE" + ] } }, "type": "object" + }, + "versionHistory": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/versionHistoryEditable" + }, + { + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "type": "object" + } + ] } }, "responses": { @@ -9186,6 +11052,14 @@ "type": "string", "default": "" } + }, + "pmql": { + "name": "pmql", + "in": "query", + "description": "PMQL query to filter results", + "schema": { + "type": "string" + } } }, "securitySchemes": { @@ -9194,9 +11068,9 @@ "description": "Laravel passport oauth2 security.", "flows": { "authorizationCode": { - "authorizationUrl": "http://processmaker.test/oauth/authorize", - "tokenUrl": "http://processmaker.test/oauth/token", - "refreshUrl": "http://processmaker.test/token/refresh", + "authorizationUrl": "https://pm4.local:8092/oauth/authorize", + "tokenUrl": "https://pm4.local:8092/oauth/token", + "refreshUrl": "https://pm4.local:8092/token/refresh", "scopes": {} } } @@ -9208,6 +11082,148 @@ } } }, + "tags": [ + { + "name": "DecisionTableCategories", + "description": "DecisionTableCategories" + }, + { + "name": "DecisionTables", + "description": "DecisionTables" + }, + { + "name": "SavedSearchCharts", + "description": "SavedSearchCharts" + }, + { + "name": "Reports", + "description": "Reports" + }, + { + "name": "SavedSearches", + "description": "SavedSearches" + }, + { + "name": "Users", + "description": "Users" + }, + { + "name": "Groups", + "description": "Groups" + }, + { + "name": "CssSettings", + "description": "CssSettings" + }, + { + "name": "Environment Variables", + "description": "Environment Variables" + }, + { + "name": "Files", + "description": "Files" + }, + { + "name": "Group Members", + "description": "Group Members" + }, + { + "name": "Notifications", + "description": "Notifications" + }, + { + "name": "Permissions", + "description": "Permissions" + }, + { + "name": "Process Categories", + "description": "Process Categories" + }, + { + "name": "Processes", + "description": "Processes" + }, + { + "name": "Process Requests", + "description": "Process Requests" + }, + { + "name": "Request Files", + "description": "Request Files" + }, + { + "name": "Screen Categories", + "description": "Screen Categories" + }, + { + "name": "Screens", + "description": "Screens" + }, + { + "name": "Script Categories", + "description": "Script Categories" + }, + { + "name": "Scripts", + "description": "Scripts" + }, + { + "name": "Rebuild Script Executors", + "description": "Rebuild Script Executors" + }, + { + "name": "Security Logs", + "description": "Security Logs" + }, + { + "name": "Settings", + "description": "Settings" + }, + { + "name": "Signals", + "description": "Signals" + }, + { + "name": "Task Assignments", + "description": "Task Assignments" + }, + { + "name": "Tasks", + "description": "Tasks" + }, + { + "name": "Personal Tokens", + "description": "Personal Tokens" + }, + { + "name": "Processes Variables", + "description": "Processes Variables" + }, + { + "name": "AnalyticsReporting", + "description": "AnalyticsReporting" + }, + { + "name": "Collections", + "description": "Collections" + }, + { + "name": "Comments", + "description": "Comments" + }, + { + "name": "DataSourcesCategories", + "description": "DataSourcesCategories" + }, + { + "name": "DataSources", + "description": "DataSources" + }, + { + "name": "Version History", + "description": "Version History" + } + ], "security": [ { "passport": [], diff --git a/tests/Feature/Api/IntermediateTimerEventTest.php b/tests/Feature/Api/IntermediateTimerEventTest.php index 5dd772f8a5..64efba1b0b 100644 --- a/tests/Feature/Api/IntermediateTimerEventTest.php +++ b/tests/Feature/Api/IntermediateTimerEventTest.php @@ -206,4 +206,44 @@ public function testScheduleIntermediateTimerEventWithMustacheSyntax() $iteToken->refresh(); $this->assertEquals('CLOSED', $iteToken->status); } + + public function testScheduleIntermediateTimerEventWithFEELSyntax() + { + $this->be($this->user); + $data = []; + $data['bpmn'] = Process::getProcessTemplate('IntermediateTimerEventFEEL.bpmn'); + $process = Process::factory()->create($data); + $definitions = $process->getDefinitions(); + $startEvent = $definitions->getEvent('_2'); + // now + 8 minutes + $timestamp = time() + 480; + // Pass variables to calculate the date and time using FEEL: date ~ "T" ~ time + $request = WorkflowManager::triggerStartEvent($process, $startEvent, [ + 'date' => date('Y-m-d', $timestamp), + 'time' => date('H:i:sP', $timestamp) + ]); + $task1 = $request->tokens()->where('element_id', '_3')->first(); + WorkflowManager::completeTask($process, $request, $task1, []); // moves to timer event I guess + + // Time travel 5 minutes into the future + Carbon::setTestNow(Carbon::now()->addMinute(5)); + + // Re-schedule events for artisan call + $scheduleManager = new TaskSchedulerManager(); + $scheduleManager->scheduleTasks(); + \Artisan::call('schedule:run'); + + $iteToken = $request->tokens()->where('element_id', '_5')->firstOrFail(); + $this->assertEquals('ACTIVE', $iteToken->status); // Not enough time has passed + + // Time travel 5 more minutes into the future + Carbon::setTestNow(Carbon::now()->addMinute(5)); + + // Re-schedule events for artisan call + $scheduleManager->scheduleTasks(); + \Artisan::call('schedule:run'); + + $iteToken->refresh(); + $this->assertEquals('CLOSED', $iteToken->status); + } } diff --git a/tests/Feature/Events/EmailEventTest.php b/tests/Feature/Events/EmailEventTest.php new file mode 100644 index 0000000000..79a37a1e9a --- /dev/null +++ b/tests/Feature/Events/EmailEventTest.php @@ -0,0 +1,480 @@ +request = ProcessRequest::factory()->create([ + 'id' => $this->requestId, + 'status' => 'ACTIVE', + ]); + } + + /** + * Test each email event handler individually + */ + public function testEmailSentHandling() + { + $this->runEventTest( + EmailSent::class, + EmailSentEvent::class, + 'X-ProcessMaker-Sent-Message-Event-ID' + ); + } + + /** + * Test EmailDelivered event handling + */ + public function testEmailDeliveredHandling() + { + $this->runEventTest( + EmailDelivered::class, + EmailDeliveredEvent::class, + 'X-ProcessMaker-Delivered-Message-Event-ID' + ); + } + + /** + * Test EmailComplaint event handling + */ + public function testEmailComplaintHandling() + { + $this->runEventTest( + EmailComplaint::class, + ComplaintMessageEvent::class, + 'X-ProcessMaker-Complaint-Message-Event-ID' + ); + } + + /** + * Test EmailViewed event handling + */ + public function testEmailViewedHandling() + { + $this->runEventTest( + EmailViewed::class, + ViewEmailEvent::class, + 'X-ProcessMaker-Viewed-Message-Event-ID' + ); + } + + /** + * Test EmailLinkClicked event handling + */ + public function testEmailLinkClickedHandling() + { + $this->runEventTest( + EmailLinkClicked::class, + LinkClickedEvent::class, + 'X-ProcessMaker-Link-Clicked-Message-Event-ID' + ); + } + + /** + * Common test runner for all event types + */ + private function runEventTest(string $handlerClass, string $eventClass, string $messageEventIdHeader) + { + // Create the event handler + $handler = new $handlerClass(); + + // Set expectations for the WorkflowManager facade + WorkflowManager::shouldReceive('triggerMessageEvent') + ->once() + ->with(Mockery::type(ProcessRequest::class), $this->messageEventId, ['email_id' => $this->emailId]) + ->andReturnTrue(); + + // Create an event mock with appropriate tracker + $trackerMock = $this->createTrackerMock($messageEventIdHeader); + $eventMock = Mockery::mock($eventClass); + $eventMock->sent_email = $trackerMock; + + // Call the handler + $handler->handle($eventMock); + } + + /** + * Test email event header validation for EmailSent + */ + public function testEmailSentHeaderMatch() + { + $this->runHeaderTest( + EmailSent::class, + EmailSentEvent::class, + 'X-ProcessMaker-Sent-Message-Event-ID' + ); + } + + /** + * Test email event header validation for EmailDelivered + */ + public function testEmailDeliveredHeaderMatch() + { + $this->runHeaderTest( + EmailDelivered::class, + EmailDeliveredEvent::class, + 'X-ProcessMaker-Delivered-Message-Event-ID' + ); + } + + /** + * Test email event header validation for EmailComplaint + */ + public function testEmailComplaintHeaderMatch() + { + $this->runHeaderTest( + EmailComplaint::class, + ComplaintMessageEvent::class, + 'X-ProcessMaker-Complaint-Message-Event-ID' + ); + } + + /** + * Test email event header validation for EmailViewed + */ + public function testEmailViewedHeaderMatch() + { + $this->runHeaderTest( + EmailViewed::class, + ViewEmailEvent::class, + 'X-ProcessMaker-Viewed-Message-Event-ID' + ); + } + + /** + * Test email event header validation for EmailLinkClicked + */ + public function testEmailLinkClickedHeaderMatch() + { + $this->runHeaderTest( + EmailLinkClicked::class, + LinkClickedEvent::class, + 'X-ProcessMaker-Link-Clicked-Message-Event-ID' + ); + } + + /** + * Common header test runner for all event types + */ + private function runHeaderTest(string $handlerClass, string $eventClass, string $messageEventIdHeader) + { + // Create the event handler + $handler = new $handlerClass(); + + // Create a special tracker mock that will only accept exact header names + $trackerMock = Mockery::mock(SentEmail::class); + + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Email-ID') + ->once() + ->andReturn($this->emailId); + + $trackerMock->shouldReceive('getHeader') + ->with($messageEventIdHeader) + ->once() + ->andReturn($this->messageEventId); + + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Request-ID') + ->once() + ->andReturn($this->requestId); + + // Create event mock + $eventMock = Mockery::mock($eventClass); + $eventMock->sent_email = $trackerMock; + + // Call the handler + $handler->handle($eventMock); + } + + /** + * Test missing process request handling for EmailSent + */ + public function testEmailSentNoProcessRequest() + { + $this->runMissingRequestTest( + EmailSent::class, + EmailSentEvent::class, + 'X-ProcessMaker-Sent-Message-Event-ID' + ); + } + + /** + * Test missing process request handling for EmailDelivered + */ + public function testEmailDeliveredNoProcessRequest() + { + $this->runMissingRequestTest( + EmailDelivered::class, + EmailDeliveredEvent::class, + 'X-ProcessMaker-Delivered-Message-Event-ID' + ); + } + + /** + * Test missing process request handling for EmailComplaint + */ + public function testEmailComplaintNoProcessRequest() + { + $this->runMissingRequestTest( + EmailComplaint::class, + ComplaintMessageEvent::class, + 'X-ProcessMaker-Complaint-Message-Event-ID' + ); + } + + /** + * Test missing process request handling for EmailViewed + */ + public function testEmailViewedNoProcessRequest() + { + $this->runMissingRequestTest( + EmailViewed::class, + ViewEmailEvent::class, + 'X-ProcessMaker-Viewed-Message-Event-ID' + ); + } + + /** + * Test missing process request handling for EmailLinkClicked + */ + public function testEmailLinkClickedNoProcessRequest() + { + $this->runMissingRequestTest( + EmailLinkClicked::class, + LinkClickedEvent::class, + 'X-ProcessMaker-Link-Clicked-Message-Event-ID' + ); + } + + /** + * Common test runner for missing process request scenarios + */ + private function runMissingRequestTest(string $handlerClass, string $eventClass, string $messageEventIdHeader) + { + // Delete the process request to simulate it not being found + $this->request->delete(); + + // Create the event handler + $handler = new $handlerClass(); + + // WorkflowManager should NOT be called when request isn't found + WorkflowManager::shouldReceive('triggerMessageEvent')->never(); + + // Create event mock with normal tracker + $trackerMock = $this->createTrackerMock($messageEventIdHeader); + $eventMock = Mockery::mock($eventClass); + $eventMock->sent_email = $trackerMock; + + // Call the handler - should not error when no request found + $handler->handle($eventMock); + } + + /** + * Test missing headers handling for EmailSent + */ + public function testEmailSentMissingHeaders() + { + $this->runMissingHeadersTest( + EmailSent::class, + EmailSentEvent::class, + 'X-ProcessMaker-Sent-Message-Event-ID' + ); + } + + /** + * Test missing headers handling for EmailDelivered + */ + public function testEmailDeliveredMissingHeaders() + { + $this->runMissingHeadersTest( + EmailDelivered::class, + EmailDeliveredEvent::class, + 'X-ProcessMaker-Delivered-Message-Event-ID' + ); + } + + /** + * Test missing headers handling for EmailComplaint + */ + public function testEmailComplaintMissingHeaders() + { + $this->runMissingHeadersTest( + EmailComplaint::class, + ComplaintMessageEvent::class, + 'X-ProcessMaker-Complaint-Message-Event-ID' + ); + } + + /** + * Test missing headers handling for EmailViewed + */ + public function testEmailViewedMissingHeaders() + { + $this->runMissingHeadersTest( + EmailViewed::class, + ViewEmailEvent::class, + 'X-ProcessMaker-Viewed-Message-Event-ID' + ); + } + + /** + * Test missing headers handling for EmailLinkClicked + */ + public function testEmailLinkClickedMissingHeaders() + { + $this->runMissingHeadersTest( + EmailLinkClicked::class, + LinkClickedEvent::class, + 'X-ProcessMaker-Link-Clicked-Message-Event-ID' + ); + } + + /** + * Common test runner for missing headers scenarios + */ + private function runMissingHeadersTest(string $handlerClass, string $eventClass, string $messageEventIdHeader) + { + // Create the event handler + $handler = new $handlerClass(); + + // WorkflowManager should NOT be called when headers missing + WorkflowManager::shouldReceive('triggerMessageEvent')->never(); + + // Create tracker mock with missing header + $trackerMock = Mockery::mock(SentEmail::class); + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Email-ID') + ->andReturn($this->emailId); + $trackerMock->shouldReceive('getHeader') + ->with($messageEventIdHeader) + ->andReturn(null); // Missing header! + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Request-ID') + ->andReturn($this->requestId); + + // Create event mock + $eventMock = Mockery::mock($eventClass); + $eventMock->sent_email = $trackerMock; + + // Call the handler - should not error when header missing + $handler->handle($eventMock); + } + + /** + * Helper method to create a tracker mock with standard headers + */ + protected function createTrackerMock(string $messageEventIdHeader) + { + $trackerMock = Mockery::mock(SentEmail::class); + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Email-ID') + ->andReturn($this->emailId); + $trackerMock->shouldReceive('getHeader') + ->with($messageEventIdHeader) + ->andReturn($this->messageEventId); + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Request-ID') + ->andReturn($this->requestId); + + return $trackerMock; + } + + /** + * Test that emails without tracking enabled don't generate warning logs + */ + public function testEmailWithoutTrackingEnabled() + { + // Mock the Log facade using Laravel's built-in support + Log::shouldReceive('debug') + ->withAnyArgs() + ->atLeast(0); + + Log::shouldReceive('error') + ->withAnyArgs() + ->atLeast(0); + + // The important part - we should not see warnings about missing headers + // when no tracking headers are present + Log::shouldReceive('warning') + ->with(Mockery::pattern('/.*missing required headers.*/'), Mockery::any()) + ->never(); + + // Create an event handler (using EmailSent as an example) + $handler = new EmailSent(); + + // Create tracker mock with no tracking headers + $trackerMock = Mockery::mock(SentEmail::class); + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Email-ID') + ->andReturn(null); // No email ID header = tracking not enabled + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Sent-Message-Event-ID') + ->andReturn(null); + $trackerMock->shouldReceive('getHeader') + ->with('X-ProcessMaker-Request-ID') + ->andReturn(null); + + // Create event mock + $eventMock = Mockery::mock(EmailSentEvent::class); + $eventMock->sent_email = $trackerMock; + + // WorkflowManager should NOT be called when tracking not enabled + WorkflowManager::shouldReceive('triggerMessageEvent')->never(); + + // Call the handler - this should not throw an exception + $handler->handle($eventMock); + } + + /** + * Clean up after each test + */ + protected function tearDown(): void + { + parent::tearDown(); + Mockery::close(); + } +}