├── .github ├── CODEOWNERS └── workflows │ ├── commitlint.yml │ ├── go.yml │ ├── lint_javascript.yml │ ├── release.yml │ └── test_npm_modules.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── .pre-commit-config.yaml ├── .prettierignore ├── .prettierrc ├── .releaserc ├── .vscode ├── launch.json └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── auditing ├── auditing.go └── auditing_test.go ├── casing ├── casing.go └── casing_test.go ├── cmd ├── cliconfig │ ├── config.go │ └── config_test.go ├── client.go ├── database │ ├── architecture.png │ ├── database.go │ └── database_test.go ├── deploy.go ├── generate.go ├── init.go ├── keel │ └── main.go ├── localTraceExporter │ ├── client.go │ └── export.go ├── program │ ├── client.go │ ├── commands.go │ ├── default.pem │ ├── init.go │ ├── model.go │ └── views.go ├── root.go ├── run.go ├── secrets.go ├── seed.go ├── test.go └── validate.go ├── codegen ├── generatedfiles.go └── writer.go ├── colors └── colors.go ├── commitlint.config.js ├── compose.yaml ├── config ├── auth.go ├── config.go ├── config_test.go ├── deploy.go ├── fixtures │ ├── test_additional_properties_invalid.yaml │ ├── test_auth.yaml │ ├── test_auth_allow_tokenurl_and_authurl.yaml │ ├── test_auth_duplicate_names.yaml │ ├── test_auth_identity_claims.yaml │ ├── test_auth_invalid_hooks.yaml │ ├── test_auth_invalid_issuer.yaml │ ├── test_auth_invalid_names.yaml │ ├── test_auth_invalid_redirect_url.yaml │ ├── test_auth_invalid_types.yaml │ ├── test_auth_missing_client_ids.yaml │ ├── test_auth_missing_names.yaml │ ├── test_auth_negative_token_lifespan.yaml │ ├── test_auth_reserved_prefix.yaml │ ├── test_auth_same_issuers.yaml │ ├── test_default_api_false.yaml │ ├── test_default_api_true.yaml │ ├── test_deploy.yaml │ ├── test_deploy_database_invalid.yaml │ ├── test_deploy_database_rds.yaml │ ├── test_deploy_database_rds_invalid.yaml │ ├── test_deploy_invalid_project_name.yaml │ ├── test_deploy_invalid_region.yaml │ ├── test_deploy_jobs.yaml │ ├── test_deploy_jobs_invalid.yaml │ ├── test_deploy_missing_project_name.yaml │ ├── test_deploy_missing_region.yaml │ ├── test_deploy_project_name_invalid.yaml │ ├── test_deploy_project_name_missing.yaml │ ├── test_deploy_region_invalid.yaml │ ├── test_deploy_region_missing.yaml │ ├── test_deploy_telemetry.yaml │ ├── test_deploy_telemetry_empty.yaml │ ├── test_duplicates.yaml │ ├── test_environment_invalid.yaml │ ├── test_environment_valid.yaml │ ├── test_keel_auth_disable.yaml │ ├── test_secrets_invalid.yaml │ └── test_tools.yaml ├── format.go ├── schema.json └── tools.go ├── cron ├── cron.go └── cron_test.go ├── db ├── connection_info.go ├── context.go ├── db.go ├── db_test.go └── gorm.go ├── deploy ├── build.go ├── lambdas │ ├── functions │ │ ├── dev-server.js │ │ └── main.js.go.tmpl │ └── runtime │ │ ├── api.go │ │ ├── cmd │ │ └── main.go │ │ ├── db.go │ │ ├── events.go │ │ ├── files.go │ │ ├── flows.go │ │ ├── functions.go │ │ ├── handler.go │ │ ├── jobs.go │ │ ├── secrets.go │ │ └── tracing.go ├── logging.go ├── logs.go ├── migrations.go ├── program.go ├── run.go ├── secrets.go ├── setup.go └── stack.go ├── docs ├── images │ ├── cli-run.png │ ├── keel-light.svg │ └── keel.svg └── testing.md ├── events ├── events.go └── events_test.go ├── expressions ├── errors.go ├── library.go ├── options │ └── options.go ├── parser.go ├── parser_test.go ├── resolve │ ├── field_lookups.go │ ├── field_lookups_test.go │ ├── ident.go │ ├── ident_array.go │ ├── ident_array_test.go │ ├── ident_test.go │ ├── operands.go │ ├── operands_test.go │ ├── value.go │ ├── value_test.go │ └── visitor.go └── typing │ ├── provider.go │ └── types.go ├── formatting ├── collections.go └── collections_test.go ├── functions ├── functions.go ├── http_transport.go └── staticanalysis.go ├── go.mod ├── go.sum ├── integration ├── .gitignore ├── integration_test.go └── testdata │ ├── action_errors │ ├── functions │ │ ├── createDbPermissionFn.ts │ │ ├── createIsAuthenticatedFn.ts │ │ ├── createNotPermittedFn.ts │ │ ├── createPermittedFn.ts │ │ ├── deleteDbPermissionFn.ts │ │ ├── deleteNotPermittedFn.ts │ │ ├── deletePermittedFn.ts │ │ ├── getDbPermissionFn.ts │ │ ├── getNotPermittedFn.ts │ │ ├── getPermittedFn.ts │ │ ├── listDbPermissionFn.ts │ │ ├── listNotPermittedFn.ts │ │ ├── listPermittedFn.ts │ │ ├── updateDbPermissionFn.ts │ │ ├── updateNotPermittedFn.ts │ │ └── updatePermittedFn.ts │ ├── schema.keel │ └── tests.test.ts │ ├── arrays │ ├── schema.keel │ └── tests.test.ts │ ├── arrays_defaults │ ├── schema.keel │ └── tests.test.ts │ ├── arrays_expressions │ ├── schema.keel │ └── tests.test.ts │ ├── arrays_functions │ ├── functions │ │ ├── createThing.ts │ │ ├── createThingEmpty.ts │ │ ├── getThing.ts │ │ ├── updateThingEmpty.ts │ │ └── writeThing.ts │ ├── schema.keel │ └── tests.test.ts │ ├── arrays_relationships │ ├── schema.keel │ └── tests.test.ts │ ├── audit_logs │ ├── functions │ │ ├── inviteGuest.ts │ │ └── inviteMany.ts │ ├── jobs │ │ ├── updateHeadCount.ts │ │ └── updateHeadCountWithKysely.ts │ ├── schema.keel │ └── tests.test.ts │ ├── built_in_actions │ ├── main.test.ts │ └── schema.keel │ ├── client_auth │ ├── keelconfig.yaml │ ├── main.test.ts │ └── schema.keel │ ├── client_basic │ ├── main.test.ts │ └── schema.keel │ ├── client_files │ ├── functions │ │ └── writeAccounts.ts │ ├── main.test.ts │ └── schema.keel │ ├── client_password_grant │ ├── keelconfig.yaml │ ├── main.test.ts │ └── schema.keel │ ├── computed_fields │ ├── schema.keel │ └── tests.test.ts │ ├── computed_fields_circular_ref │ ├── schema.keel │ └── tests.test.ts │ ├── computed_fields_many_to_one │ ├── schema.keel │ └── tests.test.ts │ ├── computed_fields_model_field │ ├── schema.keel │ └── tests.test.ts │ ├── computed_fields_null │ ├── main.test.ts │ └── schema.keel │ ├── computed_fields_one_to_many │ ├── schema.keel │ └── tests.test.ts │ ├── computed_fields_one_to_one │ ├── schema.keel │ └── tests.test.ts │ ├── computed_fields_self_referencing │ ├── main.test.ts │ └── schema.keel │ ├── duration │ ├── functions │ │ ├── createDurationInHook.ts │ │ ├── writeAndDuplicate.ts │ │ └── writeCustomFunction.ts │ ├── schema.keel │ └── tests.test.ts │ ├── embedded_data │ ├── main.test.ts │ └── schema.keel │ ├── env_vars │ ├── keelconfig.yaml │ ├── schema.keel │ └── tests.test.ts │ ├── events_basic │ ├── functions │ │ ├── createPersonFn.ts │ │ ├── updateTrackers.ts │ │ └── writeRandomPersons.ts │ ├── jobs │ │ └── createRandomPersons.ts │ ├── schema.keel │ ├── subscribers │ │ ├── verifyEmail.ts │ │ └── verifyUpdate.ts │ └── tests.test.ts │ ├── facets │ ├── schema.keel │ └── tests.test.ts │ ├── files │ ├── functions │ │ ├── createFileAndStoreInHook.ts │ │ ├── createFileInHook.ts │ │ ├── getFileEmptyHooks.ts │ │ ├── getFileNumerateContents.ts │ │ ├── kyselyTests.ts │ │ ├── listFilesEmptyHooks.ts │ │ ├── modelApiTests.ts │ │ ├── presignedUrl.ts │ │ ├── storeAndWriteMany.ts │ │ ├── updateFileEmptyHooks.ts │ │ └── writeMany.ts │ ├── schema.keel │ └── tests.test.ts │ ├── flows │ ├── flows │ │ ├── allInputs.ts │ │ ├── errorInFlow.ts │ │ ├── errorInStep.ts │ │ ├── mixedStepTypes.ts │ │ ├── onlyFunctions.ts │ │ ├── onlyPages.ts │ │ ├── scalarStep.ts │ │ ├── singleStep.ts │ │ ├── stepless.ts │ │ ├── timeoutStep.ts │ │ ├── userFlow.ts │ │ ├── validationBoolean.ts │ │ └── validationText.ts │ ├── schema.keel │ └── tests.test.ts │ ├── functions_auth_hooks │ ├── functions │ │ └── auth │ │ │ ├── afterAuthentication.ts │ │ │ └── afterIdentityCreated.ts │ ├── keelconfig.yaml │ ├── schema.keel │ └── tests.test.ts │ ├── functions_custom │ ├── functions │ │ ├── bulkPersonUpload.ts │ │ ├── countName.ts │ │ ├── countNameAdvanced.ts │ │ ├── createAndCount.ts │ │ ├── createFromFile.ts │ │ ├── createManyAndCount.ts │ │ ├── createPerson.ts │ │ ├── customPersonSearch.ts │ │ ├── customSearch.ts │ │ ├── fileInputHandling.ts │ │ ├── noInputs.ts │ │ ├── people.ts │ │ ├── readComplex.ts │ │ ├── readFile.ts │ │ ├── updatePhoto.ts │ │ └── updateWithFile.ts │ ├── schema.keel │ └── tests.test.ts │ ├── functions_errors │ ├── functions │ │ ├── badRequest.ts │ │ ├── hookNotFound.ts │ │ └── hookNotFoundCustomMessage.ts │ ├── main.test.ts │ └── schema.keel │ ├── functions_hooks │ ├── functions │ │ ├── createAuthorAndBooks.ts │ │ ├── createBookAfterWrite.ts │ │ ├── createBookAfterWriteErrorRollback.ts │ │ ├── createBookAndAuthor.ts │ │ ├── createBookBeforeWrite.ts │ │ ├── createBookBeforeWriteSync.ts │ │ ├── createBookBeforeWriteWithCover.ts │ │ ├── createBookNoInputs.ts │ │ ├── createBookWithAuthor.ts │ │ ├── deleteBookAfterWrite.ts │ │ ├── deleteBookBeforeQuery.ts │ │ ├── deleteBookBeforeQueryReturnRecord.ts │ │ ├── deleteBookBeforeWrite.ts │ │ ├── deleteBookNoInputs.ts │ │ ├── getBookAfterQuery.ts │ │ ├── getBookAfterQueryPermissions.ts │ │ ├── getBookBeforeQueryFirstOrNull.ts │ │ ├── getBookBeforeQueryQueryBuilder.ts │ │ ├── getBookNoInputs.ts │ │ ├── listBooksAfterQuery.ts │ │ ├── listBooksAfterQueryPermissions.ts │ │ ├── listBooksBeforeQuery.ts │ │ ├── listBooksBeforeQueryReturnValues.ts │ │ ├── updateBookAfterWrite.ts │ │ ├── updateBookBeforeQuery.ts │ │ ├── updateBookBeforeWrite.ts │ │ └── updateBookNoInputs.ts │ ├── main.test.ts │ └── schema.keel │ ├── functions_http │ ├── functions │ │ ├── withForm.ts │ │ ├── withHeaders.ts │ │ ├── withQueryParams.ts │ │ └── withStatus.ts │ ├── schema.keel │ └── tests.test.ts │ ├── functions_list_query_inputs │ ├── functions │ │ ├── listOptionalFieldsWithOptionalInputs.ts │ │ ├── listOptionalFieldsWithRequiredInputs.ts │ │ ├── listOptionalInputs.ts │ │ └── listRequiredInputs.ts │ ├── schema.keel │ └── tests.test.ts │ ├── functions_model_api │ ├── functions │ │ └── listPosts.ts │ ├── main.test.ts │ └── schema.keel │ ├── functions_permissions │ ├── functions │ │ ├── createAdmission.ts │ │ ├── createUser.ts │ │ ├── getUser.ts │ │ └── listUsersByOrganisation.ts │ ├── main.test.ts │ └── schema.keel │ ├── functions_use_database │ ├── functions │ │ └── getPostButOnlyIfItsAfter2020.ts │ ├── main.test.ts │ └── schema.keel │ ├── graphql │ ├── main.test.ts │ └── schema.keel │ ├── headers │ ├── functions │ │ ├── createPersonUsingHook.ts │ │ └── writePersonUsingCustomFunc.ts │ ├── schema.keel │ └── tests.test.ts │ ├── identity_and_authentication │ ├── functions │ │ └── readPostsByTeam.ts │ ├── keelconfig.yaml │ ├── schema.keel │ └── tests.test.ts │ ├── identity_backlinks │ ├── schema.keel │ └── tests.test.ts │ ├── identity_claims │ ├── keelconfig.yaml │ ├── schema.keel │ └── tests.test.ts │ ├── jobs_basic │ ├── jobs │ │ ├── allInputTypes.ts │ │ ├── updateAllViewCount.ts │ │ ├── updateGoldStarFromEnv.ts │ │ └── updateViewCount.ts │ ├── keelconfig.yaml │ ├── schema.keel │ └── tests.test.ts │ ├── jobs_permissions │ ├── jobs │ │ ├── manualJob.ts │ │ ├── manualJobDeniedInCode.ts │ │ ├── manualJobEnvExpression.ts │ │ ├── manualJobEnvExpression2.ts │ │ ├── manualJobIsAuthenticatedExpression.ts │ │ ├── manualJobMultiPermission.ts │ │ ├── manualJobMultiRoles.ts │ │ ├── manualJobTrueExpression.ts │ │ ├── manualJobWithException.ts │ │ └── scheduledWithoutPermissions.ts │ ├── keelconfig.yaml │ ├── schema.keel │ └── tests.test.ts │ ├── nullable_in_expressions │ ├── main.test.ts │ └── schema.keel │ ├── nullable_inputs │ ├── main.test.ts │ └── schema.keel │ ├── operation_list_empty_array │ ├── main.test.ts │ └── schema.keel │ ├── operation_list_pagination │ ├── main.test.ts │ └── schema.keel │ ├── operation_list_where_explicit │ ├── main.test.ts │ └── schema.keel │ ├── operation_list_where_permutations │ ├── main.test.ts │ └── schema.keel │ ├── operation_list_where_relative_dates │ ├── functions │ │ └── listPostsByDateWithHook.ts │ ├── main.test.ts │ └── schema.keel │ ├── operation_multiple_list_ops │ ├── schema.keel │ └── tests.test.ts │ ├── orderby_list_operation │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_and_roles │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_create_operation │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_custom_function │ ├── functions │ │ ├── createPost.ts │ │ ├── createPostWithRole.ts │ │ ├── deletePost.ts │ │ ├── getPost.ts │ │ ├── getSecretPost.ts │ │ ├── listPosts.ts │ │ └── updatePost.ts │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_delete_operation │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_get_operation │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_list_operation │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_update_operation │ ├── schema.keel │ └── tests.test.ts │ ├── permissions_user_role_model │ ├── functions │ │ └── getTaskFn.ts │ ├── schema.keel │ └── tests.test.ts │ ├── real_world_bank_account_app │ ├── bank_account.test.ts │ └── schema.keel │ ├── real_world_invoice_system │ ├── main.test.ts │ └── schema.keel │ ├── real_world_product_catalog │ ├── product_catalog.test.ts │ └── schema.keel │ ├── real_world_user_organisations │ ├── schema.keel │ └── tests.test.ts │ ├── real_world_user_permissions │ ├── functions │ │ ├── createAccountFn.ts │ │ └── listAccountFn.ts │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_create_with_id │ ├── main.test.ts │ └── schema.keel │ ├── relationships_creating_many_to_many │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_creating_referencing_ids │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_creating_related_data │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_filtering_expressions │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_filtering_implicit_inputs │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_in_identity_backlinks │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_in_permissions │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_in_where │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_mutations_and_queries_by_ids │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_one_to_one │ ├── schema.keel │ └── tests.test.ts │ ├── relationships_self_referencing │ ├── schema.keel │ └── tests.test.ts │ ├── route_functions │ ├── routes │ │ ├── databaseHandler.ts │ │ ├── getHandler.ts │ │ ├── headersHandler.ts │ │ ├── pathParamHandler.ts │ │ ├── postHandler.ts │ │ ├── rawBodyHandler.ts │ │ └── statusHandler.ts │ ├── schema.keel │ └── tests.test.ts │ ├── schemas_folder │ ├── main.test.ts │ └── schemas │ │ └── schema.keel │ ├── sequence_attribute │ ├── functions │ │ ├── createInvoiceFunc.ts │ │ ├── deleteInvoiceFunc.ts │ │ ├── getInvoiceFunc.ts │ │ └── updateInvoiceFunc.ts │ ├── main.test.ts │ └── schema.keel │ ├── set_backlinks │ ├── schema.keel │ └── tests.test.ts │ ├── set_create_operation │ ├── schema.keel │ └── tests.test.ts │ ├── set_relationships │ ├── main.test.ts │ └── schema.keel │ ├── set_update_operation │ ├── schema.keel │ └── tests.test.ts │ ├── simple_database_test │ ├── schema.keel │ └── tests.test.ts │ ├── sortable_list_operation │ ├── schema.keel │ └── tests.test.ts │ ├── sortable_list_pagination │ ├── schema.keel │ └── tests.test.ts │ ├── subscribers_basic │ ├── keelconfig.yaml │ ├── schema.keel │ ├── subscribers │ │ ├── subscriberEnvvars.ts │ │ ├── subscriberWithException.ts │ │ └── verifyEmail.ts │ └── tests.test.ts │ ├── unique_constraint │ ├── functions │ │ ├── createProductFn.ts │ │ └── updateProductFn.ts │ ├── schema.keel │ └── tests.test.ts │ └── vectors │ ├── schema.keel │ └── tests.test.ts ├── mail └── send.go ├── main ├── migrations ├── audit.go ├── columns.sql ├── computed_functions.sql ├── constraints.sql ├── flows.sql ├── indexes.sql ├── introspection.go ├── ksuid.sql ├── migrations.go ├── migrations_test.go ├── process_audit.sql ├── seed.go ├── set_identity_id.sql ├── set_trace_id.sql ├── set_updated_at.sql ├── snapshot.go ├── snapshot_test.go ├── sql.go ├── testdata │ ├── array_field_types.txt │ ├── composite_unique_changed.txt │ ├── composite_unique_initial.txt │ ├── computed_field_add_required.txt │ ├── computed_field_changed_expression.txt │ ├── computed_field_changed_to_computed.txt │ ├── computed_field_changed_to_required.txt │ ├── computed_field_initial.txt │ ├── computed_field_many_to_one.txt │ ├── computed_field_many_to_one_changed.txt │ ├── computed_field_model_fields.txt │ ├── computed_field_multiple_depend.txt │ ├── computed_field_removed_attr.txt │ ├── computed_field_removed_field.txt │ ├── computed_field_renamed_field.txt │ ├── computed_field_unchanged.txt │ ├── default_value_arrays.txt │ ├── default_value_changed.txt │ ├── default_value_initial.txt │ ├── field_added_basics.txt │ ├── field_added_reln_hasmany.txt │ ├── field_added_reln_hasone.txt │ ├── field_added_reln_hasone_optional.txt │ ├── field_added_required_and_default.txt │ ├── field_changed_default_attribute.txt │ ├── field_changed_optional.txt │ ├── field_changed_required_and_defaults.txt │ ├── field_changed_unique.txt │ ├── field_removed.txt │ ├── field_removed_reln_hasmany.txt │ ├── field_removed_reln_hasone.txt │ ├── indexes_added_inputs.txt │ ├── indexes_changed.txt │ ├── indexes_removed_inputs.txt │ ├── initial.txt │ ├── model_added.txt │ ├── model_removed.txt │ ├── optional_field.txt │ ├── sequence_field_added.txt │ ├── sequence_field_initial.txt │ ├── sequence_field_removed.txt │ ├── sequence_field_starts_at.txt │ └── simple_field_types.txt └── triggers.sql ├── node ├── bootstrap.go ├── bootstrap_test.go ├── client.go ├── client_test.go ├── codegen.go ├── codegen_test.go ├── development_server.go ├── node.go ├── permissions.go ├── permissions_test.go ├── process_unix.go ├── process_windows.go ├── scaffold.go ├── scaffold_test.go └── templates │ ├── client │ ├── core.ts │ └── types.d.ts │ └── functions │ ├── create.js │ ├── delete.js │ ├── get.js │ ├── list.js │ ├── types.d.ts │ └── update.js ├── packages ├── README.md ├── client-react-query │ ├── .gitignore │ ├── package.json │ ├── pnpm-lock.yaml │ ├── readme.md │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── client-react │ ├── .gitignore │ ├── package.json │ ├── pnpm-lock.yaml │ ├── readme.md │ ├── src │ │ └── index.tsx │ └── tsconfig.json ├── functions-runtime │ ├── .env.test │ ├── .gitignore │ ├── README.md │ ├── compose.yaml │ ├── package.json │ ├── pnpm-lock.yaml │ ├── scripts │ │ └── oas.ts │ ├── src │ │ ├── Duration.test.js │ │ ├── Duration.ts │ │ ├── File.ts │ │ ├── ModelAPI.js │ │ ├── ModelAPI.test.js │ │ ├── QueryBuilder.js │ │ ├── QueryContext.js │ │ ├── RequestHeaders.ts │ │ ├── TimePeriod.js │ │ ├── TimePeriod.test.js │ │ ├── applyAdditionalQueryConstraints.js │ │ ├── applyJoins.js │ │ ├── applyWhereConditions.js │ │ ├── auditing.js │ │ ├── auditing.test.js │ │ ├── camelCasePlugin.js │ │ ├── casing.js │ │ ├── casing.test.js │ │ ├── consts.js │ │ ├── database.ts │ │ ├── errors.js │ │ ├── flows │ │ │ ├── disrupts.ts │ │ │ ├── index.test-d.ts │ │ │ ├── index.ts │ │ │ ├── testingUtils.ts │ │ │ └── ui │ │ │ │ ├── elements │ │ │ │ ├── display │ │ │ │ │ ├── banner.ts │ │ │ │ │ ├── code.ts │ │ │ │ │ ├── divider.ts │ │ │ │ │ ├── grid.test-d.ts │ │ │ │ │ ├── grid.test.ts │ │ │ │ │ ├── grid.ts │ │ │ │ │ ├── header.ts │ │ │ │ │ ├── image.ts │ │ │ │ │ ├── keyValue.ts │ │ │ │ │ ├── list.ts │ │ │ │ │ ├── markdown.ts │ │ │ │ │ └── table.ts │ │ │ │ ├── input │ │ │ │ │ ├── boolean.ts │ │ │ │ │ ├── number.ts │ │ │ │ │ └── text.ts │ │ │ │ └── select │ │ │ │ │ ├── one.test-d.ts │ │ │ │ │ └── one.ts │ │ │ │ ├── index.ts │ │ │ │ ├── page.test-d.ts │ │ │ │ └── page.ts │ │ ├── handleFlow.ts │ │ ├── handleJob.js │ │ ├── handleJob.test.js │ │ ├── handleRequest.js │ │ ├── handleRequest.test.js │ │ ├── handleRoute.js │ │ ├── handleSubscriber.js │ │ ├── index.ts │ │ ├── parsing.js │ │ ├── parsing.test.js │ │ ├── permissions.test.js │ │ ├── permissions.ts │ │ ├── tracing.js │ │ ├── tracing.test.js │ │ ├── tryExecuteFlow.js │ │ ├── tryExecuteFunction.js │ │ ├── tryExecuteJob.js │ │ ├── tryExecuteSubscriber.js │ │ └── type-utils.ts │ ├── tsconfig.json │ ├── tsup.config.ts │ └── vite.config.js ├── keel │ ├── README.md │ ├── package.json │ ├── pnpm-lock.yaml │ └── scripts │ │ └── setup.js ├── testing-runtime │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── pnpm-lock.yaml │ ├── src │ │ ├── ActionExecutor.mjs │ │ ├── Executor.mjs │ │ ├── JobExecutor.mjs │ │ ├── SubscriberExecutor.mjs │ │ ├── index.d.ts │ │ ├── index.mjs │ │ ├── index.test.ts │ │ ├── toHaveAuthenticationError.mjs │ │ ├── toHaveAuthorizationError.mjs │ │ ├── toHaveError.mjs │ │ └── vitest-setup.mjs │ └── vitest.config.mjs └── wasm │ ├── .gitignore │ ├── .npmignore │ ├── encodeWasm.js │ ├── index.d.ts │ ├── index.js │ ├── index.test.ts │ ├── lib │ ├── main.go │ ├── wasm_exec.js │ └── wasm_exec_node.js │ ├── package.json │ └── pnpm-lock.yaml ├── permissions ├── permissions.go └── permissions_test.go ├── proto ├── action.go ├── field.go ├── message.go ├── model.go ├── model_test.go ├── query.go ├── query_test.go ├── schema.go ├── schema.pb.go ├── schema.proto └── schema_test.go ├── rpc ├── opentelemetry │ └── proto │ │ ├── collector │ │ ├── README.md │ │ ├── logs │ │ │ └── v1 │ │ │ │ ├── logs_service.proto │ │ │ │ └── logs_service_http.yaml │ │ ├── metrics │ │ │ └── v1 │ │ │ │ ├── metrics_service.proto │ │ │ │ └── metrics_service_http.yaml │ │ └── trace │ │ │ └── v1 │ │ │ ├── trace_service.proto │ │ │ └── trace_service_http.yaml │ │ ├── common │ │ └── v1 │ │ │ └── common.proto │ │ ├── logs │ │ └── v1 │ │ │ └── logs.proto │ │ ├── metrics │ │ └── v1 │ │ │ └── metrics.proto │ │ ├── resource │ │ └── v1 │ │ │ └── resource.proto │ │ └── trace │ │ └── v1 │ │ └── trace.proto ├── rpc.proto ├── rpc │ ├── rpc.pb.go │ └── rpc.twirp.go └── rpcApi │ ├── context.go │ └── server.go ├── runtime ├── actions │ ├── authenticate.go │ ├── authorization.go │ ├── authorization_test.go │ ├── create.go │ ├── delete.go │ ├── embed.go │ ├── evaluation.go │ ├── expression.go │ ├── files.go │ ├── filters.go │ ├── generate_computed.go │ ├── generate_computed_test.go │ ├── generate_filter.go │ ├── generate_select.go │ ├── get.go │ ├── identity.go │ ├── list.go │ ├── operator.go │ ├── pagination.go │ ├── parsing.go │ ├── parsing_test.go │ ├── query.go │ ├── query_test.go │ ├── scope.go │ ├── update.go │ └── writeinputs.go ├── apis │ ├── authapi │ │ ├── authorize_endpoint.go │ │ ├── authorize_endpoint_test.go │ │ ├── openapi_definition.go │ │ ├── providers_endpoint.go │ │ ├── response.go │ │ ├── revoke_endpoint.go │ │ ├── revoke_endpoint_test.go │ │ ├── token_endpoint.go │ │ └── token_endpoint_test.go │ ├── flowsapi │ │ ├── flow_handler.go │ │ ├── list_flows_endpoint.go │ │ └── my_runs_endpoint.go │ ├── graphql │ │ ├── graphql.go │ │ ├── graphql_test.go │ │ ├── introspection.go │ │ ├── resolvers.go │ │ ├── responses.go │ │ ├── testdata │ │ │ └── graphql │ │ │ │ ├── any_type.graphql │ │ │ │ ├── any_type.keel │ │ │ │ ├── apis.graphql │ │ │ │ ├── apis.keel │ │ │ │ ├── apis_with_actions.graphql │ │ │ │ ├── apis_with_actions.keel │ │ │ │ ├── arrays.graphql │ │ │ │ ├── arrays.keel │ │ │ │ ├── create_operation.graphql │ │ │ │ ├── create_operation.keel │ │ │ │ ├── create_operation_optional_fields.graphql │ │ │ │ ├── create_operation_optional_fields.keel │ │ │ │ ├── delete_operation.graphql │ │ │ │ ├── delete_operation.keel │ │ │ │ ├── enums.graphql │ │ │ │ ├── enums.keel │ │ │ │ ├── facets.graphql │ │ │ │ ├── facets.keel │ │ │ │ ├── get_operation.graphql │ │ │ │ ├── get_operation.keel │ │ │ │ ├── identity_relationships.graphql │ │ │ │ ├── identity_relationships.keel │ │ │ │ ├── list_operation.graphql │ │ │ │ ├── list_operation.keel │ │ │ │ ├── list_operation_optional_fields.graphql │ │ │ │ ├── list_operation_optional_fields.keel │ │ │ │ ├── list_operation_optional_relationship_fields.graphql │ │ │ │ ├── list_operation_optional_relationship_fields.keel │ │ │ │ ├── list_operation_sortable.graphql │ │ │ │ ├── list_operation_sortable.keel │ │ │ │ ├── message_arrays.graphql │ │ │ │ ├── message_arrays.keel │ │ │ │ ├── message_enums.graphql │ │ │ │ ├── message_enums.keel │ │ │ │ ├── messages_as_input_and_response.graphql │ │ │ │ ├── messages_as_input_and_response.keel │ │ │ │ ├── model_as_message_field.graphql │ │ │ │ ├── model_as_message_field.keel │ │ │ │ ├── nested_messages.graphql │ │ │ │ ├── nested_messages.keel │ │ │ │ ├── no_get_operations.graphql │ │ │ │ ├── no_get_operations.keel │ │ │ │ ├── no_inputs.graphql │ │ │ │ ├── no_inputs.keel │ │ │ │ ├── no_with_inputs.graphql │ │ │ │ ├── no_with_inputs.keel │ │ │ │ ├── optional_fields.graphql │ │ │ │ ├── optional_fields.keel │ │ │ │ ├── read_operation.graphql │ │ │ │ ├── read_operation.keel │ │ │ │ ├── relationships.graphql │ │ │ │ ├── relationships.keel │ │ │ │ ├── relationships_one_to_one.graphql │ │ │ │ ├── relationships_one_to_one.keel │ │ │ │ ├── response_message_with_model_field.graphql │ │ │ │ ├── response_message_with_model_field.keel │ │ │ │ ├── scalar_fields.graphql │ │ │ │ ├── scalar_fields.keel │ │ │ │ ├── update_operation.graphql │ │ │ │ ├── update_operation.keel │ │ │ │ ├── write_operation.graphql │ │ │ │ └── write_operation.keel │ │ └── types.go │ ├── httpjson │ │ └── httpjson.go │ └── jsonrpc │ │ └── jsonrpc.go ├── auth │ └── context.go ├── common │ └── common.go ├── expressions │ └── operand.go ├── flows │ ├── authorisation.go │ ├── events.go │ ├── flow.go │ ├── orchestrator.go │ └── run.go ├── jsonschema │ ├── jsonschema.go │ ├── jsonschema_test.go │ └── testdata │ │ ├── arbitrary_function_any.json │ │ ├── arbitrary_function_any.keel │ │ ├── create.json │ │ ├── create.keel │ │ ├── create_array_input.json │ │ ├── create_array_input.keel │ │ ├── create_with_related_models.json │ │ ├── create_with_related_models.keel │ │ ├── get.json │ │ ├── get.keel │ │ ├── list.json │ │ ├── list.keel │ │ ├── list_on_related_field.json │ │ ├── list_on_related_field.keel │ │ ├── list_optional_inputs.json │ │ ├── list_optional_inputs.keel │ │ ├── list_sortable.json │ │ ├── list_sortable.keel │ │ ├── message_arrays.json │ │ ├── message_arrays.keel │ │ ├── message_enums.json │ │ ├── message_enums.keel │ │ ├── message_model_field.json │ │ ├── message_model_field.keel │ │ ├── read.json │ │ ├── read.keel │ │ ├── read_empty_input_message.json │ │ ├── read_empty_input_message.keel │ │ ├── read_named_inputs.json │ │ ├── read_named_inputs.keel │ │ ├── read_nested.json │ │ ├── read_nested.keel │ │ ├── update_all_optional_inputs.json │ │ ├── update_all_optional_inputs.keel │ │ ├── update_no_with_inputs.json │ │ ├── update_no_with_inputs.keel │ │ ├── update_nullable_relationship.json │ │ ├── update_nullable_relationship.keel │ │ ├── update_related_id.json │ │ ├── update_related_id.keel │ │ ├── update_required_inputs.json │ │ ├── update_required_inputs.keel │ │ ├── write.json │ │ ├── write.keel │ │ ├── write_nested.json │ │ └── write_nested.keel ├── locale │ └── context.go ├── oauth │ ├── access_token.go │ ├── access_token_test.go │ ├── auth_code.go │ ├── auth_code_test.go │ ├── id_token.go │ ├── id_token_test.go │ ├── jwt.go │ ├── oauthtest │ │ └── oidc_test_server.go │ ├── refresh_token.go │ └── refresh_token_test.go ├── openapi │ ├── flowConfig.json │ ├── openapi.go │ ├── openapi_test.go │ ├── testdata │ │ ├── api.json │ │ ├── api.keel │ │ ├── api_default.json │ │ ├── api_default.keel │ │ ├── api_identity.json │ │ ├── api_identity.keel │ │ ├── arbitrary_functions.json │ │ ├── arbitrary_functions.keel │ │ ├── arrays.json │ │ ├── arrays.keel │ │ ├── basic.json │ │ ├── basic.keel │ │ ├── embedded_data.json │ │ ├── embedded_data.keel │ │ ├── facets.json │ │ ├── facets.keel │ │ ├── files.json │ │ ├── files.keel │ │ ├── flow.json │ │ ├── flow.keel │ │ ├── job.json │ │ ├── job.keel │ │ ├── job_scheduled.json │ │ ├── job_scheduled.keel │ │ ├── named_inputs.json │ │ ├── named_inputs.keel │ │ ├── no_inputs.json │ │ ├── no_inputs.keel │ │ ├── optional_fields.json │ │ └── optional_fields.keel │ └── uiConfig.json ├── runtime.go ├── runtime_audit_test.go ├── runtime_events_test.go ├── runtime_graphql_test.go ├── runtime_rpc_test.go ├── runtimectx │ ├── ctx.go │ ├── env.go │ ├── mail.go │ ├── now.go │ ├── oauth.go │ ├── private_key.go │ ├── request_headers.go │ ├── secret.go │ └── storage.go ├── runtimetest │ ├── path.go │ └── path_test.go └── types │ └── types.go ├── schema ├── .gitignore ├── attributes │ ├── composite_unique.go │ ├── composite_unique_test.go │ ├── computed.go │ ├── computed_test.go │ ├── default.go │ ├── default_test.go │ ├── permission.go │ ├── permission_test.go │ ├── set.go │ ├── set_test.go │ ├── where.go │ └── where_test.go ├── backlinks.go ├── completions │ ├── completions.go │ ├── completions_test.go │ ├── fixtures │ │ └── keelconfig.yaml │ └── tokens.go ├── definitions │ ├── definitions.go │ └── definitions_test.go ├── format │ ├── format.go │ ├── format_test.go │ ├── testdata │ │ ├── action_empty_with.txt │ │ ├── action_inputs.txt │ │ ├── action_inputs_in_query_and_with.txt │ │ ├── action_inputs_multiline.txt │ │ ├── action_inputs_with_comments.txt │ │ ├── actions.txt │ │ ├── actions_many_attributes.txt │ │ ├── actions_single_function_attribute.txt │ │ ├── api.txt │ │ ├── api_with_actions.txt │ │ ├── arbitrary_function.txt │ │ ├── arbitrary_function_inputs.txt │ │ ├── arbitrary_function_messages_long_line.txt │ │ ├── arbitrary_function_multiline.txt │ │ ├── arbitrary_function_multiple_inputs_long_line.txt │ │ ├── arbitrary_function_repeated_field.txt │ │ ├── basic.txt │ │ ├── casing.txt │ │ ├── comments.txt │ │ ├── enum.txt │ │ ├── expressions.txt │ │ ├── field_attributes.txt │ │ ├── flow_inputs.txt │ │ ├── job.txt │ │ ├── job_inputs.txt │ │ ├── job_inputs_optional.txt │ │ ├── message.txt │ │ ├── model_attributes.txt │ │ ├── model_section_order.txt │ │ ├── optional_field.txt │ │ ├── optional_input.txt │ │ ├── repeated_field.txt │ │ ├── roles.txt │ │ ├── route_functions.txt │ │ ├── schema_order.txt │ │ └── with_inputs.txt │ └── writer.go ├── makeproto.go ├── node │ └── node.go ├── parser │ ├── consts.go │ ├── expressions.go │ ├── parser.go │ └── parser_test.go ├── query │ └── query.go ├── reader │ └── reader.go ├── schema.go ├── schema_test.go ├── testdata │ ├── errors │ │ ├── action_types.keel │ │ ├── actions_no_bare_model_as_input.keel │ │ ├── actions_update_invalid_where_operator.keel │ │ ├── actions_update_no_unique_input.keel │ │ ├── actions_update_non_unique_input.keel │ │ ├── actions_update_non_unique_where.keel │ │ ├── api_duplicate_definitions.keel │ │ ├── api_invalid_action_names.keel │ │ ├── api_names_are_models.keel │ │ ├── api_unsupported_attribute.keel │ │ ├── arbitrary_function_action_types.keel │ │ ├── arbitrary_function_multiple_inputs.keel │ │ ├── arbitrary_functions_unknown_message_return.keel │ │ ├── array_fields_default.keel │ │ ├── array_fields_expression_incorrect_operator.keel │ │ ├── array_fields_expression_type_mismatch.keel │ │ ├── array_fields_orderby.keel │ │ ├── array_fields_relation.keel │ │ ├── array_fields_required.keel │ │ ├── array_fields_set.keel │ │ ├── array_fields_unique.keel │ │ ├── attribute_computed.keel │ │ ├── attribute_computed_expression.keel │ │ ├── attribute_computed_functions.keel │ │ ├── attribute_computed_nullables.keel │ │ ├── attribute_embed.keel │ │ ├── attribute_expression_complex.keel │ │ ├── attribute_expression_invalid_root_model.keel │ │ ├── attribute_expression_unresolvable_lhs.keel │ │ ├── attribute_expression_unresolvable_rhs.keel │ │ ├── attribute_facet.keel │ │ ├── attribute_facet_invalid_args.keel │ │ ├── attribute_on.keel │ │ ├── attribute_on_invalid_action_type_args.keel │ │ ├── attribute_on_invalid_subscriber_arg.keel │ │ ├── attribute_orderby.keel │ │ ├── attribute_orderby_invalid_args.keel │ │ ├── attribute_sequence.keel │ │ ├── attribute_sortable.keel │ │ ├── attribute_sortable_invalid_args.keel │ │ ├── attribute_unique.keel │ │ ├── attribute_unique_invalids_args.keel │ │ ├── clashing_explicit_implicit_inputs.keel │ │ ├── create_inputs_not_mutable_fields.keel │ │ ├── create_missing_identity.keel │ │ ├── create_missing_identity_nested.keel │ │ ├── default_invalid_expression.keel │ │ ├── delete_operation_no_unique_lookup.keel │ │ ├── delete_operation_no_unique_lookup_in_inputs.keel │ │ ├── delete_operation_no_unique_lookup_in_wheres.keel │ │ ├── duplicate_explicit_inputs.keel │ │ ├── duplicate_implicit_inputs.keel │ │ ├── duplicate_relationship_inputs.keel │ │ ├── enum_default_gibberish.keel │ │ ├── enum_default_multiple_conditions.keel │ │ ├── enum_default_no_expression.keel │ │ ├── enum_default_wrong_type.keel │ │ ├── environment_variables.keel │ │ ├── expression_array_operator_mismatch.keel │ │ ├── expression_enum_set_operator_mismatch.keel │ │ ├── expression_enum_set_with_invalid_rhs_operators.keel │ │ ├── expression_enum_where_operator_mismatch.keel │ │ ├── expression_forbidden_lhs_array.keel │ │ ├── expression_in_array.keel │ │ ├── expression_literal_string_in_literal_array.keel │ │ ├── expression_null_non_optional_field.keel │ │ ├── expression_preserving_spaces_and_tabs.keel │ │ ├── expressions_compare_model_operand_base_diff_types.keel │ │ ├── expressions_compare_model_operand_diff_types.keel │ │ ├── expressions_compare_model_operand_incompatible_operator.keel │ │ ├── field_required_of_same_model_type.keel │ │ ├── field_required_with_optional_input.keel │ │ ├── field_unsupported_attribute.keel │ │ ├── fields_invalid_types.keel │ │ ├── fields_names_max_lenght.keel │ │ ├── fields_unique_in_model.keel │ │ ├── flows_permission_attribute.keel │ │ ├── function_disabled_functionality.keel │ │ ├── function_unsupported_attribute.keel │ │ ├── get_operation_no_unique_lookup.keel │ │ ├── get_operation_no_unique_lookup_in_inputs.keel │ │ ├── get_operation_no_unique_lookup_in_wheres.keel │ │ ├── identity_backlinks_invalid_relations_field.keel │ │ ├── identity_backlinks_no_relations_attribute.keel │ │ ├── invalid_arbitrary_function_input_name.keel │ │ ├── invalid_with_usage.keel │ │ ├── jobs_attributes.keel │ │ ├── jobs_duplicate_inputs_names.keel │ │ ├── jobs_duplicate_names.keel │ │ ├── jobs_empty.keel │ │ ├── jobs_invalid_input_types.keel │ │ ├── jobs_permission_attribute_expression_invalid_args.keel │ │ ├── jobs_permission_attribute_role_invalid_args.keel │ │ ├── jobs_schedule.keel │ │ ├── lhs_rhs_type_mismatch.keel │ │ ├── message_in_operation.keel │ │ ├── message_input_with_normal_custom_fn.keel │ │ ├── message_types_and_inline_inputs.keel │ │ ├── messages.keel │ │ ├── model_names_max_lenght.keel │ │ ├── model_unsupported_attribute.keel │ │ ├── name_clashes_enum.keel │ │ ├── name_clashes_message.keel │ │ ├── name_clashes_model.keel │ │ ├── operation_create_missing_inputs.keel │ │ ├── operation_inputs_labels.keel │ │ ├── operation_inputs_match_model_fields.keel │ │ ├── operation_set_expression_forbidden_operator.keel │ │ ├── operation_set_expression_invalid_lhs.keel │ │ ├── operation_set_expression_unresolvable_lhs.keel │ │ ├── operation_set_expression_unresolvable_rhs.keel │ │ ├── operation_set_expression_wrong_type.keel │ │ ├── operation_set_forbidden_value_expression.keel │ │ ├── operation_unsupported_attribute.keel │ │ ├── operation_where_expression_forbidden_operator.keel │ │ ├── operation_where_expression_unresolvable_lhs.keel │ │ ├── operation_where_expression_unresolvable_rhs.keel │ │ ├── operation_where_forbidden_value_expression.keel │ │ ├── operation_where_too_many_arguments.keel │ │ ├── operations_unique_globally.keel │ │ ├── operations_unique_globally_multiple_models.keel │ │ ├── operations_unused_inputs.keel │ │ ├── permission_LHS_identity_comparison.keel │ │ ├── permission_action.keel │ │ ├── permission_attribute_action_as_actiontype_arg.keel │ │ ├── permission_attribute_expression_forbidden_operator.keel │ │ ├── permission_attribute_expression_invalid_operands.keel │ │ ├── permission_attribute_invalid_args.keel │ │ ├── permission_attribute_invalid_roles.keel │ │ ├── permission_attribute_missing_actions.keel │ │ ├── permission_attribute_missing_condition.keel │ │ ├── permission_job.keel │ │ ├── read_write_in_operation.keel │ │ ├── recursive_messages.keel │ │ ├── relationships_attr_empty.keel │ │ ├── relationships_attr_must_be_identifier.keel │ │ ├── relationships_attr_must_be_on_model_field.keel │ │ ├── relationships_attr_must_be_on_non_repeated_field.keel │ │ ├── relationships_attr_must_point_to_related_field.keel │ │ ├── relationships_attr_related_field_must_be_type_this_model.keel │ │ ├── relationships_attr_related_field_must_not_be_duplicated.keel │ │ ├── relationships_create_nested_invalid_belongsto_id_input.keel │ │ ├── relationships_create_nested_missing_all_fields.keel │ │ ├── relationships_create_nested_missing_some_fields.keel │ │ ├── relationships_create_nested_misspelled_field.keel │ │ ├── relationships_create_nested_recognizes_non_create.keel │ │ ├── relationships_create_nested_undertands_optional.keel │ │ ├── relationships_many_to_many.keel │ │ ├── relationships_mixed_unmatched_one_to_many.keel │ │ ├── relationships_mixed_unmatched_one_to_one.keel │ │ ├── relationships_one_to_many_duplicate_relation_to_many_side.keel │ │ ├── relationships_one_to_many_needs_relation_attribute.keel │ │ ├── relationships_one_to_many_no_fields_on_one_side.keel │ │ ├── relationships_one_to_many_no_relation_attributes.keel │ │ ├── relationships_one_to_many_unmatched_many_side.keel │ │ ├── relationships_one_to_many_unmatched_one_side.keel │ │ ├── relationships_one_to_many_unmatched_relation_to_many_side.keel │ │ ├── relationships_one_to_one_mismatch.keel │ │ ├── relationships_one_to_one_missing_relation_attribute.keel │ │ ├── relationships_one_to_one_missing_required_model_input.keel │ │ ├── relationships_one_to_one_multi_missing_relation_attribute.keel │ │ ├── relationships_one_to_one_needs_relation_attribute.keel │ │ ├── relationships_one_to_one_no_unique_attr.keel │ │ ├── relationships_one_to_one_relation_attr_both_sides.keel │ │ ├── relationships_one_to_one_relation_field_is_repeated_field.keel │ │ ├── relationships_one_to_one_relation_field_not_exists.keel │ │ ├── relationships_one_to_one_relation_field_not_valid_type.keel │ │ ├── relationships_one_to_one_unique_and_relation_attr_both_sides.keel │ │ ├── relationships_one_to_one_with_relation_attr_no_unique_attr.keel │ │ ├── relationships_one_to_one_with_relation_on_wrong_side.keel │ │ ├── relationships_optional_on_many.keel │ │ ├── relationships_update_nested_invalid_input.keel │ │ ├── request_headers_invalid_dot_notation.keel │ │ ├── reserved_action_name_request_password_reset.keel │ │ ├── reserved_action_name_request_password_reset_function.keel │ │ ├── reserved_action_name_reset_password.keel │ │ ├── reserved_action_name_reset_password_function.keel │ │ ├── reserved_field_name_created_at.keel │ │ ├── reserved_field_name_id.keel │ │ ├── reserved_field_name_updated_at.keel │ │ ├── reserved_model_name_identity.keel │ │ ├── reserved_model_names.keel │ │ ├── returns_edge_cases.keel │ │ ├── returns_required_with_arbitrary_action_types.keel │ │ ├── role_duplicate_definitions.keel │ │ ├── route_functions.keel │ │ ├── set_attribute_backlink_repeated_fields.keel │ │ ├── set_attribute_backlink_repeated_lhs_fields.keel │ │ ├── set_attribute_built_in_fields.keel │ │ ├── set_attribute_ctx_identity_fields_invalid_types.keel │ │ ├── set_attribute_ctx_identity_invalid_fields.keel │ │ ├── set_attribute_invalid_assignment_condition.keel │ │ ├── set_attribute_lhs_is_invalid.keel │ │ ├── set_attribute_lhs_not_within_write_scope.keel │ │ ├── set_attribute_rhs_is_invalid.keel │ │ ├── set_invalid_operators.keel │ │ ├── unique_composite_lookup.keel │ │ ├── unique_enum.keel │ │ ├── unique_input_bare_model.keel │ │ ├── unique_input_non_unique_relationship.keel │ │ ├── unique_lookup.keel │ │ ├── unique_lookup_model.keel │ │ ├── unique_lookup_nested.keel │ │ ├── unique_messages.keel │ │ ├── unique_model_names.keel │ │ ├── unique_restrictions.keel │ │ ├── unresolvable_lhs_with_incorrect_operator.keel │ │ ├── update_inputs_not_mutable_fields.keel │ │ ├── update_operation_no_unique_lookup.keel │ │ ├── update_operation_no_unique_lookup_in_inputs.keel │ │ ├── update_operation_no_unique_lookup_in_wheres.keel │ │ ├── where_expression_invalid_operands.keel │ │ ├── where_expression_single_string_literal.keel │ │ └── where_mismatched_operator_for_matching_lhs_rhs.keel │ ├── generate_testdata.go │ └── proto │ │ ├── action_inputs_in_both_query_and_with │ │ ├── proto.json │ │ └── schema.keel │ │ ├── action_inputs_long_form │ │ ├── proto.json │ │ └── schema.keel │ │ ├── action_inputs_with_empty │ │ ├── proto.json │ │ └── schema.keel │ │ ├── action_inputs_with_omitted │ │ ├── proto.json │ │ └── schema.keel │ │ ├── action_no_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_default_api_false │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_default_api_model_with_no_actions │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_none_defined_and_default_api_false │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_none_defined_and_default_api_true │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_override_default_api_actions │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_override_default_api_models │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_with_actions_specified │ │ ├── proto.json │ │ └── schema.keel │ │ ├── apis_without_actions_specified │ │ ├── proto.json │ │ └── schema.keel │ │ ├── arbitrary_function │ │ ├── proto.json │ │ └── schema.keel │ │ ├── arbitrary_function_any_type │ │ ├── proto.json │ │ └── schema.keel │ │ ├── arbitrary_function_inline_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── arbitrary_function_no_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── arbitrary_functions_any │ │ ├── proto.json │ │ └── schema.keel │ │ ├── array_fields │ │ ├── proto.json │ │ └── schema.keel │ │ ├── array_fields_default │ │ ├── proto.json │ │ └── schema.keel │ │ ├── array_fields_optional │ │ ├── proto.json │ │ └── schema.keel │ │ ├── array_fields_set │ │ ├── proto.json │ │ └── schema.keel │ │ ├── attribute_computed │ │ ├── proto.json │ │ └── schema.keel │ │ ├── attribute_embed │ │ ├── proto.json │ │ └── schema.keel │ │ ├── attribute_facet │ │ ├── proto.json │ │ └── schema.keel │ │ ├── attribute_on │ │ ├── proto.json │ │ └── schema.keel │ │ ├── attribute_orderby │ │ ├── proto.json │ │ └── schema.keel │ │ ├── attribute_sequence │ │ ├── proto.json │ │ └── schema.keel │ │ ├── attribute_sortable │ │ ├── proto.json │ │ └── schema.keel │ │ ├── basics │ │ ├── proto.json │ │ └── schema.keel │ │ ├── computed_fields │ │ ├── proto.json │ │ └── schema.keel │ │ ├── create_inputs_missing_reln_valid │ │ ├── proto.json │ │ └── schema.keel │ │ ├── default_zero │ │ ├── proto.json │ │ └── schema.keel │ │ ├── delete_action │ │ ├── proto.json │ │ └── schema.keel │ │ ├── delete_operation_unique_lookup_in_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── delete_operation_unique_lookup_in_wheres │ │ ├── proto.json │ │ └── schema.keel │ │ ├── empty_fields │ │ ├── proto.json │ │ └── schema.keel │ │ ├── enum_default │ │ ├── proto.json │ │ └── schema.keel │ │ ├── enums │ │ ├── proto.json │ │ └── schema.keel │ │ ├── environment_variables │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expression_enum_field_comparison │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expression_enum_set │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expression_identity_field_comparison │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expression_named_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expression_nullish_optional_comparison │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expressions │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expressions_compare_model_operand │ │ ├── proto.json │ │ └── schema.keel │ │ ├── expressions_null_values │ │ ├── proto.json │ │ └── schema.keel │ │ ├── field_array_of_same_message_type │ │ ├── proto.json │ │ └── schema.keel │ │ ├── field_optional_of_same_message_type │ │ ├── proto.json │ │ └── schema.keel │ │ ├── field_optional_of_same_model_type │ │ ├── proto.json │ │ └── schema.keel │ │ ├── fields_attributes │ │ ├── proto.json │ │ └── schema.keel │ │ ├── fields_names │ │ ├── proto.json │ │ └── schema.keel │ │ ├── fields_optional │ │ ├── proto.json │ │ └── schema.keel │ │ ├── fields_types │ │ ├── proto.json │ │ └── schema.keel │ │ ├── flow_fields │ │ ├── proto.json │ │ └── schema.keel │ │ ├── flow_permissions │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── functions │ │ ├── proto.json │ │ └── schema.keel │ │ ├── functions_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── get_operation_unique_lookup_in_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── get_operation_unique_lookup_in_wheres │ │ ├── proto.json │ │ └── schema.keel │ │ ├── identity_backlinks │ │ ├── proto.json │ │ └── schema.keel │ │ ├── identity_backlinks_relation_attribute │ │ ├── proto.json │ │ └── schema.keel │ │ ├── identity_claims │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── job_adhoc │ │ ├── proto.json │ │ └── schema.keel │ │ ├── job_fields │ │ ├── proto.json │ │ └── schema.keel │ │ ├── job_permissions_expressions │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── job_permissions_roles │ │ ├── proto.json │ │ └── schema.keel │ │ ├── job_scheduled │ │ ├── proto.json │ │ └── schema.keel │ │ ├── message_enum_field │ │ ├── proto.json │ │ └── schema.keel │ │ ├── message_file_field │ │ ├── proto.json │ │ └── schema.keel │ │ ├── message_type │ │ ├── proto.json │ │ └── schema.keel │ │ ├── message_type_nested_model │ │ ├── proto.json │ │ └── schema.keel │ │ ├── models_multiple │ │ ├── proto.json │ │ └── schema.keel │ │ ├── models_permissions │ │ ├── proto.json │ │ └── schema.keel │ │ ├── models_unique │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_attr_set │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_attr_set_null │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_attr_validate │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_attr_where │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_create_all_required_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_create_mutate_id │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_create_relationships │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_get_unique_input │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_get_unique_where │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_list_nested_input_optional_model │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_multiple │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_nested_model_input │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_permissions │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_types │ │ ├── proto.json │ │ └── schema.keel │ │ ├── operations_update_mutate_id │ │ ├── proto.json │ │ └── schema.keel │ │ ├── optional_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── optional_nested_input │ │ ├── proto.json │ │ └── schema.keel │ │ ├── permission_expression_with_identity │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relations_both_sides │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relations_multi_uses_reln_attribute │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relations_single │ │ ├── 1.json │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_create_with_explicit_input │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_create_with_implicit_input │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_mixed │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_mixed_one_to_many_single_sided │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_mixed_one_to_one_single_sided │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_mixed_one_to_one_single_sided_2 │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_mixed_with_self_relationship │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_many_multi_one_sided │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_many_multi_one_sided_same_side │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_many_single_sided_multiple │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_both_directions │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_both_directions_missing_relation_attribute │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_both_directions_no_relation_attribute │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_both_sides │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_missing_reln │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_multi_layered │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_nested_create │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_unique_both_sides │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_one_to_one_with_unique │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_self_referencing_multiple │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_self_referencing_no_reln_attribute │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_self_referencing_uses_reln_attribute │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_update_with_explicit_input │ │ ├── proto.json │ │ └── schema.keel │ │ ├── relationships_update_with_implicit_input │ │ ├── proto.json │ │ └── schema.keel │ │ ├── request_headers_expression │ │ ├── proto.json │ │ └── schema.keel │ │ ├── roles │ │ ├── proto.json │ │ └── schema.keel │ │ ├── route_functions │ │ ├── proto.json │ │ └── schema.keel │ │ ├── secrets │ │ ├── keelconfig.yaml │ │ ├── proto.json │ │ └── schema.keel │ │ ├── set_attribute_backlink_repeated_rhs_fields │ │ ├── proto.json │ │ └── schema.keel │ │ ├── set_attribute_backlinks │ │ ├── proto.json │ │ └── schema.keel │ │ ├── set_attribute_ctx_identity_fields │ │ ├── proto.json │ │ └── schema.keel │ │ ├── set_attribute_lhs_within_write_scope │ │ ├── proto.json │ │ └── schema.keel │ │ ├── unique_attribute │ │ ├── proto.json │ │ └── schema.keel │ │ ├── unique_composite_lookup │ │ ├── proto.json │ │ └── schema.keel │ │ ├── unique_lookup │ │ ├── proto.json │ │ └── schema.keel │ │ ├── unique_lookup_model │ │ ├── proto.json │ │ └── schema.keel │ │ ├── unique_lookup_nested │ │ ├── proto.json │ │ └── schema.keel │ │ ├── update_operation_unique_lookup_in_inputs │ │ ├── proto.json │ │ └── schema.keel │ │ ├── update_operation_unique_lookup_in_wheres │ │ ├── proto.json │ │ └── schema.keel │ │ └── where_expression_allowed_operators_builtin_types │ │ ├── proto.json │ │ └── schema.keel └── validation │ ├── api_models.go │ ├── attribute_arguments.go │ ├── casing.go │ ├── computed_attribute.go │ ├── computed_nullable_field.go │ ├── conflicting_value_inputs.go │ ├── create_nested_input_is_many.go │ ├── default_attribute.go │ ├── duplicate_action_names.go │ ├── duplicate_enum_names.go │ ├── duplicate_inputs.go │ ├── duplicate_job_names.go │ ├── duplicate_message_names.go │ ├── duplicate_model_names.go │ ├── embed_attribute.go │ ├── errorhandling │ ├── errors.go │ ├── errors.yml │ ├── format.go │ └── hints.go │ ├── facet_attribute.go │ ├── function_attribute_disallowed_attributes.go │ ├── invalid_with_usage.go │ ├── jobs.go │ ├── messages.go │ ├── name_clashes.go │ ├── not_mutable_inputs.go │ ├── on_attribute.go │ ├── orderby_attribute.go │ ├── permission_attribute.go │ ├── recursive_fields.go │ ├── relationships.go │ ├── required_field_of_same_model_type.go │ ├── route_functions.go │ ├── rules │ ├── actions │ │ ├── actions.go │ │ ├── create_required.go │ │ ├── input_label.go │ │ └── input_type.go │ ├── api │ │ └── api.go │ ├── attribute │ │ └── attribute.go │ ├── field │ │ └── field.go │ ├── model │ │ └── model.go │ ├── role │ │ └── role.go │ └── rules.go │ ├── schedule_attribute.go │ ├── sequence_attribute.go │ ├── set_attribute.go │ ├── sortable_attribute.go │ ├── studio_features.go │ ├── unique_attribute.go │ ├── unique_lookup.go │ ├── unused_inputs.go │ ├── update_nested_inputs.go │ ├── validation.go │ ├── visitor.go │ └── where_attribute.go ├── scripts └── new-test-case.sh ├── shell.nix ├── storage ├── database_storer.go └── storage.go ├── testhelpers ├── default.pem └── testhelpers.go ├── testing ├── aws.go ├── testing.go └── util.go ├── timeperiod ├── timeperiod.go └── timeperiod_test.go ├── tools.go ├── tools ├── config.go ├── diff.go ├── generator.go ├── proto │ ├── query.go │ ├── tools.pb.go │ └── tools.proto ├── service.go ├── testdata │ ├── blog │ │ ├── schema.keel │ │ └── tools.json │ ├── config_use_api │ │ ├── keelconfig.yaml │ │ ├── schema.keel │ │ └── tools.json │ ├── config_use_api_case_insensitive │ │ ├── keelconfig.yaml │ │ ├── schema.keel │ │ └── tools.json │ ├── config_use_api_does_not_exist │ │ ├── keelconfig.yaml │ │ ├── schema.keel │ │ └── tools.json │ ├── entry_activity_actions │ │ ├── schema.keel │ │ └── tools.json │ ├── facets │ │ ├── schema.keel │ │ └── tools.json │ ├── flows │ │ ├── schema.keel │ │ └── tools.json │ ├── generate_testdata.go │ ├── input_get_entry_actions │ │ ├── schema.keel │ │ └── tools.json │ ├── input_get_entry_actions_with_relationships │ │ ├── schema.keel │ │ └── tools.json │ ├── input_lookup_actions │ │ ├── schema.keel │ │ └── tools.json │ ├── input_lookup_actions_with_relationships │ │ ├── schema.keel │ │ └── tools.json │ ├── nested_inputs │ │ ├── schema.keel │ │ └── tools.json │ ├── nested_outputs │ │ ├── schema.keel │ │ └── tools.json │ ├── one_to_many_links │ │ ├── schema.keel │ │ └── tools.json │ ├── with_api_definition_no_config │ │ ├── schema.keel │ │ └── tools.json │ └── without_default_api_no_config │ │ ├── keelconfig.yaml │ │ ├── schema.keel │ │ └── tools.json ├── tools.go ├── tools_test.go └── validator.go ├── tsconfig.json └── util ├── mocks └── client.go ├── privateKey └── pkgen.go ├── process.go ├── schemafinder └── find.go ├── str.go ├── tracing.go └── tracing_test.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | ## code changes will send PR to following users 2 | 3 | * @teamkeel/core 4 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: [pull_request] 3 | 4 | jobs: 5 | commitlint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | with: 10 | fetch-depth: 0 11 | - uses: wagoid/commitlint-github-action@v4 12 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/compilerla/conventional-pre-commit 3 | rev: v1.3.0 4 | hooks: 5 | - id: conventional-pre-commit 6 | stages: [commit-msg] 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.md 2 | pnpm-lock.yaml -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5" 3 | } -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | { "name": "main" }, 4 | { "name": "next", "prerelease": true }, 5 | { "name": "prerelease", "prerelease": true } 6 | ], 7 | "repositoryUrl": "https://github.com/teamkeel/keel", 8 | "debug": true, 9 | "dryRun": false, 10 | "plugins": [ 11 | "@semantic-release/commit-analyzer", 12 | "@semantic-release/git" 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "go.lintTool": "golangci-lint", 3 | "go.lintFlags": [ 4 | "--config=${workspaceFolder}/.golangci.yml", 5 | ], 6 | "go.lintOnSave": "workspace", 7 | "protoc": { 8 | "options": [ 9 | "--proto_path=${workspaceRoot}/tools/proto", 10 | "--proto_path=${workspaceRoot}/proto", 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /cmd/database/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamkeel/keel/874bde74910d5da397c9d628b39724e3708f0c33/cmd/database/architecture.png -------------------------------------------------------------------------------- /cmd/keel/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/teamkeel/keel/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /cmd/localTraceExporter/export.go: -------------------------------------------------------------------------------- 1 | package localTraceExporter 2 | 3 | import ( 4 | "context" 5 | 6 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace" 7 | ) 8 | 9 | // New constructs a new Exporter and starts it. 10 | func New(ctx context.Context) (*otlptrace.Exporter, error) { 11 | return otlptrace.New(ctx, NewClient()) 12 | } 13 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | }; 4 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_allow_tokenurl_and_authurl.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | providers: 3 | - type: oidc 4 | name: myoidc 5 | authorizationUrl: "https://website.com" 6 | issuerUrl: "https://website.com" 7 | tokenUrl: "https://website.com" 8 | clientId: "kasj28fnq09ak" 9 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_duplicate_names.yaml: -------------------------------------------------------------------------------- 1 | # auth.providers.1.name: Duplicate name my_google 2 | 3 | auth: 4 | providers: 5 | - type: google 6 | name: my_google 7 | clientId: foo_1 8 | 9 | # Duplicate name 10 | - type: oidc 11 | name: my_google 12 | issuerUrl: "https://dev-skhlutl45lbqkvhv.us.auth0.com" 13 | clientId: "3jdfh92h2" 14 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_identity_claims.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | providers: 3 | - type: google 4 | name: google 5 | clientId: "1234" 6 | 7 | claims: 8 | - key: https://slack.com/#team_ID 9 | field: teamId 10 | unique: true 11 | - key: something-else 12 | field: somethingElse 13 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_invalid_hooks.yaml: -------------------------------------------------------------------------------- 1 | # auth.hooks.0: auth.hooks.0 must be one of the following: "afterAuthentication", "afterIdentityCreated" 2 | # auth.hooks.1: auth.hooks.1 must be one of the following: "afterAuthentication", "afterIdentityCreated" 3 | 4 | auth: 5 | hooks: [afterAuthenticated, afterIdentity] 6 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_invalid_redirect_url.yaml: -------------------------------------------------------------------------------- 1 | # auth.redirectUrl: Does not match format 'uri' 2 | 3 | auth: 4 | redirectUrl: not a url 5 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_missing_client_ids.yaml: -------------------------------------------------------------------------------- 1 | # auth.providers.0: clientId is required 2 | # auth.providers.1: clientId is required 3 | 4 | auth: 5 | providers: 6 | - type: google 7 | name: google_1 8 | 9 | - type: oidc 10 | name: Baidu 11 | issuerUrl: "https://dev-skhlutl45lbqkvhv.us.auth0.com" -------------------------------------------------------------------------------- /config/fixtures/test_auth_missing_names.yaml: -------------------------------------------------------------------------------- 1 | # auth.providers.0: name is required 2 | # auth.providers.1: name is required 3 | 4 | auth: 5 | providers: 6 | - type: google 7 | clientId: abcdef 8 | 9 | - type: oidc 10 | clientId: abcdef 11 | issuerUrl: https://some.url 12 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_negative_token_lifespan.yaml: -------------------------------------------------------------------------------- 1 | # auth.tokens.accessTokenExpiry: Must be greater than or equal to 1 2 | # auth.tokens.refreshTokenExpiry: Must be greater than or equal to 1 3 | 4 | auth: 5 | tokens: 6 | accessTokenExpiry: -1 7 | refreshTokenExpiry: -1 -------------------------------------------------------------------------------- /config/fixtures/test_auth_reserved_prefix.yaml: -------------------------------------------------------------------------------- 1 | # auth.providers.0.name: Cannot start with 'keel_' 2 | # auth.providers.1.name: Cannot start with 'KEEL_' 3 | 4 | auth: 5 | providers: 6 | - type: google 7 | name: keel_client 8 | clientId: foo_1 9 | 10 | - type: google 11 | name: KEEL_CLIENT 12 | clientId: foo_1 13 | -------------------------------------------------------------------------------- /config/fixtures/test_auth_same_issuers.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | tokens: 3 | accessTokenExpiry: 3600 4 | refreshTokenExpiry: 604800 5 | 6 | providers: 7 | - type: google 8 | name: google_1 9 | clientId: "1234" 10 | 11 | - type: google 12 | name: google_2 13 | clientId: "1234" 14 | 15 | - type: oidc 16 | name: google_3 17 | issuerUrl: https://accounts.google.com/ 18 | clientId: "1234" 19 | -------------------------------------------------------------------------------- /config/fixtures/test_default_api_false.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: false -------------------------------------------------------------------------------- /config/fixtures/test_default_api_true.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: true -------------------------------------------------------------------------------- /config/fixtures/test_deploy.yaml: -------------------------------------------------------------------------------- 1 | deploy: 2 | projectName: my-project 3 | region: us-east-2 4 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_database_rds.yaml: -------------------------------------------------------------------------------- 1 | deploy: 2 | projectName: my-project 3 | region: us-east-2 4 | database: 5 | provider: rds 6 | rds: 7 | storage: 50 8 | multiAz: true 9 | instance: db.t4g.medium 10 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_database_rds_invalid.yaml: -------------------------------------------------------------------------------- 1 | # deploy.database.rds: can only be provided if deploy.database.provider is 'rds' 2 | 3 | deploy: 4 | projectName: my-project 5 | region: us-east-2 6 | database: 7 | provider: external 8 | rds: 9 | instance: db.t4g.medium 10 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_invalid_project_name.yaml: -------------------------------------------------------------------------------- 1 | # deploy.projectName: Does not match pattern '^[a-z][a-z-]+$' 2 | 3 | deploy: 4 | projectName: My Project 5 | region: eu-west-2 6 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_invalid_region.yaml: -------------------------------------------------------------------------------- 1 | # deploy.region: Does not match pattern '^[a-z]{2}-[a-z]+-\d{1}$' 2 | 3 | deploy: 4 | projectName: my-project 5 | region: Frankfurt 6 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_jobs.yaml: -------------------------------------------------------------------------------- 1 | deploy: 2 | projectName: my-project 3 | region: eu-west-2 4 | jobs: 5 | webhookUrl: https://my-domain.com/webhooks/jobs 6 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_jobs_invalid.yaml: -------------------------------------------------------------------------------- 1 | # deploy.jobs: Additional property foo is not allowed 2 | # deploy.jobs.webhookUrl: Does not match format 'uri' 3 | 4 | deploy: 5 | projectName: my-project 6 | region: eu-west-2 7 | jobs: 8 | foo: bar 9 | webhookUrl: this-is-not-a-url 10 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_missing_project_name.yaml: -------------------------------------------------------------------------------- 1 | # deploy: projectName is required 2 | 3 | deploy: 4 | region: eu-west-2 5 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_missing_region.yaml: -------------------------------------------------------------------------------- 1 | # deploy: region is required 2 | 3 | deploy: 4 | projectName: my-project 5 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_project_name_invalid.yaml: -------------------------------------------------------------------------------- 1 | # deploy.projectName: Does not match pattern '^[a-z][a-z-]+$' 2 | 3 | deploy: 4 | projectName: not valid 5 | region: eu-west-2 6 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_project_name_missing.yaml: -------------------------------------------------------------------------------- 1 | # deploy: projectName is required 2 | 3 | deploy: 4 | region: eu-west-2 5 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_region_invalid.yaml: -------------------------------------------------------------------------------- 1 | # deploy.region: Does not match pattern '^[a-z]{2}-[a-z]+-\d{1}$' 2 | 3 | deploy: 4 | projectName: my-project 5 | region: the-moon 6 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_region_missing.yaml: -------------------------------------------------------------------------------- 1 | # deploy: region is required 2 | 3 | deploy: 4 | projectName: my-project 5 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_telemetry.yaml: -------------------------------------------------------------------------------- 1 | deploy: 2 | projectName: my-project 3 | region: eu-west-2 4 | telemetry: 5 | collector: ./path/to/my/collector.yaml 6 | -------------------------------------------------------------------------------- /config/fixtures/test_deploy_telemetry_empty.yaml: -------------------------------------------------------------------------------- 1 | deploy: 2 | projectName: my-project 3 | region: eu-west-2 4 | telemetry: {} 5 | -------------------------------------------------------------------------------- /config/fixtures/test_environment_valid.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: "TEST" 3 | value: "test" 4 | - name: SOME_OTHER_KEY 5 | value: some-value 6 | -------------------------------------------------------------------------------- /config/fixtures/test_keel_auth_disable.yaml: -------------------------------------------------------------------------------- 1 | disableKeelAuth: true -------------------------------------------------------------------------------- /config/fixtures/test_secrets_invalid.yaml: -------------------------------------------------------------------------------- 1 | # secrets.0.name: Does not match pattern '^[A-Z][A-Z0-9_]*$' 2 | # secrets.1: Additional property value is not allowed 3 | 4 | secrets: 5 | - name: "not-valid-name" 6 | - name: SOME_OTHER_KEY 7 | value: "Value cannot be provided for secrets" 8 | -------------------------------------------------------------------------------- /config/fixtures/test_tools.yaml: -------------------------------------------------------------------------------- 1 | console: 2 | api: tools -------------------------------------------------------------------------------- /config/tools.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type ConsoleConfig struct { 4 | Api *string `yaml:"api,omitempty"` 5 | } 6 | 7 | // AccessTokenExpiry retrieves the configured or default access token expiry. 8 | func (c *ConsoleConfig) ToolsApi() string { 9 | if c.Api != nil { 10 | return *c.Api 11 | } else { 12 | return "api" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/images/cli-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamkeel/keel/874bde74910d5da397c9d628b39724e3708f0c33/docs/images/cli-run.png -------------------------------------------------------------------------------- /integration/.gitignore: -------------------------------------------------------------------------------- 1 | testdata/*/.build 2 | testdata/*/package.json 3 | testdata/*/package-lock.json 4 | testdata/*/pnpm-lock.yaml 5 | testdata/*/tsconfig.json -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/createDbPermissionFn.ts: -------------------------------------------------------------------------------- 1 | import { CreateDbPermissionFn } from "@teamkeel/sdk"; 2 | 3 | export default CreateDbPermissionFn({ 4 | beforeWrite: async (ctx, inputs) => { 5 | return { 6 | title: inputs.title, 7 | lastUpdatedById: inputs.lastUpdatedBy!.id!, 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/createIsAuthenticatedFn.ts: -------------------------------------------------------------------------------- 1 | import { CreateIsAuthenticatedFn } from "@teamkeel/sdk"; 2 | 3 | export default CreateIsAuthenticatedFn({ 4 | beforeWrite: async (ctx, inputs) => { 5 | return { 6 | title: inputs.title, 7 | lastUpdatedById: ctx.identity!.id, 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/createNotPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { CreateNotPermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default CreateNotPermittedFn({ 4 | beforeWrite: async (ctx, inputs) => { 5 | return { 6 | title: inputs.title, 7 | lastUpdatedById: ctx.identity?.id || "", 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/createPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { CreatePermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default CreatePermittedFn({ 4 | beforeWrite: async (ctx, inputs) => { 5 | return { 6 | title: inputs.title, 7 | lastUpdatedById: inputs.lastUpdatedBy!.id!, 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/deleteDbPermissionFn.ts: -------------------------------------------------------------------------------- 1 | import { DeleteDbPermissionFn } from "@teamkeel/sdk"; 2 | 3 | export default DeleteDbPermissionFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/deleteNotPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { DeleteNotPermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default DeleteNotPermittedFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/deletePermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { DeletePermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default DeletePermittedFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/getDbPermissionFn.ts: -------------------------------------------------------------------------------- 1 | import { GetDbPermissionFn } from "@teamkeel/sdk"; 2 | 3 | export default GetDbPermissionFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/getNotPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { GetNotPermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default GetNotPermittedFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/getPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { GetPermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default GetPermittedFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/listDbPermissionFn.ts: -------------------------------------------------------------------------------- 1 | import { ListDbPermissionFn } from "@teamkeel/sdk"; 2 | 3 | export default ListDbPermissionFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/listNotPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { ListNotPermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default ListNotPermittedFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/listPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { ListPermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default ListPermittedFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/updateDbPermissionFn.ts: -------------------------------------------------------------------------------- 1 | import { UpdateDbPermissionFn } from "@teamkeel/sdk"; 2 | 3 | export default UpdateDbPermissionFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/updateNotPermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { UpdateNotPermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default UpdateNotPermittedFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/action_errors/functions/updatePermittedFn.ts: -------------------------------------------------------------------------------- 1 | import { UpdatePermittedFn } from "@teamkeel/sdk"; 2 | 3 | export default UpdatePermittedFn({ 4 | beforeWrite: async (ctx, inputs) => { 5 | return { 6 | title: inputs.values.title, 7 | lastUpdatedById: inputs.values.lastUpdatedBy?.id, 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/arrays_functions/functions/createThingEmpty.ts: -------------------------------------------------------------------------------- 1 | import { CreateThingEmpty } from "@teamkeel/sdk"; 2 | 3 | export default CreateThingEmpty({ 4 | beforeWrite(ctx, inputs, values) { 5 | if (inputs.files?.[0]?.filename === "one.txt") { 6 | } 7 | 8 | return { 9 | ...values, 10 | }; 11 | }, 12 | afterWrite(ctx, inputs, values) { 13 | return { 14 | ...values, 15 | }; 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /integration/testdata/arrays_functions/functions/getThing.ts: -------------------------------------------------------------------------------- 1 | import { GetThing } from "@teamkeel/sdk"; 2 | 3 | export default GetThing(); 4 | -------------------------------------------------------------------------------- /integration/testdata/arrays_functions/functions/updateThingEmpty.ts: -------------------------------------------------------------------------------- 1 | import { UpdateThingEmpty } from "@teamkeel/sdk"; 2 | 3 | export default UpdateThingEmpty({ 4 | beforeWrite(ctx, inputs, values) { 5 | return { 6 | ...values, 7 | }; 8 | }, 9 | afterWrite(ctx, inputs, values) { 10 | return { 11 | ...values, 12 | }; 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /integration/testdata/client_files/functions/writeAccounts.ts: -------------------------------------------------------------------------------- 1 | import { WriteAccounts, models } from "@teamkeel/sdk"; 2 | 3 | // To learn more about jobs, visit https://docs.keel.so/jobs 4 | export default WriteAccounts(async (ctx, inputs) => { 5 | return { csv: inputs.csv }; 6 | }); 7 | -------------------------------------------------------------------------------- /integration/testdata/client_password_grant/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | tokens: 3 | accessTokenExpiry: 61 # An extra 60 to cater for the buffer built into the keelClient refresh cycle 4 | refreshTokenExpiry: 4 5 | -------------------------------------------------------------------------------- /integration/testdata/client_password_grant/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | identity Identity? 5 | } 6 | 7 | actions { 8 | list allPosts() { 9 | @permission(expression: ctx.isAuthenticated) 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /integration/testdata/computed_fields_model_field/schema.keel: -------------------------------------------------------------------------------- 1 | 2 | 3 | model OrderJob { 4 | fields { 5 | customer Customer @computed(orderJob.order.customer) 6 | order Order 7 | } 8 | } 9 | 10 | model Order { 11 | fields { 12 | customer Customer 13 | } 14 | } 15 | 16 | model Customer { 17 | fields { 18 | name Text 19 | orders Order[] 20 | } 21 | } -------------------------------------------------------------------------------- /integration/testdata/duration/functions/writeCustomFunction.ts: -------------------------------------------------------------------------------- 1 | import { WriteCustomFunction, models } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with custom functions, visit https://docs.keel.so/functions 4 | export default WriteCustomFunction(async (ctx, inputs) => { 5 | const mod = await models.myDuration.create({ dur: inputs.dur }); 6 | return { 7 | model: mod, 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /integration/testdata/env_vars/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: PERSON_NAME 3 | value: "Pedro" 4 | -------------------------------------------------------------------------------- /integration/testdata/events_basic/functions/createPersonFn.ts: -------------------------------------------------------------------------------- 1 | import { CreatePersonFn, CreatePersonFnHooks } from "@teamkeel/sdk"; 2 | 3 | const hooks: CreatePersonFnHooks = { 4 | afterWrite: async (ctx, inputs, person) => { 5 | if (inputs.name === "") { 6 | throw new Error("error occurred"); 7 | } 8 | }, 9 | }; 10 | 11 | export default CreatePersonFn(hooks); 12 | -------------------------------------------------------------------------------- /integration/testdata/events_basic/functions/updateTrackers.ts: -------------------------------------------------------------------------------- 1 | import { models, UpdateTrackers } from "@teamkeel/sdk"; 2 | 3 | export default UpdateTrackers(async (ctx, inputs) => { 4 | const trackers = await models.tracker.findMany(); 5 | for (const t of trackers) { 6 | await models.tracker.update({ id: t.id }, { views: t.views + 1 }); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/events_basic/jobs/createRandomPersons.ts: -------------------------------------------------------------------------------- 1 | import { models, CreateRandomPersons } from "@teamkeel/sdk"; 2 | 3 | export default CreateRandomPersons(async (ctx, inputs) => { 4 | await models.person.create({ name: "Keelson", email: "keelson@keel.xyz" }); 5 | await models.person.create({ name: "Weaveton", email: "weaveton@keel.xyz" }); 6 | 7 | if (inputs.raiseException) { 8 | throw new Error("error occurred"); 9 | } 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/events_basic/subscribers/verifyEmail.ts: -------------------------------------------------------------------------------- 1 | import { models, VerifyEmail, SubscriberContextAPI } from "@teamkeel/sdk"; 2 | 3 | export default VerifyEmail(async (ctx: SubscriberContextAPI, event) => { 4 | await models.person.update( 5 | { id: event.target.data.id }, 6 | { verifiedEmail: true } 7 | ); 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/files/functions/getFileEmptyHooks.ts: -------------------------------------------------------------------------------- 1 | import { GetFileEmptyHooks, GetFileEmptyHooksHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: GetFileEmptyHooksHooks = {}; 5 | 6 | export default GetFileEmptyHooks(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/files/functions/listFilesEmptyHooks.ts: -------------------------------------------------------------------------------- 1 | import { ListFilesEmptyHooks, ListFilesEmptyHooksHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: ListFilesEmptyHooksHooks = {}; 5 | 6 | export default ListFilesEmptyHooks(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/files/functions/presignedUrl.ts: -------------------------------------------------------------------------------- 1 | import { PresignedUrl } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with custom functions, visit https://docs.keel.so/functions 4 | export default PresignedUrl(async (ctx, inputs) => { 5 | const file = await inputs.file.store(); 6 | 7 | const url = await file.getPresignedUrl(); 8 | 9 | return url; 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/files/functions/updateFileEmptyHooks.ts: -------------------------------------------------------------------------------- 1 | import { UpdateFileEmptyHooks } from "@teamkeel/sdk"; 2 | 3 | export default UpdateFileEmptyHooks({}); 4 | -------------------------------------------------------------------------------- /integration/testdata/flows/flows/errorInFlow.ts: -------------------------------------------------------------------------------- 1 | import { ErrorInFlow } from "@teamkeel/sdk"; 2 | 3 | export default ErrorInFlow({}, async (ctx) => { 4 | throw new Error("Error in flow"); 5 | }); 6 | -------------------------------------------------------------------------------- /integration/testdata/flows/flows/errorInStep.ts: -------------------------------------------------------------------------------- 1 | import { ErrorInStep } from "@teamkeel/sdk"; 2 | 3 | export default ErrorInStep({}, async (ctx) => { 4 | await ctx.step("erroring step", { maxRetries: 3 }, async () => { 5 | throw new Error("Error in step"); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/flows/flows/scalarStep.ts: -------------------------------------------------------------------------------- 1 | import { ScalarStep, models } from "@teamkeel/sdk"; 2 | 3 | export default ScalarStep({}, async (ctx) => { 4 | await ctx.step("scalar step", async () => { 5 | return 10; 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/flows/flows/singleStep.ts: -------------------------------------------------------------------------------- 1 | import { SingleStep, models } from "@teamkeel/sdk"; 2 | 3 | export default SingleStep({}, async (ctx) => { 4 | await ctx.step("insert thing", async () => { 5 | return { number: 10 }; 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/flows/flows/stepless.ts: -------------------------------------------------------------------------------- 1 | import { Stepless, models } from "@teamkeel/sdk"; 2 | 3 | export default Stepless({}, async () => { 4 | const thing = await models.thing.create({ 5 | name: "Keelson", 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/flows/flows/timeoutStep.ts: -------------------------------------------------------------------------------- 1 | import { TimeoutStep } from "@teamkeel/sdk"; 2 | 3 | export default TimeoutStep({}, async (ctx) => { 4 | await ctx.step("timeout step", { timeoutInMs: 1 }, async () => { 5 | await new Promise((resolve) => setTimeout(resolve, 100)); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/flows/flows/userFlow.ts: -------------------------------------------------------------------------------- 1 | import { UserFlow } from "@teamkeel/sdk"; 2 | 3 | export default UserFlow({}, async (ctx) => {}); 4 | -------------------------------------------------------------------------------- /integration/testdata/functions_auth_hooks/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | hooks: [afterAuthentication, afterIdentityCreated] 3 | 4 | environment: 5 | - name: "TEST" 6 | value: "test" 7 | 8 | secrets: 9 | - name: API_KEY 10 | -------------------------------------------------------------------------------- /integration/testdata/functions_auth_hooks/schema.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | name Text 4 | identity Identity @unique 5 | loginCount Number @default(0) 6 | } 7 | } -------------------------------------------------------------------------------- /integration/testdata/functions_custom/functions/bulkPersonUpload.ts: -------------------------------------------------------------------------------- 1 | import { models, permissions, BulkPersonUpload } from "@teamkeel/sdk"; 2 | 3 | export default BulkPersonUpload(async (_, value) => { 4 | permissions.allow(); 5 | 6 | return { 7 | people: await Promise.all(value.people.map((p) => models.person.create(p))), 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /integration/testdata/functions_custom/functions/countName.ts: -------------------------------------------------------------------------------- 1 | import { models, permissions, CountName } from "@teamkeel/sdk"; 2 | 3 | export default CountName(async (_, inputs) => { 4 | permissions.allow(); 5 | 6 | const persons = await models.person.findMany({ 7 | where: { 8 | name: { equals: inputs.name }, 9 | }, 10 | }); 11 | 12 | return { 13 | count: persons.length, 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /integration/testdata/functions_custom/functions/customPersonSearch.ts: -------------------------------------------------------------------------------- 1 | import { models, permissions, CustomPersonSearch } from "@teamkeel/sdk"; 2 | 3 | export default CustomPersonSearch(async (_, { params }) => { 4 | permissions.allow(); 5 | 6 | const { names } = params; 7 | const people = await models.person.findMany({ 8 | where: { 9 | name: { oneOf: names }, 10 | }, 11 | }); 12 | 13 | return { 14 | people, 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /integration/testdata/functions_custom/functions/customSearch.ts: -------------------------------------------------------------------------------- 1 | import { models, permissions, CustomSearch } from "@teamkeel/sdk"; 2 | 3 | export default CustomSearch(async (_, value) => { 4 | permissions.allow(); 5 | 6 | return value; 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/functions_custom/functions/noInputs.ts: -------------------------------------------------------------------------------- 1 | import { models, permissions, NoInputs } from "@teamkeel/sdk"; 2 | 3 | export default NoInputs(async (ctx) => { 4 | permissions.allow(); 5 | return { success: true }; 6 | }); 7 | -------------------------------------------------------------------------------- /integration/testdata/functions_custom/functions/readComplex.ts: -------------------------------------------------------------------------------- 1 | import { ReadComplex, permissions } from "@teamkeel/sdk"; 2 | 3 | export default ReadComplex(async (ctx, inputs) => { 4 | permissions.allow(); 5 | return inputs; 6 | }); 7 | -------------------------------------------------------------------------------- /integration/testdata/functions_errors/functions/badRequest.ts: -------------------------------------------------------------------------------- 1 | import { BadRequest, errors } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with custom functions, visit https://docs.keel.so/functions 4 | export default BadRequest(async (ctx, inputs) => { 5 | throw new errors.BadRequest("invalid inputs"); 6 | 7 | return; 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/functions_errors/functions/hookNotFound.ts: -------------------------------------------------------------------------------- 1 | import { errors, HookNotFound, HookNotFoundHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: HookNotFoundHooks = { 5 | beforeQuery(ctx, inputs, query) { 6 | throw new errors.NotFound(); 7 | return query; 8 | }, 9 | }; 10 | 11 | export default HookNotFound(hooks); 12 | -------------------------------------------------------------------------------- /integration/testdata/functions_errors/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | bar Text 4 | } 5 | 6 | actions { 7 | get hookNotFound(id) @function 8 | get hookNotFoundCustomMessage(id) @function 9 | read badRequest(id) returns (Any) 10 | } 11 | 12 | @permission( 13 | actions: [get], 14 | expression: true 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/createAuthorAndBooks.ts: -------------------------------------------------------------------------------- 1 | import { CreateAuthorAndBooks } from "@teamkeel/sdk"; 2 | 3 | export default CreateAuthorAndBooks({ 4 | beforeWrite(ctx, inputs, values) { 5 | return { 6 | ...values, 7 | books: inputs.books.map((b) => { 8 | return { 9 | ...b, 10 | published: true, 11 | }; 12 | }), 13 | }; 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/createBookAfterWrite.ts: -------------------------------------------------------------------------------- 1 | import { CreateBookAfterWrite, models } from "@teamkeel/sdk"; 2 | 3 | export default CreateBookAfterWrite({ 4 | async afterWrite(ctx, inputs, data) { 5 | await models.review.create({ 6 | bookId: data.id, 7 | review: inputs.review, 8 | }); 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/createBookAfterWriteErrorRollback.ts: -------------------------------------------------------------------------------- 1 | import { CreateBookAfterWriteErrorRollback } from "@teamkeel/sdk"; 2 | 3 | export default CreateBookAfterWriteErrorRollback({ 4 | afterWrite(ctx, inputs, data) { 5 | if (data.title.toLowerCase() == "lady chatterley's lover") { 6 | // Throwing an error should cause the transaction to be rolled back 7 | throw new Error("this book is banned"); 8 | } 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/createBookBeforeWriteSync.ts: -------------------------------------------------------------------------------- 1 | import { CreateBookBeforeWriteSync } from "@teamkeel/sdk"; 2 | 3 | export default CreateBookBeforeWriteSync({ 4 | // Note: this is testing a sync hook so very important there is no "async" keyword before the function 5 | beforeWrite(ctx, inputs, values) { 6 | return { 7 | ...values, 8 | title: values.title.toUpperCase(), 9 | }; 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/createBookBeforeWriteWithCover.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreateBookBeforeWriteWithCover, 3 | CreateBookBeforeWriteWithCoverHooks, 4 | } from "@teamkeel/sdk"; 5 | 6 | export default CreateBookBeforeWriteWithCover({ 7 | async beforeWrite(ctx, inputs, values) { 8 | return { 9 | ...values, 10 | title: values.title.toUpperCase(), 11 | }; 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/deleteBookAfterWrite.ts: -------------------------------------------------------------------------------- 1 | import { DeleteBookAfterWrite, models } from "@teamkeel/sdk"; 2 | 3 | export default DeleteBookAfterWrite({ 4 | async afterWrite(ctx, inputs, data) { 5 | await models.deletedBook.create({ 6 | bookId: data.id, 7 | title: `${data.title} (${inputs.reason})`, 8 | deletedAt: ctx.now(), 9 | }); 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/deleteBookBeforeQuery.ts: -------------------------------------------------------------------------------- 1 | import { DeleteBookBeforeQuery } from "@teamkeel/sdk"; 2 | 3 | export default DeleteBookBeforeQuery({ 4 | async beforeQuery(ctx, inputs, query) { 5 | if (!inputs.allowPublished) { 6 | return query.where({ 7 | published: false, 8 | }); 9 | } 10 | 11 | return query; 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/deleteBookBeforeQueryReturnRecord.ts: -------------------------------------------------------------------------------- 1 | import { DeleteBookBeforeQueryReturnRecord } from "@teamkeel/sdk"; 2 | 3 | export default DeleteBookBeforeQueryReturnRecord({ 4 | async beforeQuery(ctx, inputs, query) { 5 | const record = await query.findOne(); 6 | return record; 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/deleteBookNoInputs.ts: -------------------------------------------------------------------------------- 1 | import { DeleteBookNoInputs, DeleteBookNoInputsHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: DeleteBookNoInputsHooks = {}; 5 | 6 | export default DeleteBookNoInputs({ 7 | beforeQuery(ctx, query) { 8 | return query.where({ 9 | title: "The Farseer 2", 10 | }); 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/listBooksAfterQuery.ts: -------------------------------------------------------------------------------- 1 | import { ListBooksAfterQuery } from "@teamkeel/sdk"; 2 | 3 | export default ListBooksAfterQuery({ 4 | afterQuery(ctx, inputs, records) { 5 | return records.map((r) => { 6 | return { 7 | ...r, 8 | title: r.title.toUpperCase(), 9 | }; 10 | }); 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/listBooksAfterQueryPermissions.ts: -------------------------------------------------------------------------------- 1 | import { ListBooksAfterQueryPermissions, permissions } from "@teamkeel/sdk"; 2 | 3 | export default ListBooksAfterQueryPermissions({ 4 | afterQuery(ctx, inputs, records) { 5 | for (const r of records) { 6 | if (inputs.where.onlyPublished && !r.published) { 7 | permissions.deny(); 8 | } 9 | } 10 | 11 | return records; 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/listBooksBeforeQuery.ts: -------------------------------------------------------------------------------- 1 | import { ListBooksBeforeQuery } from "@teamkeel/sdk"; 2 | 3 | // This function is testing that the beforeQuery hook of a 4 | // list function can return a mutated version of the provided QueryBuilder 5 | export default ListBooksBeforeQuery({ 6 | beforeQuery(ctx, inputs, query) { 7 | return query.where({ 8 | title: { 9 | endsWith: "Magic", 10 | }, 11 | }); 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/updateBookBeforeQuery.ts: -------------------------------------------------------------------------------- 1 | import { UpdateBookBeforeQuery } from "@teamkeel/sdk"; 2 | 3 | export default UpdateBookBeforeQuery({ 4 | beforeQuery(ctx, inputs, query) { 5 | query = query.where({ 6 | published: true, 7 | }); 8 | 9 | if (inputs.where.returnRecord) { 10 | return query.findOne(); 11 | } 12 | 13 | return query; 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /integration/testdata/functions_hooks/functions/updateBookBeforeWrite.ts: -------------------------------------------------------------------------------- 1 | import { UpdateBookBeforeWrite, permissions } from "@teamkeel/sdk"; 2 | 3 | export default UpdateBookBeforeWrite({ 4 | async beforeWrite(ctx, inputs, values, record) { 5 | if (values.title.toLowerCase().includes("how to build a bomb")) { 6 | permissions.deny(); 7 | } 8 | 9 | return { 10 | ...values, 11 | published: !record.published, 12 | }; 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /integration/testdata/functions_http/functions/withForm.ts: -------------------------------------------------------------------------------- 1 | import { WithForm, permissions } from "@teamkeel/sdk"; 2 | 3 | export default WithForm(async (ctx, inputs) => { 4 | permissions.allow(); 5 | 6 | return inputs; 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/functions_http/functions/withHeaders.ts: -------------------------------------------------------------------------------- 1 | import { WithHeaders, permissions } from "@teamkeel/sdk"; 2 | 3 | export default WithHeaders(async (ctx, inputs) => { 4 | permissions.allow(); 5 | 6 | const value = ctx.headers.get("X-MyRequestHeader"); 7 | ctx.response.headers.set("X-MyResponseHeader", value || ""); 8 | 9 | return {}; 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/functions_http/functions/withQueryParams.ts: -------------------------------------------------------------------------------- 1 | import { WithQueryParams, permissions } from "@teamkeel/sdk"; 2 | 3 | export default WithQueryParams(async (ctx, inputs) => { 4 | permissions.allow(); 5 | 6 | return inputs; 7 | }); 8 | -------------------------------------------------------------------------------- /integration/testdata/functions_http/functions/withStatus.ts: -------------------------------------------------------------------------------- 1 | import { WithStatus, permissions } from "@teamkeel/sdk"; 2 | export default WithStatus(async (ctx, inputs) => { 3 | permissions.allow(); 4 | 5 | const { response } = ctx; 6 | response.headers.set("Location", "https://some.url"); 7 | response.status = 301; 8 | 9 | return null; 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/functions_http/schema.keel: -------------------------------------------------------------------------------- 1 | model TestModel { 2 | actions { 3 | read withQueryParams(Any) returns (Any) 4 | read withForm(Any) returns (Any) 5 | read withHeaders(Any) returns (Any) 6 | read withStatus(Any) returns (Any) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /integration/testdata/functions_list_query_inputs/functions/listOptionalFieldsWithOptionalInputs.ts: -------------------------------------------------------------------------------- 1 | import { ListOptionalFieldsWithOptionalInputs } from "@teamkeel/sdk"; 2 | 3 | export default ListOptionalFieldsWithOptionalInputs(); 4 | -------------------------------------------------------------------------------- /integration/testdata/functions_list_query_inputs/functions/listOptionalFieldsWithRequiredInputs.ts: -------------------------------------------------------------------------------- 1 | import { ListOptionalFieldsWithRequiredInputs } from "@teamkeel/sdk"; 2 | 3 | export default ListOptionalFieldsWithRequiredInputs(); 4 | -------------------------------------------------------------------------------- /integration/testdata/functions_list_query_inputs/functions/listOptionalInputs.ts: -------------------------------------------------------------------------------- 1 | import { ListOptionalInputs } from "@teamkeel/sdk"; 2 | 3 | export default ListOptionalInputs(); 4 | -------------------------------------------------------------------------------- /integration/testdata/functions_list_query_inputs/functions/listRequiredInputs.ts: -------------------------------------------------------------------------------- 1 | import { ListRequiredInputs } from "@teamkeel/sdk"; 2 | 3 | export default ListRequiredInputs(); 4 | -------------------------------------------------------------------------------- /integration/testdata/functions_permissions/functions/createAdmission.ts: -------------------------------------------------------------------------------- 1 | import { models, CreateAdmission } from "@teamkeel/sdk"; 2 | 3 | export default CreateAdmission({ 4 | async beforeWrite(ctx, inputs, values) { 5 | const audience = await models.audience.findOne({ 6 | identityId: ctx.identity!.id, 7 | }); 8 | 9 | return { 10 | ...values, 11 | audienceId: audience!.id, 12 | }; 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /integration/testdata/functions_permissions/functions/createUser.ts: -------------------------------------------------------------------------------- 1 | import { CreateUser } from "@teamkeel/sdk"; 2 | 3 | export default CreateUser({ 4 | beforeWrite(ctx, inputs, values) { 5 | return { 6 | ...values, 7 | identityId: ctx.identity!.id, 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/functions_permissions/functions/getUser.ts: -------------------------------------------------------------------------------- 1 | import { GetUser } from "@teamkeel/sdk"; 2 | 3 | export default GetUser({}); 4 | -------------------------------------------------------------------------------- /integration/testdata/functions_permissions/functions/listUsersByOrganisation.ts: -------------------------------------------------------------------------------- 1 | import { ListUsersByOrganisation } from "@teamkeel/sdk"; 2 | 3 | export default ListUsersByOrganisation({}); 4 | -------------------------------------------------------------------------------- /integration/testdata/functions_use_database/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | } 5 | 6 | actions { 7 | get getPostButOnlyIfItsAfter2020(id) { 8 | @permission(expression: true) 9 | @function 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /integration/testdata/headers/functions/createPersonUsingHook.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CreatePersonUsingHook, 3 | CreatePersonUsingHookHooks, 4 | } from "@teamkeel/sdk"; 5 | 6 | const hooks: CreatePersonUsingHookHooks = {}; 7 | 8 | export default CreatePersonUsingHook({ 9 | beforeWrite: async (ctx, _) => { 10 | return { 11 | name: ctx.headers.get("Person-Name")!, 12 | }; 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /integration/testdata/headers/functions/writePersonUsingCustomFunc.ts: -------------------------------------------------------------------------------- 1 | import { WritePersonUsingCustomFunc, models } from "@teamkeel/sdk"; 2 | 3 | export default WritePersonUsingCustomFunc(async (ctx, _) => { 4 | return await models.person.create({ name: ctx.headers.get("Person-Name")! }); 5 | }); 6 | -------------------------------------------------------------------------------- /integration/testdata/identity_and_authentication/functions/readPostsByTeam.ts: -------------------------------------------------------------------------------- 1 | import { ReadPostsByTeam, models } from "@teamkeel/sdk"; 2 | 3 | export default ReadPostsByTeam(async (_, inputs) => { 4 | const posts = await models.post 5 | .where({ 6 | identity: { 7 | teamId: { equals: inputs.team }, 8 | }, 9 | }) 10 | .findMany(); 11 | 12 | return posts; 13 | }); 14 | -------------------------------------------------------------------------------- /integration/testdata/identity_and_authentication/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | claims: 3 | - key: team 4 | field: teamId 5 | -------------------------------------------------------------------------------- /integration/testdata/identity_claims/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | claims: 3 | - key: teamId 4 | field: teamId 5 | unique: true 6 | - key: my_key_1 7 | field: myField1 8 | unique: false 9 | - key: my_key_2 10 | field: myField2 11 | -------------------------------------------------------------------------------- /integration/testdata/identity_claims/schema.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | 3 | } -------------------------------------------------------------------------------- /integration/testdata/jobs_basic/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: GOLD_STAR 3 | value: "20" 4 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJob.ts: -------------------------------------------------------------------------------- 1 | import { ManualJob, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJob(async (ctx, inputs) => { 4 | const track = await models.trackJob.update(inputs, { didJobRun: true }); 5 | if (track == null) { 6 | throw new Error("expected row"); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobDeniedInCode.ts: -------------------------------------------------------------------------------- 1 | import { ManualJobDeniedInCode, models, permissions } from "@teamkeel/sdk"; 2 | 3 | export default ManualJobDeniedInCode(async (ctx, inputs) => { 4 | await models.trackJob.update({ id: inputs.id }, { didJobRun: true }); 5 | 6 | if (inputs.denyIt) { 7 | permissions.deny(); 8 | } else { 9 | permissions.allow(); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobEnvExpression.ts: -------------------------------------------------------------------------------- 1 | import { ManualJobEnvExpression, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJobEnvExpression(async (ctx, inputs) => { 4 | const track = await models.trackJob.update(inputs, { didJobRun: true }); 5 | if (track == null) { 6 | throw new Error("expected row"); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobEnvExpression2.ts: -------------------------------------------------------------------------------- 1 | import { ManualJobEnvExpression2, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJobEnvExpression2(async (ctx, inputs) => { 4 | const track = await models.trackJob.update(inputs, { didJobRun: true }); 5 | if (track == null) { 6 | throw new Error("expected row"); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobIsAuthenticatedExpression.ts: -------------------------------------------------------------------------------- 1 | import { ManualJobIsAuthenticatedExpression, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJobIsAuthenticatedExpression(async (ctx, inputs) => { 4 | const track = await models.trackJob.update(inputs, { didJobRun: true }); 5 | if (track == null) { 6 | throw new Error("expected row"); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobMultiPermission.ts: -------------------------------------------------------------------------------- 1 | import { ManualJobMultiPermission, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJobMultiPermission(async (ctx, inputs) => { 4 | const track = await models.trackJob.update(inputs, { didJobRun: true }); 5 | if (track == null) { 6 | throw new Error("expected row"); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobMultiRoles.ts: -------------------------------------------------------------------------------- 1 | import { ManualJobMultiRoles, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJobMultiRoles(async (ctx, inputs) => { 4 | const track = await models.trackJob.update(inputs, { didJobRun: true }); 5 | if (track == null) { 6 | throw new Error("expected row"); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobTrueExpression.ts: -------------------------------------------------------------------------------- 1 | import { ManualJobTrueExpression, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJobTrueExpression(async (ctx, inputs) => { 4 | const track = await models.trackJob.update(inputs, { didJobRun: true }); 5 | if (track == null) { 6 | throw new Error("expected row"); 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/manualJobWithException.ts: -------------------------------------------------------------------------------- 1 | import { ManualJob, models } from "@teamkeel/sdk"; 2 | 3 | export default ManualJob(async (ctx, inputs) => { 4 | await models.trackJob.update(inputs, { didJobRun: true }); 5 | throw new Error("something bad has happened!"); 6 | }); 7 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/jobs/scheduledWithoutPermissions.ts: -------------------------------------------------------------------------------- 1 | import { ScheduledWithoutPermissions, models } from "@teamkeel/sdk"; 2 | 3 | export default ScheduledWithoutPermissions(async (ctx) => { 4 | const track = await models.trackJob.update( 5 | { id: "12345" }, 6 | { didJobRun: true } 7 | ); 8 | if (track == null) { 9 | throw new Error("expected row"); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /integration/testdata/jobs_permissions/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: REGION 3 | value: "za-test" 4 | -------------------------------------------------------------------------------- /integration/testdata/operation_list_empty_array/schema.keel: -------------------------------------------------------------------------------- 1 | model Project { 2 | fields { 3 | name Text 4 | } 5 | } 6 | 7 | model Todo { 8 | fields { 9 | label Text 10 | project Project? 11 | owner Identity 12 | } 13 | 14 | actions { 15 | list listTodo(project.id?) { 16 | @permission(expression: todo.owner == ctx.identity) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /integration/testdata/operation_list_pagination/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | } 5 | 6 | actions { 7 | list listPosts() 8 | } 9 | 10 | @permission( 11 | expression: true, 12 | actions: [list] 13 | ) 14 | } 15 | 16 | api Web { 17 | models { 18 | Post 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /integration/testdata/operation_multiple_list_ops/schema.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | fields { 3 | something Text 4 | } 5 | 6 | actions { 7 | list listOne() 8 | list listTwo() 9 | } 10 | 11 | @permission( 12 | expression: true, 13 | actions: [create, get, list, update, delete] 14 | ) 15 | } 16 | 17 | api Test { 18 | models { 19 | Thing 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /integration/testdata/permissions_custom_function/functions/createPost.ts: -------------------------------------------------------------------------------- 1 | import { CreatePost } from "@teamkeel/sdk"; 2 | 3 | export default CreatePost({ 4 | beforeWrite: async (ctx, inputs) => { 5 | return { 6 | title: inputs.title, 7 | businessId: inputs.business.id, 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/permissions_custom_function/functions/createPostWithRole.ts: -------------------------------------------------------------------------------- 1 | import { CreatePostWithRole } from "@teamkeel/sdk"; 2 | 3 | export default CreatePostWithRole({ 4 | beforeWrite: async (ctx, inputs) => { 5 | return { 6 | title: inputs.title, 7 | businessId: inputs.business.id, 8 | }; 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /integration/testdata/permissions_custom_function/functions/deletePost.ts: -------------------------------------------------------------------------------- 1 | import { DeletePost } from "@teamkeel/sdk"; 2 | 3 | export default DeletePost(); 4 | -------------------------------------------------------------------------------- /integration/testdata/permissions_custom_function/functions/getPost.ts: -------------------------------------------------------------------------------- 1 | import { GetPost } from "@teamkeel/sdk"; 2 | 3 | export default GetPost(); 4 | -------------------------------------------------------------------------------- /integration/testdata/permissions_custom_function/functions/getSecretPost.ts: -------------------------------------------------------------------------------- 1 | import { GetSecretPost } from "@teamkeel/sdk"; 2 | 3 | export default GetSecretPost(); 4 | -------------------------------------------------------------------------------- /integration/testdata/permissions_custom_function/functions/listPosts.ts: -------------------------------------------------------------------------------- 1 | import { ListPosts } from "@teamkeel/sdk"; 2 | 3 | export default ListPosts(); 4 | -------------------------------------------------------------------------------- /integration/testdata/permissions_custom_function/functions/updatePost.ts: -------------------------------------------------------------------------------- 1 | import { UpdatePost } from "@teamkeel/sdk"; 2 | 3 | export default UpdatePost(); 4 | -------------------------------------------------------------------------------- /integration/testdata/permissions_user_role_model/functions/getTaskFn.ts: -------------------------------------------------------------------------------- 1 | import { GetTaskFn, GetTaskFnHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, 4 | // visit https://docs.keel.so/functions 5 | const hooks: GetTaskFnHooks = {}; 6 | 7 | export default GetTaskFn(hooks); 8 | -------------------------------------------------------------------------------- /integration/testdata/real_world_user_permissions/functions/createAccountFn.ts: -------------------------------------------------------------------------------- 1 | import { CreateAccountFn, CreateAccountFnHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: CreateAccountFnHooks = {}; 5 | 6 | export default CreateAccountFn(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/real_world_user_permissions/functions/listAccountFn.ts: -------------------------------------------------------------------------------- 1 | import { ListAccountFn, ListAccountFnHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: ListAccountFnHooks = {}; 5 | 6 | export default ListAccountFn(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/routes/databaseHandler.ts: -------------------------------------------------------------------------------- 1 | import { RouteFunction, models } from "@teamkeel/sdk"; 2 | 3 | const handler: RouteFunction = async (request, ctx) => { 4 | const body = JSON.parse(request.body); 5 | 6 | const person = await models.person.create({ 7 | name: body.name, 8 | }); 9 | 10 | return { 11 | body: JSON.stringify({ 12 | id: person.id, 13 | }), 14 | }; 15 | }; 16 | 17 | export default handler; 18 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/routes/getHandler.ts: -------------------------------------------------------------------------------- 1 | import { RouteFunction } from "@teamkeel/sdk"; 2 | 3 | const handler: RouteFunction = async (request, ctx) => { 4 | const q = new URLSearchParams(request.query); 5 | 6 | return { 7 | body: JSON.stringify({ 8 | foo: q.get("foo"), 9 | }), 10 | }; 11 | }; 12 | 13 | export default handler; 14 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/routes/headersHandler.ts: -------------------------------------------------------------------------------- 1 | import { RouteFunction } from "@teamkeel/sdk"; 2 | 3 | const handler: RouteFunction = async (request, ctx) => { 4 | const v = request.headers.get(`X-My-Request-Header`); 5 | 6 | return { 7 | body: "", 8 | headers: { 9 | [`X-My-Response-Header`]: `${v}bar`, 10 | }, 11 | }; 12 | }; 13 | 14 | export default handler; 15 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/routes/pathParamHandler.ts: -------------------------------------------------------------------------------- 1 | import { RouteFunction } from "@teamkeel/sdk"; 2 | 3 | const handler: RouteFunction = async (request, ctx) => { 4 | return { 5 | body: JSON.stringify({ 6 | foo: request.params["foo"] || "", 7 | }), 8 | }; 9 | }; 10 | 11 | export default handler; 12 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/routes/postHandler.ts: -------------------------------------------------------------------------------- 1 | import { RouteFunction } from "@teamkeel/sdk"; 2 | 3 | const handler: RouteFunction = async (request, ctx) => { 4 | const body = JSON.parse(request.body); 5 | 6 | return { 7 | body: JSON.stringify({ 8 | foo: body.foo, 9 | fizz: "buzz", 10 | }), 11 | }; 12 | }; 13 | 14 | export default handler; 15 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/routes/rawBodyHandler.ts: -------------------------------------------------------------------------------- 1 | import { RouteFunction } from "@teamkeel/sdk"; 2 | import { createHash } from "node:crypto"; 3 | 4 | const handler: RouteFunction = async (request, ctx) => { 5 | const sha1 = createHash("sha1").update(request.body).digest("hex"); 6 | 7 | return { 8 | body: JSON.stringify({ 9 | sha1, 10 | }), 11 | }; 12 | }; 13 | 14 | export default handler; 15 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/routes/statusHandler.ts: -------------------------------------------------------------------------------- 1 | import { RouteFunction } from "@teamkeel/sdk"; 2 | 3 | const handler: RouteFunction = async (request, ctx) => { 4 | return { 5 | body: "", 6 | statusCode: 204, 7 | }; 8 | }; 9 | 10 | export default handler; 11 | -------------------------------------------------------------------------------- /integration/testdata/route_functions/schema.keel: -------------------------------------------------------------------------------- 1 | routes { 2 | get("/get/route", getHandler) 3 | get("/path/param/route/:foo", pathParamHandler) 4 | post("/post/route", postHandler) 5 | post("/raw/body/route", rawBodyHandler) 6 | post("/headers/route", headersHandler) 7 | post("/database/route", databaseHandler) 8 | put("/status/route", statusHandler) 9 | } 10 | 11 | model Person { 12 | fields { 13 | name Text 14 | } 15 | } -------------------------------------------------------------------------------- /integration/testdata/sequence_attribute/functions/createInvoiceFunc.ts: -------------------------------------------------------------------------------- 1 | import { CreateInvoiceFunc, CreateInvoiceFuncHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: CreateInvoiceFuncHooks = {}; 5 | 6 | export default CreateInvoiceFunc(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/sequence_attribute/functions/deleteInvoiceFunc.ts: -------------------------------------------------------------------------------- 1 | import { DeleteInvoiceFunc, DeleteInvoiceFuncHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: DeleteInvoiceFuncHooks = {}; 5 | 6 | export default DeleteInvoiceFunc(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/sequence_attribute/functions/getInvoiceFunc.ts: -------------------------------------------------------------------------------- 1 | import { GetInvoiceFunc, GetInvoiceFuncHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: GetInvoiceFuncHooks = {}; 5 | 6 | export default GetInvoiceFunc(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/sequence_attribute/functions/updateInvoiceFunc.ts: -------------------------------------------------------------------------------- 1 | import { UpdateInvoiceFunc, UpdateInvoiceFuncHooks } from "@teamkeel/sdk"; 2 | 3 | // To learn more about what you can do with hooks, visit https://docs.keel.so/functions 4 | const hooks: UpdateInvoiceFuncHooks = {}; 5 | 6 | export default UpdateInvoiceFunc(hooks); 7 | -------------------------------------------------------------------------------- /integration/testdata/simple_database_test/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /integration/testdata/sortable_list_pagination/schema.keel: -------------------------------------------------------------------------------- 1 | model Item { 2 | fields { 3 | name Text 4 | letter Text 5 | value Number 6 | } 7 | 8 | actions { 9 | list listItems() { 10 | @sortable(letter, value) 11 | @permission(expression: true) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /integration/testdata/subscribers_basic/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: MY_VAR 3 | value: "20" 4 | -------------------------------------------------------------------------------- /integration/testdata/subscribers_basic/schema.keel: -------------------------------------------------------------------------------- 1 | model Member { 2 | fields { 3 | name Text 4 | email Text 5 | verified Boolean @default(false) 6 | } 7 | 8 | @on([create, update], verifyEmail) 9 | @on([create, update], subscriberWithException) 10 | @on([create, update], subscriberEnvvars) 11 | } 12 | 13 | model TrackSubscriber { 14 | fields { 15 | didSubscriberRun Boolean @default(false) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /integration/testdata/unique_constraint/functions/createProductFn.ts: -------------------------------------------------------------------------------- 1 | import { CreateProductFn } from "@teamkeel/sdk"; 2 | 3 | export default CreateProductFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/unique_constraint/functions/updateProductFn.ts: -------------------------------------------------------------------------------- 1 | import { UpdateProductFn } from "@teamkeel/sdk"; 2 | 3 | export default UpdateProductFn(); 4 | -------------------------------------------------------------------------------- /integration/testdata/vectors/schema.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | fields { 3 | vector Vector 4 | optionalVector Vector? 5 | } 6 | actions { 7 | create createThing() with (vector) { 8 | @permission(expression: true) 9 | } 10 | 11 | get getThing(id) { 12 | @permission(expression: true) 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teamkeel/keel/874bde74910d5da397c9d628b39724e3708f0c33/main -------------------------------------------------------------------------------- /migrations/computed_functions.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | routine_name 3 | FROM 4 | information_schema.routines 5 | WHERE 6 | routine_type = 'FUNCTION' 7 | AND 8 | routine_schema = 'public' AND routine_name LIKE '%__comp' OR routine_name LIKE '%__exec_comp_fns' OR routine_name LIKE '%__comp_dep' OR routine_name LIKE '%__comp_dep_update'; -------------------------------------------------------------------------------- /migrations/set_identity_id.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION set_identity_id(id VARCHAR) 2 | RETURNS TEXT AS $$ 3 | BEGIN 4 | RETURN set_config('audit.identity_id', id, true); 5 | END 6 | $$ LANGUAGE plpgsql; -------------------------------------------------------------------------------- /migrations/set_trace_id.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION set_trace_id(id VARCHAR) 2 | RETURNS TEXT AS $$ 3 | BEGIN 4 | RETURN set_config('audit.trace_id', id, true); 5 | END 6 | $$ LANGUAGE plpgsql; -------------------------------------------------------------------------------- /migrations/set_updated_at.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE FUNCTION set_updated_at() RETURNS TRIGGER AS $$ 2 | BEGIN 3 | NEW.updated_at = NOW(); 4 | RETURN NEW; 5 | END 6 | $$ LANGUAGE plpgsql; -------------------------------------------------------------------------------- /migrations/testdata/computed_field_unchanged.txt: -------------------------------------------------------------------------------- 1 | model Item { 2 | fields { 3 | price Decimal 4 | quantity Number 5 | total Decimal @computed(item.quantity * item.price) 6 | } 7 | } 8 | 9 | === 10 | 11 | model Item { 12 | fields { 13 | price Decimal 14 | quantity Number 15 | total Decimal @computed(item.quantity * item.price) 16 | } 17 | } 18 | 19 | === 20 | 21 | === 22 | 23 | [] 24 | 25 | -------------------------------------------------------------------------------- /migrations/testdata/field_added_required_and_default.txt: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | } 6 | 7 | === 8 | 9 | model Person { 10 | fields { 11 | name Text 12 | age Number @default(10) 13 | } 14 | } 15 | 16 | === 17 | 18 | ALTER TABLE "person" ADD COLUMN "age" INTEGER NOT NULL DEFAULT 10; 19 | 20 | === 21 | 22 | [ 23 | { "Model": "Person", "Field": "age", "Type": "ADDED" } 24 | ] 25 | -------------------------------------------------------------------------------- /migrations/testdata/field_removed.txt: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | age Number 5 | } 6 | } 7 | 8 | === 9 | 10 | model Person { 11 | fields { 12 | name Text 13 | } 14 | } 15 | 16 | === 17 | 18 | ALTER TABLE "person" DROP COLUMN "age" CASCADE; 19 | 20 | === 21 | 22 | [ 23 | { "Model": "Person", "Field": "age", "Type": "REMOVED" } 24 | ] 25 | -------------------------------------------------------------------------------- /migrations/testdata/model_removed.txt: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | } 6 | 7 | model Animal { 8 | fields { 9 | name Text 10 | } 11 | } 12 | 13 | === 14 | 15 | model Person { 16 | fields { 17 | name Text 18 | } 19 | } 20 | 21 | === 22 | 23 | DROP TABLE "animal" CASCADE; 24 | 25 | === 26 | 27 | [ 28 | { "Model": "Animal", "Field": "", "Type": "REMOVED" } 29 | ] 30 | -------------------------------------------------------------------------------- /migrations/testdata/sequence_field_removed.txt: -------------------------------------------------------------------------------- 1 | model Invoice { 2 | fields { 3 | reference Text @sequence("INV-") 4 | } 5 | } 6 | 7 | === 8 | 9 | model Invoice {} 10 | 11 | === 12 | 13 | ALTER TABLE "invoice" DROP COLUMN "reference__sequence" CASCADE; 14 | 15 | === 16 | 17 | [ 18 | {"Model":"Invoice","Field":"reference","Type":"REMOVED"} 19 | ] 20 | -------------------------------------------------------------------------------- /migrations/triggers.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | event_object_table table_name, 3 | trigger_name trigger_name, 4 | event_manipulation statement_type, 5 | action_statement, 6 | action_timing 7 | FROM 8 | information_schema.triggers 9 | WHERE 10 | trigger_schema = 'public' -------------------------------------------------------------------------------- /node/process_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package node 4 | 5 | import "syscall" 6 | 7 | func (ds *DevelopmentServer) getSysProcAttr() *syscall.SysProcAttr { 8 | return nil 9 | } 10 | 11 | func (ds *DevelopmentServer) kill() error { 12 | // See https://medium.com/@felixge/killing-a-child-process-and-all-of-its-children-in-go-54079af94773 13 | // for more info on this 14 | return ds.cmd.Process.Kill() 15 | } 16 | -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | `packages/` contains all of the JavaScript/TypeScript packages that we publish to the NPM registry. 2 | 3 | You can view the packages that are published at https://www.npmjs.com/org/teamkeel 4 | 5 | Please do not place any other directories here that you do not intend to publish to NPM! 6 | -------------------------------------------------------------------------------- /packages/client-react-query/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.tgz 3 | lib/ 4 | !.env.test 5 | dist/ -------------------------------------------------------------------------------- /packages/client-react-query/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "strict": true, 8 | "jsx": "react", 9 | "lib": ["ESNext", "dom"], 10 | "esModuleInterop": true, 11 | "sourceMap": true 12 | }, 13 | "include": ["src"], 14 | "exclude": ["node_modules", "**/__tests__/*"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/client-react/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.tgz 3 | lib/ 4 | !.env.test 5 | dist/ -------------------------------------------------------------------------------- /packages/client-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./dist", 7 | "strict": true, 8 | "jsx": "react", 9 | "lib": ["ESNext", "dom"], 10 | "esModuleInterop": true, 11 | "sourceMap": true 12 | }, 13 | "include": ["src"], 14 | "exclude": ["node_modules", "**/__tests__/*"] 15 | } 16 | -------------------------------------------------------------------------------- /packages/functions-runtime/.env.test: -------------------------------------------------------------------------------- 1 | KEEL_DB_CONN=postgresql://postgres:postgres@localhost:7654/functions-runtime 2 | KEEL_DB_CONN_TYPE=pg -------------------------------------------------------------------------------- /packages/functions-runtime/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.tgz 3 | dist/ 4 | !.env.test -------------------------------------------------------------------------------- /packages/functions-runtime/README.md: -------------------------------------------------------------------------------- 1 | # `@teamkeel/functions-runtime` 2 | 3 | `@teamkeel/functions-runtime` is an internal package used by `@teamkeel/sdk`. Do not install this package directly. 4 | -------------------------------------------------------------------------------- /packages/functions-runtime/compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: pgvector/pgvector:pg15 4 | restart: always 5 | environment: 6 | - POSTGRES_USER=postgres 7 | - POSTGRES_PASSWORD=postgres 8 | - POSTGRES_DB=functions-runtime 9 | ports: 10 | - "7654:5432" 11 | -------------------------------------------------------------------------------- /packages/functions-runtime/src/flows/testingUtils.ts: -------------------------------------------------------------------------------- 1 | import { createFlowContext, FlowFunction } from "."; 2 | import { FlowConfig } from "."; 3 | 4 | export const testFlowContext = (config?: T) => 5 | createFlowContext("test-run-id", {}, "test-span-id"); 6 | 7 | export const testFlow = ( 8 | config: C, 9 | fn: FlowFunction 10 | ) => { 11 | return { config, fn }; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/functions-runtime/src/tryExecuteFlow.js: -------------------------------------------------------------------------------- 1 | import { withDatabase } from "./database"; 2 | 3 | function tryExecuteFlow(db, cb) { 4 | return withDatabase(db, false, async () => { 5 | return cb(); 6 | }); 7 | } 8 | 9 | export { tryExecuteFlow }; 10 | -------------------------------------------------------------------------------- /packages/functions-runtime/src/type-utils.ts: -------------------------------------------------------------------------------- 1 | import { Duration } from "./Duration"; 2 | 3 | function isPlainObject(obj: unknown): boolean { 4 | return Object.prototype.toString.call(obj) === "[object Object]"; 5 | } 6 | 7 | function isRichType(obj: unknown): boolean { 8 | if (!isPlainObject(obj)) { 9 | return false; 10 | } 11 | 12 | return obj instanceof Duration; 13 | } 14 | 15 | export { isPlainObject, isRichType }; 16 | -------------------------------------------------------------------------------- /packages/functions-runtime/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | 3 | export default defineConfig({ 4 | entry: ["src/index.ts"], 5 | format: ["cjs", "esm"], 6 | dts: true, 7 | splitting: false, 8 | sourcemap: true, 9 | keepNames: true, 10 | clean: true, 11 | target: "node22", 12 | loader: { 13 | ".js": "jsx", 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /packages/functions-runtime/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from "vite"; 2 | 3 | export default ({ mode }) => { 4 | process.env = { ...process.env, ...loadEnv(mode, process.cwd(), "") }; 5 | 6 | return defineConfig({}); 7 | }; 8 | -------------------------------------------------------------------------------- /packages/testing-runtime/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *.tgz 3 | dist/ 4 | -------------------------------------------------------------------------------- /packages/testing-runtime/README.md: -------------------------------------------------------------------------------- 1 | # `@teamkeel/testing-runtime` 2 | 3 | `@teamkeel/testng-runtime` is an internal package used by `@teamkeel/testing`. Do not install this package directly. 4 | -------------------------------------------------------------------------------- /packages/testing-runtime/src/ActionExecutor.mjs: -------------------------------------------------------------------------------- 1 | import { Executor } from "./Executor.mjs"; 2 | 3 | export class ActionExecutor extends Executor { 4 | constructor(props) { 5 | props.apiBaseUrl = process.env.KEEL_TESTING_ACTIONS_API_URL; 6 | props.parseJsonResult = true; 7 | 8 | super(props); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/testing-runtime/src/JobExecutor.mjs: -------------------------------------------------------------------------------- 1 | import { Executor } from "./Executor.mjs"; 2 | 3 | export class JobExecutor extends Executor { 4 | constructor(props) { 5 | props.apiBaseUrl = process.env.KEEL_TESTING_JOBS_URL; 6 | props.parseJsonResult = false; 7 | 8 | super(props); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/testing-runtime/src/SubscriberExecutor.mjs: -------------------------------------------------------------------------------- 1 | import { Executor } from "./Executor.mjs"; 2 | 3 | export class SubscriberExecutor extends Executor { 4 | constructor(props) { 5 | props.apiBaseUrl = process.env.KEEL_TESTING_SUBSCRIBERS_URL; 6 | props.parseJsonResult = false; 7 | 8 | super(props); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/testing-runtime/src/index.mjs: -------------------------------------------------------------------------------- 1 | export { sql } from "kysely"; 2 | export { ActionExecutor } from "./ActionExecutor.mjs"; 3 | export { JobExecutor } from "./JobExecutor.mjs"; 4 | export { SubscriberExecutor } from "./SubscriberExecutor.mjs"; 5 | export { toHaveError } from "./toHaveError.mjs"; 6 | export { toHaveAuthorizationError } from "./toHaveAuthorizationError.mjs"; 7 | export { toHaveAuthenticationError } from "./toHaveAuthenticationError.mjs"; 8 | -------------------------------------------------------------------------------- /packages/testing-runtime/vitest.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | setupFiles: ["./src/vitest-setup"], 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/wasm/.gitignore: -------------------------------------------------------------------------------- 1 | dist/ -------------------------------------------------------------------------------- /proto/model_test.go: -------------------------------------------------------------------------------- 1 | package proto 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestFieldNames(t *testing.T) { 10 | t.Parallel() 11 | require.Equal(t, []string{"Field1", "Field2"}, referenceSchema.GetModels()[0].FieldNames()) 12 | } 13 | -------------------------------------------------------------------------------- /rpc/opentelemetry/proto/collector/README.md: -------------------------------------------------------------------------------- 1 | # OpenTelemetry Collector Proto 2 | 3 | This package describes the OpenTelemetry collector protocol. 4 | 5 | ## Packages 6 | 7 | 1. `common` package contains the common messages shared between different services. 8 | 2. `trace` package contains the Trace Service protos. 9 | 3. `metrics` package contains the Metrics Service protos. 10 | 4. `logs` package contains the Logs Service protos. 11 | -------------------------------------------------------------------------------- /rpc/opentelemetry/proto/collector/logs/v1/logs_service_http.yaml: -------------------------------------------------------------------------------- 1 | # This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the 2 | # OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. 3 | type: google.api.Service 4 | config_version: 3 5 | http: 6 | rules: 7 | - selector: opentelemetry.proto.collector.logs.v1.LogsService.Export 8 | post: /v1/logs 9 | body: "*" -------------------------------------------------------------------------------- /rpc/opentelemetry/proto/collector/metrics/v1/metrics_service_http.yaml: -------------------------------------------------------------------------------- 1 | # This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the 2 | # OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. 3 | type: google.api.Service 4 | config_version: 3 5 | http: 6 | rules: 7 | - selector: opentelemetry.proto.collector.metrics.v1.MetricsService.Export 8 | post: /v1/metrics 9 | body: "*" -------------------------------------------------------------------------------- /rpc/opentelemetry/proto/collector/trace/v1/trace_service_http.yaml: -------------------------------------------------------------------------------- 1 | # This is an API configuration to generate an HTTP/JSON -> gRPC gateway for the 2 | # OpenTelemetry service using github.com/grpc-ecosystem/grpc-gateway. 3 | type: google.api.Service 4 | config_version: 3 5 | http: 6 | rules: 7 | - selector: opentelemetry.proto.collector.trace.v1.TraceService.Export 8 | post: /v1/traces 9 | body: "*" 10 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/any_type.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | _health: Boolean 3 | getAnything(input: Any): Any 4 | } 5 | 6 | scalar Any 7 | 8 | scalar ISO8601 9 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/any_type.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | read getAnything(Any) returns(Any) 4 | } 5 | } 6 | 7 | api Test { 8 | models { 9 | Person 10 | } 11 | } -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/apis.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | actions { 6 | get getPerson(id) 7 | create createPerson() with (name) 8 | } 9 | } 10 | 11 | api Test { 12 | models { 13 | Person 14 | Identity 15 | } 16 | } -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/arrays.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | fields { 3 | texts Text[] 4 | numbers Number[] 5 | enums MyEnum[] 6 | dates Date[] 7 | } 8 | actions { 9 | list things(texts, numbers, enums, dates) 10 | } 11 | } 12 | 13 | enum MyEnum { 14 | One 15 | Two 16 | Three 17 | } 18 | 19 | api Test { 20 | models { 21 | Thing 22 | } 23 | } -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/delete_operation.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | get getPerson(id) 8 | delete deletePerson(id) 9 | } 10 | } 11 | 12 | api Test { 13 | models { 14 | Person 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/enums.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | occupation Occupation 4 | } 5 | 6 | actions { 7 | get getPerson(id) 8 | create createPerson() with (occupation) 9 | } 10 | } 11 | 12 | enum Occupation { 13 | Teacher 14 | Doctor 15 | FIRE_FIGHTER 16 | astronaut 17 | Officer_1 18 | } 19 | 20 | api Test { 21 | models { 22 | Person 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/get_operation.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | get getPerson(id) 4 | } 5 | } 6 | 7 | api Test { 8 | models { 9 | Person 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/identity_relationships.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | user Identity 4 | } 5 | 6 | actions { 7 | get getPerson(id) 8 | } 9 | } 10 | 11 | api Test { 12 | models { 13 | Person 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/list_operation_sortable.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | firstName Text 4 | surname Text 5 | } 6 | 7 | actions { 8 | list listAuthors() { 9 | @sortable(firstName, surname) 10 | } 11 | } 12 | } 13 | 14 | api Test { 15 | models { 16 | Author 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/message_arrays.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | _health: Boolean 3 | readPerson(input: PersonInput!): PersonResponse 4 | } 5 | 6 | input PersonInput { 7 | id: [ID!]! 8 | } 9 | 10 | type PersonResponse { 11 | id: [ID]! 12 | } 13 | 14 | scalar Any 15 | 16 | scalar ISO8601 17 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/message_arrays.keel: -------------------------------------------------------------------------------- 1 | message PersonResponse { 2 | id ID[] 3 | } 4 | 5 | message PersonInput { 6 | id ID[] 7 | } 8 | 9 | model Person { 10 | fields { 11 | name Text 12 | } 13 | 14 | actions { 15 | read readPerson(PersonInput) returns (PersonResponse) 16 | } 17 | } 18 | 19 | api Test { 20 | models { 21 | Person 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/messages_as_input_and_response.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | _health: Boolean 3 | } 4 | 5 | type Mutation { 6 | writePerson(input: TheMessageInput!): TheMessage 7 | } 8 | 9 | input TheMessageInput { 10 | thing: Boolean! 11 | } 12 | 13 | type TheMessage { 14 | thing: Boolean! 15 | } 16 | 17 | scalar Any 18 | 19 | scalar ISO8601 20 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/messages_as_input_and_response.keel: -------------------------------------------------------------------------------- 1 | message TheMessage { 2 | thing Boolean 3 | } 4 | 5 | 6 | model Person { 7 | fields { 8 | name Text 9 | } 10 | 11 | actions { 12 | write writePerson(TheMessage) returns (TheMessage) 13 | } 14 | } 15 | 16 | api Test { 17 | models { 18 | Person 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/no_get_operations.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | _health: Boolean 3 | } 4 | 5 | scalar Any 6 | 7 | scalar ISO8601 8 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/no_get_operations.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | } 3 | 4 | api Test { 5 | models { 6 | Person 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/optional_fields.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | someBoolean Boolean? 4 | someNumber Number? 5 | someText Text? 6 | someMarkdown Markdown? 7 | someDecimal Decimal? 8 | } 9 | 10 | actions { 11 | get getPerson(id) 12 | } 13 | } 14 | 15 | api Test { 16 | models { 17 | Person 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/response_message_with_model_field.keel: -------------------------------------------------------------------------------- 1 | message ClassroomPeople { 2 | teacher Person 3 | substitute Person? 4 | pupils Person[] 5 | } 6 | 7 | model Person { 8 | fields { 9 | name Text 10 | } 11 | 12 | actions { 13 | read inClassroom(classId: ID) returns (ClassroomPeople) 14 | } 15 | } 16 | 17 | api Test { 18 | models { 19 | Person 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /runtime/apis/graphql/testdata/graphql/scalar_fields.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | someBoolean Boolean 4 | someNumber Number 5 | someText Text 6 | someMarkdown Markdown 7 | someDecimal Decimal 8 | someDuration Duration 9 | } 10 | 11 | actions { 12 | get getPerson(id) 13 | } 14 | } 15 | 16 | api Test { 17 | models { 18 | Person 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/arbitrary_function_any.json: -------------------------------------------------------------------------------- 1 | { 2 | "unevaluatedProperties": false, 3 | "anyOf": [ 4 | { "type": "string", "title": "string" }, 5 | { "type": "object", "title": "object" }, 6 | { "type": "array", "title": "array" }, 7 | { "type": "integer", "title": "integer" }, 8 | { "type": "number", "title": "number" }, 9 | { "type": "boolean", "title": "boolean" }, 10 | { "type": "null", "title": "null" } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/arbitrary_function_any.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Any 3 | } 4 | 5 | model Post { 6 | actions { 7 | read testAction(Any) returns (Foo) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/create.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "birthday": { "type": "string", "format": "date" }, 5 | "name": { "type": "string" }, 6 | "nickName": { "type": ["string", "null"] } 7 | }, 8 | "unevaluatedProperties": false, 9 | "required": ["name", "nickName", "birthday"] 10 | } 11 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/create.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | nickName Text? 5 | birthday Date 6 | } 7 | 8 | actions { 9 | create testAction() with (name, nickName, birthday) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/create_array_input.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "tags": { "type": "array", "items": { "type": "string" } }, 5 | "title": { "type": "string" } 6 | }, 7 | "unevaluatedProperties": false, 8 | "required": ["title", "tags"] 9 | } 10 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/create_array_input.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | tags Text[] 5 | } 6 | 7 | actions { 8 | create testAction() with (title, tags) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { "id": { "type": "string" } }, 4 | "unevaluatedProperties": false, 5 | "required": ["id"] 6 | } 7 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/get.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | get testAction(id) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/list_on_related_field.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | company Company 4 | } 5 | 6 | actions { 7 | list testAction(company.name, company.tradingAs) 8 | } 9 | } 10 | 11 | model Company { 12 | fields { 13 | name Text 14 | tradingAs Text? 15 | } 16 | } 17 | 18 | api Test { 19 | models { 20 | Person 21 | Company 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/list_optional_inputs.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | firstName Text 4 | company Company 5 | } 6 | 7 | actions { 8 | list testAction(firstName?, company.name?) 9 | } 10 | } 11 | 12 | model Company { 13 | fields { 14 | name Text 15 | } 16 | } -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/list_sortable.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | firstName Text 4 | surname Text 5 | } 6 | 7 | actions { 8 | list testAction() { 9 | @sortable(surname, firstName) 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/message_arrays.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { "ids": { "type": "array", "items": { "type": "string" } } }, 4 | "unevaluatedProperties": false, 5 | "required": ["ids"] 6 | } 7 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/message_arrays.keel: -------------------------------------------------------------------------------- 1 | message PersonResponse { 2 | ids ID[] 3 | } 4 | 5 | message PersonInput { 6 | ids ID[] 7 | } 8 | 9 | model Person { 10 | fields { 11 | name Text 12 | } 13 | 14 | actions { 15 | read testAction(PersonInput) returns (PersonResponse) 16 | } 17 | } 18 | 19 | api Test { 20 | models { 21 | Person 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/message_enums.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "favouriteSport": { "enum": ["Cricket", "Rugby", "Soccer"] }, 5 | "sports": { 6 | "type": "array", 7 | "items": { "enum": ["Cricket", "Rugby", "Soccer"] } 8 | } 9 | }, 10 | "unevaluatedProperties": false, 11 | "required": ["sports"] 12 | } 13 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/message_model_field.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | person Person 3 | people Person[] 4 | } 5 | 6 | model Person { 7 | actions { 8 | read testAction(Foo) returns(Foo) 9 | } 10 | } 11 | 12 | api Test { 13 | models { 14 | Person 15 | } 16 | } -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/read.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "name": { "type": "string" }, 5 | "optionalName": { "type": "string" } 6 | }, 7 | "unevaluatedProperties": false, 8 | "required": ["name"] 9 | } 10 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/read.keel: -------------------------------------------------------------------------------- 1 | message PersonResponse { 2 | name Text 3 | } 4 | 5 | message PersonInput { 6 | name Text 7 | optionalName Text? 8 | } 9 | 10 | model Person { 11 | fields { 12 | name Text 13 | } 14 | 15 | actions { 16 | read testAction(PersonInput) returns (PersonResponse) 17 | } 18 | } 19 | 20 | api Test { 21 | models { 22 | Person 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/read_empty_input_message.json: -------------------------------------------------------------------------------- 1 | { "type": "object", "unevaluatedProperties": false } 2 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/read_empty_input_message.keel: -------------------------------------------------------------------------------- 1 | message In {} 2 | 3 | model Person { 4 | actions { 5 | read testAction(In) returns (Any) 6 | } 7 | } 8 | 9 | api Test { 10 | models { 11 | Person 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/read_named_inputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "arraySize": { "type": "number" }, 5 | "iterations": { "type": "number" }, 6 | "rollback": { "type": "boolean" } 7 | }, 8 | "unevaluatedProperties": false, 9 | "required": ["iterations", "arraySize", "rollback"] 10 | } 11 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/read_named_inputs.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | read testAction(iterations: Number, arraySize: Number, rollback: Boolean) returns (Any) 4 | } 5 | } 6 | 7 | api Test { 8 | models { 9 | Person 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/update_all_optional_inputs.keel: -------------------------------------------------------------------------------- 1 | enum Hobby { 2 | Tennis 3 | Chess 4 | } 5 | 6 | model Person { 7 | fields { 8 | name Text 9 | birthday Date 10 | hobby Hobby 11 | picture File 12 | canHoldBreath Duration 13 | } 14 | 15 | actions { 16 | update testAction(id) with (name?, birthday?, hobby?, picture?, canHoldBreath?) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/update_no_with_inputs.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | name Text? 4 | email Text 5 | } 6 | 7 | actions { 8 | update testAction(id) { 9 | @set(account.email = ctx.identity.email) 10 | } 11 | } 12 | } 13 | 14 | api Test { 15 | models { 16 | Account 17 | } 18 | } -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/update_nullable_relationship.keel: -------------------------------------------------------------------------------- 1 | model Company {} 2 | 3 | model Person { 4 | fields { 5 | employee Company? 6 | name Text? 7 | } 8 | 9 | actions { 10 | update testAction(id) with (name, employee.id) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/update_related_id.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | company Company 5 | } 6 | 7 | actions { 8 | update testAction(id) with (company.id) 9 | } 10 | } 11 | 12 | model Company { 13 | fields { 14 | name Text 15 | } 16 | } 17 | 18 | api Test { 19 | models { 20 | Person 21 | Company 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/update_required_inputs.keel: -------------------------------------------------------------------------------- 1 | enum Hobby { 2 | Tennis 3 | Chess 4 | } 5 | 6 | model Person { 7 | fields { 8 | name Text 9 | birthday Date 10 | hobby Hobby 11 | picture File 12 | } 13 | 14 | actions { 15 | update testAction(id) with (name, birthday, hobby, picture) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/write.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "properties": { 4 | "name": { "type": "string" }, 5 | "optionalName": { "type": "string" } 6 | }, 7 | "unevaluatedProperties": false, 8 | "required": ["name"] 9 | } 10 | -------------------------------------------------------------------------------- /runtime/jsonschema/testdata/write.keel: -------------------------------------------------------------------------------- 1 | message PersonResponse { 2 | name Text 3 | } 4 | 5 | message PersonInput { 6 | name Text 7 | optionalName Text? 8 | } 9 | 10 | model Person { 11 | fields { 12 | name Text 13 | } 14 | 15 | actions { 16 | write testAction(PersonInput) returns (PersonResponse) 17 | } 18 | } 19 | 20 | api Test { 21 | models { 22 | Person 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /runtime/openapi/testdata/api_default.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | actions { 3 | get getAuthor(id) 4 | delete deleteAuthor(id) 5 | } 6 | } -------------------------------------------------------------------------------- /runtime/openapi/testdata/api_identity.keel: -------------------------------------------------------------------------------- 1 | api Admin { 2 | models { 3 | Identity 4 | } 5 | } -------------------------------------------------------------------------------- /runtime/openapi/testdata/files.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | photo File 4 | } 5 | actions { 6 | get getAccount(id) 7 | list listAccounts() 8 | write writeAccounts(FileMessage) returns (FileMessage) 9 | } 10 | } 11 | 12 | message FileMessage { 13 | file File 14 | } -------------------------------------------------------------------------------- /runtime/openapi/testdata/flow.keel: -------------------------------------------------------------------------------- 1 | flow MyWorkflow { 2 | inputs { 3 | name Text 4 | age Number 5 | } 6 | } 7 | 8 | flow AnotherWorkflow { 9 | inputs { 10 | age Number 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /runtime/openapi/testdata/job.keel: -------------------------------------------------------------------------------- 1 | job TestJob { 2 | inputs { 3 | input1 Text 4 | input2 Boolean 5 | input3 File 6 | } 7 | 8 | @permission(roles: [Admin]) 9 | } 10 | 11 | job TestJob2 { 12 | inputs { 13 | testinput1 Text 14 | testinput2 Boolean 15 | } 16 | 17 | @permission(roles: [Admin]) 18 | } 19 | 20 | role Admin { 21 | } 22 | -------------------------------------------------------------------------------- /runtime/openapi/testdata/job_scheduled.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.1.0", 3 | "info": { 4 | "title": "TestScheduled", 5 | "version": "1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /runtime/openapi/testdata/job_scheduled.keel: -------------------------------------------------------------------------------- 1 | job TestScheduled { 2 | @schedule("*/3 * * * *") 3 | } 4 | -------------------------------------------------------------------------------- /runtime/openapi/testdata/named_inputs.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | read testAction(iterations: Number, arraySize: Number, rollback: Boolean) returns (Any) 4 | } 5 | } 6 | 7 | api Test { 8 | models { 9 | Person 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /runtime/runtimectx/now.go: -------------------------------------------------------------------------------- 1 | package runtimectx 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func GetNow() time.Time { 8 | return time.Now().UTC() 9 | } 10 | -------------------------------------------------------------------------------- /schema/.gitignore: -------------------------------------------------------------------------------- 1 | testdata/package.json 2 | testdata/package-lock.json 3 | testdata/tsconfig.json 4 | 5 | testdata/*/package.json 6 | testdata/*/package-lock.json 7 | testdata/*/tsconfig.json 8 | -------------------------------------------------------------------------------- /schema/completions/fixtures/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: "TEST" 3 | value: "test" 4 | - name: "TEST_2" 5 | value: "test2" 6 | 7 | secrets: 8 | - name: API_KEY 9 | -------------------------------------------------------------------------------- /schema/format/testdata/actions_many_attributes.txt: -------------------------------------------------------------------------------- 1 | model Post { 2 | actions { 3 | get getPost(id) @function @permission(expression: true) 4 | } 5 | } 6 | 7 | === 8 | 9 | model Post { 10 | actions { 11 | get getPost(id) { 12 | @function 13 | @permission(expression: true) 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /schema/format/testdata/actions_single_function_attribute.txt: -------------------------------------------------------------------------------- 1 | model Post { 2 | actions { 3 | get getPost(id) @function 4 | } 5 | } 6 | 7 | === 8 | 9 | model Post { 10 | actions { 11 | get getPost(id) @function 12 | } 13 | } -------------------------------------------------------------------------------- /schema/format/testdata/api.txt: -------------------------------------------------------------------------------- 1 | api Web { 2 | models { 3 | Person 4 | } 5 | } 6 | 7 | === 8 | 9 | api Web { 10 | models { 11 | Person 12 | } 13 | } -------------------------------------------------------------------------------- /schema/format/testdata/arbitrary_function.txt: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Text 3 | } 4 | model Person { 5 | actions { 6 | read getPerson(Foo) returns(Foo) 7 | write mutatePerson(Foo) returns ( Foo ) 8 | } 9 | } 10 | === 11 | message Foo { 12 | bar Text 13 | } 14 | 15 | model Person { 16 | actions { 17 | read getPerson(Foo) returns (Foo) 18 | write mutatePerson(Foo) returns (Foo) 19 | } 20 | } -------------------------------------------------------------------------------- /schema/format/testdata/basic.txt: -------------------------------------------------------------------------------- 1 | model Person { fields { name Text } } 2 | === 3 | model Person { 4 | fields { 5 | name Text 6 | } 7 | } -------------------------------------------------------------------------------- /schema/format/testdata/enum.txt: -------------------------------------------------------------------------------- 1 | enum Colours { 2 | Red Blue Green 3 | V220V ALLCAPS alllower some_underscore 4 | } 5 | 6 | === 7 | 8 | enum Colours { 9 | Red 10 | Blue 11 | Green 12 | V220V 13 | ALLCAPS 14 | alllower 15 | some_underscore 16 | } 17 | -------------------------------------------------------------------------------- /schema/format/testdata/flow_inputs.txt: -------------------------------------------------------------------------------- 1 | flow MyWorkflow { 2 | inputs { 3 | name Text 4 | Age Number 5 | ThingID ID 6 | anotherFile File 7 | } 8 | } 9 | 10 | === 11 | 12 | flow MyWorkflow { 13 | inputs { 14 | name Text 15 | age Number 16 | thingId ID 17 | anotherFile File 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /schema/format/testdata/job.txt: -------------------------------------------------------------------------------- 1 | job ScheduleJob { 2 | @schedule("* * * * *") 3 | } 4 | 5 | === 6 | 7 | job ScheduleJob { 8 | @schedule("* * * * *") 9 | } -------------------------------------------------------------------------------- /schema/format/testdata/job_inputs.txt: -------------------------------------------------------------------------------- 1 | job MyJob { 2 | inputs { 3 | name Text 4 | Age Number 5 | ThingID ID 6 | } 7 | 8 | @permission( "admin") 9 | } 10 | 11 | === 12 | 13 | job MyJob { 14 | inputs { 15 | name Text 16 | age Number 17 | thingId ID 18 | } 19 | 20 | @permission("admin") 21 | } -------------------------------------------------------------------------------- /schema/format/testdata/job_inputs_optional.txt: -------------------------------------------------------------------------------- 1 | job MyJob { 2 | inputs { 3 | name Text ? 4 | } 5 | 6 | @permission("admin") 7 | } 8 | 9 | === 10 | 11 | job MyJob { 12 | inputs { 13 | name Text? 14 | } 15 | 16 | @permission("admin") 17 | } -------------------------------------------------------------------------------- /schema/format/testdata/message.txt: -------------------------------------------------------------------------------- 1 | message Foo { 2 | // bar field 3 | bar Text 4 | // baz field 5 | baz Number 6 | // boo field 7 | boo Text? 8 | } 9 | === 10 | message Foo { 11 | // bar field 12 | bar Text 13 | // baz field 14 | baz Number 15 | // boo field 16 | boo Text? 17 | } -------------------------------------------------------------------------------- /schema/format/testdata/model_attributes.txt: -------------------------------------------------------------------------------- 1 | model Person { 2 | @permission(expression: ctx.identity != null, roles: [Staff]) 3 | } 4 | 5 | === 6 | 7 | model Person { 8 | @permission( 9 | expression: ctx.identity != null, 10 | roles: [Staff] 11 | ) 12 | } -------------------------------------------------------------------------------- /schema/format/testdata/optional_field.txt: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text? 4 | } 5 | } 6 | 7 | === 8 | 9 | model Person { 10 | fields { 11 | name Text? 12 | } 13 | } -------------------------------------------------------------------------------- /schema/format/testdata/optional_input.txt: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text? 4 | } 5 | actions { 6 | create createPerson() with (name?) 7 | } 8 | } 9 | 10 | === 11 | 12 | model Person { 13 | fields { 14 | name Text? 15 | } 16 | 17 | actions { 18 | create createPerson() with (name?) 19 | } 20 | } -------------------------------------------------------------------------------- /schema/format/testdata/repeated_field.txt: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | faveColours Text[] 4 | optionalColours Text[]? 5 | } 6 | } 7 | 8 | === 9 | 10 | model Person { 11 | fields { 12 | faveColours Text[] 13 | optionalColours Text[]? 14 | } 15 | } -------------------------------------------------------------------------------- /schema/format/testdata/roles.txt: -------------------------------------------------------------------------------- 1 | role Staff { 2 | emails { 3 | "david@myorg.com" 4 | "sally@myorg.com" 5 | } 6 | domains { 7 | "myorg.com" 8 | } 9 | } 10 | 11 | === 12 | 13 | role Staff { 14 | domains { 15 | "myorg.com" 16 | } 17 | 18 | emails { 19 | "david@myorg.com" 20 | "sally@myorg.com" 21 | } 22 | } -------------------------------------------------------------------------------- /schema/format/testdata/route_functions.txt: -------------------------------------------------------------------------------- 1 | routes { 2 | // a comment 3 | get("/my/route", myHandler) 4 | post( 5 | "/my/post/route", 6 | myOtherHandler 7 | ) 8 | } 9 | 10 | === 11 | 12 | routes { 13 | // a comment 14 | get("/my/route", myHandler) 15 | post("/my/post/route", myOtherHandler) 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/errors/action_types.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | //expect-error:9:12:TypeError:foo is not a valid action type. Valid types are get, create, update, list, or delete 8 | foo something() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/actions_update_invalid_where_operator.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | niNumber Number @unique 5 | } 6 | 7 | actions { 8 | //expect-error:16:26:ActionInputError:The action 'updateName' can only update a single record and therefore must be filtered by unique fields 9 | update updateName() with (name) { 10 | @where(person.niNumber > 100) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/actions_update_no_unique_input.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:26:ActionInputError:The action 'updateName' can only update a single record and therefore must be filtered by unique fields 8 | update updateName() with (name) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/actions_update_non_unique_input.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | age Number 5 | } 6 | 7 | actions { 8 | //expect-error:16:26:ActionInputError:The action 'updateName' can only update a single record and therefore must be filtered by unique fields 9 | update updateName(age) with (name) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/actions_update_non_unique_where.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | age Number 5 | } 6 | 7 | actions { 8 | //expect-error:16:26:ActionInputError:The action 'updateName' can only update a single record and therefore must be filtered by unique fields 9 | update updateName() with (name) { 10 | @where(person.age == 21) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/api_names_are_models.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | } 3 | 4 | api Web { 5 | models { 6 | Post 7 | //expect-error:9:21:E047:api 'Web' has an unrecognised model UnknownModel 8 | UnknownModel 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/api_unsupported_attribute.keel: -------------------------------------------------------------------------------- 1 | api Web { 2 | //expect-error:5:10:E011:api 'Web' has an unrecognised attribute @what 3 | @what 4 | } 5 | -------------------------------------------------------------------------------- /schema/testdata/errors/arbitrary_function_action_types.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Text 3 | } 4 | 5 | model Person { 6 | actions { 7 | //expect-error:9:12:TypeError:The 'returns' keyword can only be used with 'read' or 'write' actions 8 | get getPerson(id) returns (foo) @function 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/arbitrary_functions_unknown_message_return.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | //expect-error:46:60:ActionInputError:read and write functions must return a message-based response, or Any 4 | write createBulkPeople(Any) returns (UnknownMessage) @function 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/array_fields_relation.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | fields { 3 | //expect-error:22:31:RelationshipError:The @relation attribute cannot be used on non-model fields 4 | texts Text[] @relation(otherThing.thing) 5 | } 6 | } 7 | 8 | model OtherThing { 9 | fields { 10 | thing Thing 11 | } 12 | } 13 | 14 | enum MyEnum { 15 | One 16 | Two 17 | } -------------------------------------------------------------------------------- /schema/testdata/errors/array_fields_required.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | fields { 3 | name Text 4 | texts Text[] 5 | } 6 | 7 | actions { 8 | //expect-error:16:27:E034:required field 'texts' must be set by a non-optional input, a @set expression or with @default 9 | create createThing() with (name) 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/array_fields_unique.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | fields { 3 | //expect-error:22:29:TypeError:@unique is not permitted on has many relationships or arrays 4 | texts Text[] @unique 5 | //expect-error:24:31:TypeError:@unique is not permitted on has many relationships or arrays 6 | enums MyEnum[] @unique 7 | } 8 | } 9 | 10 | enum MyEnum { 11 | One 12 | Two 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/attribute_expression_invalid_root_model.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | } 5 | 6 | @permission( 7 | //expect-error:21:24:AttributeExpressionError:unknown identifier 'pos' 8 | expression: pos.title != "", 9 | actions: [get] 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/attribute_unique_invalids_args.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:28:31:AttributeArgumentError:unexpected argument for @unique as no arguments are expected 4 | //expect-error:20:32:AttributeArgumentError:1 argument(s) provided to @unique but expected 0 5 | title Text @unique(arg) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/errors/clashing_explicit_implicit_inputs.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | } 5 | 6 | actions { 7 | create createPost() with (title, coolTitle: Text) { 8 | //expect-error:18:28:ActionInputError:title is already being used as a value input so cannot also be used in @set 9 | @set(post.title = coolTitle) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/create_missing_identity.keel: -------------------------------------------------------------------------------- 1 | model Todo { 2 | fields { 3 | name Text 4 | identity Identity 5 | } 6 | 7 | actions { 8 | //expect-error:16:26:ActionInputError:the identity field of Todo is not set as part of this create action 9 | create createTodo() with (name) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/default_invalid_expression.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:41:43:AttributeExpressionError:operator '==' not supported in this context 4 | published Boolean @default(true == false) 5 | //expect-error:35:38:AttributeExpressionError:unknown identifier 'ctx' 6 | isAuthed Boolean @default(ctx.isAuthenticated) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /schema/testdata/errors/delete_operation_no_unique_lookup.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | actions { 3 | //expect-error:16:29:ActionInputError:The action 'deleteMyModel' can only delete a single record and therefore must be filtered by unique fields 4 | delete deleteMyModel() 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/delete_operation_no_unique_lookup_in_inputs.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | text Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:29:ActionInputError:The action 'deleteMyModel' can only delete a single record and therefore must be filtered by unique fields 8 | delete deleteMyModel(text) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/delete_operation_no_unique_lookup_in_wheres.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | nonUnique Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:29:ActionInputError:The action 'deleteMyModel' can only delete a single record and therefore must be filtered by unique fields 8 | delete deleteMyModel() { 9 | @where(myModel.nonUnique == "something") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/enum_default_gibberish.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:32:41:AttributeExpressionError:unknown identifier 'Gibberish' 4 | type PostType @default(Gibberish) 5 | } 6 | } 7 | 8 | enum PostType { 9 | Draft 10 | Published 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/enum_default_multiple_conditions.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:41:43:AttributeExpressionError:operator '||' not supported in this context 4 | published Boolean @default(true || false) 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/enum_default_no_expression.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:23:31:AttributeArgumentError:@default requires an expression 4 | type PostType @default 5 | } 6 | } 7 | 8 | enum PostType { 9 | Draft 10 | Published 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/enum_default_wrong_type.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:32:39:AttributeExpressionError:expression expected to resolve to type PostType but it is Text 4 | type PostType @default("Draft") 5 | } 6 | } 7 | 8 | enum PostType { 9 | Draft 10 | Published 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/environment_variables.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | @permission( 3 | //expect-error:28:29:AttributeExpressionError:field 'FOO' does not exist 4 | expression: ctx.env.FOO == "d", 5 | actions: [get] 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/errors/expression_array_operator_mismatch.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | authors Author[] 4 | } 5 | 6 | @permission( 7 | //expect-error:27:28:AttributeExpressionError:cannot use operator '>' with types Text and Text[] 8 | expression: "bob" > post.authors.name, 9 | actions: [get] 10 | ) 11 | } 12 | 13 | model Author { 14 | fields { 15 | name Text 16 | post Post 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema/testdata/errors/expression_in_array.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | authors Author[] 4 | } 5 | 6 | @permission( 7 | //expect-error:27:29:AttributeExpressionError:cannot use operator 'in' with types Text and Number[] 8 | expression: "bob" in post.authors.name, 9 | actions: [get] 10 | ) 11 | } 12 | 13 | model Author { 14 | fields { 15 | name Number 16 | post Post 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema/testdata/errors/expression_literal_string_in_literal_array.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | @permission( 3 | //expect-error:27:29:AttributeExpressionError:cannot use operator 'in' with types Text and Number[] 4 | expression: "bob" in [1, 2], 5 | actions: [get] 6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/errors/expression_preserving_spaces_and_tabs.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | @permission( 3 | //expect-error:62:63:AttributeExpressionError:field 'unknown' does not exist 4 | expression: ctx.isAuthenticated == ctx.unknown, 5 | actions: [get] 6 | ) 7 | } -------------------------------------------------------------------------------- /schema/testdata/errors/field_required_of_same_model_type.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | //expect-error:16:22:TypeError:The model 'Person' cannot have a field of its own type unless it is optional. 4 | person Person 5 | } 6 | 7 | actions { 8 | create createPerson() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/field_required_with_optional_input.keel: -------------------------------------------------------------------------------- 1 | model StockLocation { 2 | fields { 3 | title Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:35:E034:required field 'title' must be set by a non-optional input, a @set expression or with @default 8 | create createStockLocation() with (title?) 9 | } 10 | } -------------------------------------------------------------------------------- /schema/testdata/errors/field_unsupported_attribute.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | //expect-error:27:37:E011:field 'identity' has an unrecognised attribute @something 4 | identity Identity @something 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/fields_names_max_lenght.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | //expect-error:9:62:E052:Cannot use 'aVeryLongLongLongLongLongFieldNameThatIsTrulyVeryLong' as a field name as it is too long. 4 | aVeryLongLongLongLongLongFieldNameThatIsTrulyVeryLong Text 5 | aLongLongLongLongLongFieldNameThatIsNotVeryLong Text 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/errors/fields_unique_in_model.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | name Text 4 | //expect-error:9:13:E003:Cannot use 'name' as it has already been defined on this model 5 | name Text 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/errors/function_unsupported_attribute.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | username Text 4 | } 5 | 6 | actions { 7 | create createProfile() with (username) { 8 | //expect-error:13:21:E011:actions 'createProfile' has an unrecognised attribute @unknown 9 | @unknown 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/get_operation_no_unique_lookup.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | actions { 3 | //expect-error:13:23:ActionInputError:The action 'getMyModel' can only get a single record and therefore must be filtered by unique fields 4 | get getMyModel() 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/get_operation_no_unique_lookup_in_inputs.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | text Text 4 | } 5 | 6 | actions { 7 | //expect-error:13:23:ActionInputError:The action 'getMyModel' can only get a single record and therefore must be filtered by unique fields 8 | get getMyModel(text) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/get_operation_no_unique_lookup_in_wheres.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | nonUnique Text 4 | } 5 | 6 | actions { 7 | //expect-error:13:23:ActionInputError:The action 'getMyModel' can only get a single record and therefore must be filtered by unique fields 8 | get getMyModel() { 9 | @where(myModel.nonUnique == "something") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/identity_backlinks_invalid_relations_field.keel: -------------------------------------------------------------------------------- 1 | model CompanyEmployee { 2 | fields { 3 | identity Identity { 4 | @unique 5 | //expect-error:23:32:RelationshipError:The field 'createdAt' on Identity must be of type CompanyEmployee in order to establish a relationship 6 | @relation(createdAt) 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /schema/testdata/errors/identity_backlinks_no_relations_attribute.keel: -------------------------------------------------------------------------------- 1 | model CompanyEmployee { 2 | fields { 3 | abcIdentity Identity @unique 4 | //expect-error:9:20:RelationshipError:Cannot associate with field 'companyEmployee' on Identity to form a one to one relationship because it is already associated with 'abcIdentity' 5 | xyzIdentity Identity @unique 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/errors/jobs_duplicate_inputs_names.keel: -------------------------------------------------------------------------------- 1 | job MyJob1 { 2 | inputs { 3 | myField Text 4 | //expect-error:9:16:DuplicateDefinitionError:Job input with name 'myField' already exists 5 | myField Text 6 | } 7 | 8 | @permission(roles: [Admin]) 9 | } 10 | 11 | role Admin { 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/jobs_duplicate_names.keel: -------------------------------------------------------------------------------- 1 | //expect-error:5:10:NamingError:There already exists a job with the name 'MyJob' 2 | job MyJob { 3 | @schedule("* * * * *") 4 | } 5 | 6 | //expect-error:5:10:NamingError:There already exists a job with the name 'MyJob' 7 | job MyJob { 8 | @schedule("* * * * *") 9 | } 10 | -------------------------------------------------------------------------------- /schema/testdata/errors/jobs_empty.keel: -------------------------------------------------------------------------------- 1 | //expect-error:5:10:JobDefinitionError:Job 'MyJob' must be defined with either @schedule or @permission 2 | job MyJob { 3 | } 4 | -------------------------------------------------------------------------------- /schema/testdata/errors/lhs_rhs_type_mismatch.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | } 5 | 6 | @permission( 7 | //expect-error:32:34:AttributeExpressionError:cannot use operator '==' with types Text and Number 8 | expression: post.title == 12, 9 | actions: [get] 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/message_in_operation.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Text 3 | } 4 | 5 | model Person { 6 | actions { 7 | //expect-error:29:32:E005:Action inputs must be one of the fields defined in the model 8 | //expect-error:29:32:E033:create actions cannot take read inputs 9 | create createPerson(foo) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/message_input_with_normal_custom_fn.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Text 3 | } 4 | 5 | model Person { 6 | actions { 7 | //expect-error:37:40:E005:Action inputs must be one of the fields defined in the model 8 | create createPerson() with (foo) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/message_types_and_inline_inputs.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Text 3 | } 4 | 5 | model Person { 6 | fields { 7 | name Text 8 | } 9 | 10 | actions { 11 | //expect-error:24:27:ActionInputError:read and write functions must receive exactly one message-based input 12 | read getPerson(Foo, baz: Text) returns (Foo) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/errors/messages.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | one Text 3 | //expect-error:5:8:DuplicateDefinitionError:field 'one' already defined in message 'Foo' 4 | one Text 5 | //expect-error:9:21:TypeError:invalid type 'FibbleBibble' - must be a built-in type, model, enum, or message 6 | two FibbleBibble 7 | //expect-error:16:23:AttributeNotAllowedError:message fields do not support attributes 8 | three Text @unique 9 | } 10 | -------------------------------------------------------------------------------- /schema/testdata/errors/model_names_max_lenght.keel: -------------------------------------------------------------------------------- 1 | //expect-error:7:51:E053:Cannot use 'ALongLongLongLongLongModelNameThatIsVeryLong' as a model name as it is too long. 2 | model ALongLongLongLongLongModelNameThatIsVeryLong { 3 | fields { 4 | foo Text 5 | } 6 | } 7 | 8 | model ALongLongLongLongModelNameThatNotVeryLong { 9 | fields { 10 | foo Text 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/model_unsupported_attribute.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | //expect-error:5:16:E011:model 'Person' has an unrecognised attribute @whatisthis 3 | @whatisthis 4 | } 5 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_create_missing_inputs.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | age Number 5 | notRequired Text? 6 | } 7 | 8 | actions { 9 | //expect-error:16:28:E034:required field 'age' must be set by a non-optional input, a @set expression or with @default 10 | create createPerson() with (name) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_inputs_match_model_fields.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | //expect-error:43:47:E005:Action inputs must be one of the fields defined in the model 8 | create createPerson() with (name, blah) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_set_expression_forbidden_operator.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | identity Identity @unique 4 | username Text @unique 5 | } 6 | 7 | actions { 8 | update createProfile(id) with (username) { 9 | //expect-error:18:50:AttributeExpressionError:the @set attribute must be an assignment expression 10 | @set(profile.identity == ctx.identity) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_set_expression_invalid_lhs.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | username Text @unique 4 | } 5 | 6 | actions { 7 | update createProfile(id) with (username) { 8 | //expect-error:18:21:AttributeExpressionError:The @set attribute can only be used to set model fields 9 | @set(123 = ctx.identity) 10 | } 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_set_expression_unresolvable_lhs.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | username Text @unique 4 | } 5 | 6 | actions { 7 | update createProfile(id) with (username) { 8 | //expect-error:25:26:AttributeExpressionError:field 'identit' does not exist 9 | @set(profile.identit = ctx.identity) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_set_expression_unresolvable_rhs.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | username Text @unique 4 | } 5 | 6 | actions { 7 | create createProfile() { 8 | //expect-error:37:49:AttributeExpressionError:expression expected to resolve to type Text but it is Identity 9 | @set(profile.username = ctx.identity) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_set_expression_wrong_type.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | username Text @unique 4 | } 5 | 6 | actions { 7 | create createProfile() { 8 | //expect-error:37:40:AttributeExpressionError:expression expected to resolve to type Text but it is Number 9 | @set(profile.username = 123) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_set_forbidden_value_expression.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | identity Identity @unique 4 | username Text @unique 5 | } 6 | 7 | actions { 8 | update createProfile(id) with (username) { 9 | //expect-error:18:34:AttributeExpressionError:the @set attribute must be an assignment expression 10 | @set(profile.identity) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_unsupported_attribute.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | email Text @unique 4 | username Text @unique 5 | } 6 | 7 | actions { 8 | create createProfile() with (username, email) { 9 | //expect-error:13:18:E011:actions 'createProfile' has an unrecognised attribute @save 10 | @save(profile.identity = ctx.identity) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_where_expression_unresolvable_lhs.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | identity Identity @unique 4 | username Text @unique 5 | } 6 | 7 | actions { 8 | get getProfile(username) { 9 | //expect-error:27:28:AttributeExpressionError:field 'identit' does not exist 10 | @where(profile.identit == ctx.identity) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_where_expression_unresolvable_rhs.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | identity Identity @unique 4 | username Text @unique 5 | } 6 | 7 | actions { 8 | get getProfile(username) { 9 | //expect-error:40:47:AttributeExpressionError:unknown identifier 'context' 10 | @where(profile.identity == context.identity) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/operation_where_forbidden_value_expression.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | identity Identity @unique 4 | username Text @unique 5 | } 6 | 7 | actions { 8 | get getProfile(username) { 9 | //expect-error:20:36:AttributeExpressionError:expression expected to resolve to type Boolean but it is Identity 10 | @where(profile.identity) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/permission_attribute_action_as_actiontype_arg.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | get getPerson(id) 4 | } 5 | 6 | @permission( 7 | expression: true, 8 | //expect-error:19:28:AttributeExpressionError:unknown identifier 'getPerson' 9 | actions: [getPerson] 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/permission_attribute_expression_forbidden_operator.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | username Text 4 | } 5 | 6 | @permission( 7 | //expect-error:21:48:AttributeExpressionError:assignment operator '=' not valid - did you mean to use the comparison operator '=='? 8 | expression: profile.username = "adaam2", 9 | actions: [get] 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/permission_attribute_missing_actions.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | //expect-error:5:16:AttributeArgumentError:required argument 'actions' missing 3 | @permission(expression: true) 4 | } 5 | -------------------------------------------------------------------------------- /schema/testdata/errors/permission_attribute_missing_condition.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | get getPerson(id) 4 | } 5 | 6 | //expect-error:5:16:AttributeArgumentError:@permission requires either the 'expressions' or 'roles' argument to be provided 7 | @permission(actions: [get]) 8 | } 9 | -------------------------------------------------------------------------------- /schema/testdata/errors/permission_job.keel: -------------------------------------------------------------------------------- 1 | job MyJob { 2 | @permission( 3 | //expect-error:9:16:AttributeArgumentError:cannot provide 'actions' arguments when using @permission in a job 4 | //expect-error:9:16:AttributeArgumentError:unexpected argument 'actions' for @permission 5 | actions: [get], 6 | roles: [MyRole] 7 | ) 8 | } 9 | 10 | role MyRole { 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_attr_empty.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:24:33:AttributeArgumentError:expected an argument for @relation 4 | //expect-error:24:33:RelationshipError:The @relation argument must refer to a field on Author 5 | author2 Author @relation 6 | } 7 | } 8 | 9 | model Author { 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_attr_must_be_identifier.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:9:15:E009:field author has an unsupported type Author 4 | //expect-error:23:32:RelationshipError:The @relation attribute cannot be used on non-model fields 5 | author Author @relation(123) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_attr_must_be_on_model_field.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | //expect-error:21:30:RelationshipError:The @relation attribute cannot be used on non-model fields 4 | author Text @relation(author) 5 | } 6 | } 7 | 8 | model Author { 9 | fields { 10 | author Post 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_attr_must_be_on_non_repeated_field.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | unused Post 4 | } 5 | } 6 | 7 | model Post { 8 | fields { 9 | //expect-error:25:34:RelationshipError:The @relation attribute must be defined on the other side of a one to many relationship 10 | author Author[] @relation(written) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_many_to_many.keel: -------------------------------------------------------------------------------- 1 | model ModelA { 2 | fields { 3 | //expect-error:9:10:RelationshipError:The field 'b' does not have an associated field on ModelB 4 | b ModelB[] 5 | } 6 | } 7 | 8 | model ModelB { 9 | fields { 10 | //expect-error:9:10:RelationshipError:The field 'a' does not have an associated field on ModelA 11 | a ModelA[] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_one_to_many_no_fields_on_one_side.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | } 3 | 4 | model Post { 5 | fields { 6 | //expect-error:9:15:RelationshipError:The field 'author' does not have an associated field on Author 7 | author Author[] 8 | //expect-error:9:17:RelationshipError:The field 'coAuthor' does not have an associated field on Author 9 | coAuthor Author[] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_one_to_many_no_relation_attributes.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | written Post 4 | //expect-error:9:18:RelationshipError:Cannot associate with repeated field 'author' on Post to form a one to many relationship because it is already associated with field 'written' 5 | coWritten Post 6 | } 7 | } 8 | 9 | model Post { 10 | fields { 11 | author Author[] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_one_to_many_unmatched_many_side.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | post Post 4 | } 5 | } 6 | 7 | model Post { 8 | fields { 9 | author Author[] 10 | //expect-error:9:17:RelationshipError:Cannot form a one to many relationship with field 'post' on Author as it is already associated with field 'author' 11 | coAuthor Author[] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_one_to_one_mismatch.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | student Student @unique 4 | } 5 | } 6 | 7 | model Student { 8 | fields { 9 | user User 10 | //expect-error:9:14:RelationshipError:Cannot form a one to one relationship with field 'student' on User as it is already associated with field 'user' 11 | user2 User 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_one_to_one_missing_relation_attribute.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | student Student @unique 4 | } 5 | } 6 | 7 | model Student { 8 | fields { 9 | user User 10 | //expect-error:9:14:RelationshipError:Cannot form a one to one relationship with field 'student' on User as it is already associated with field 'user' 11 | user2 User 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_one_to_one_with_relation_attr_no_unique_attr.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | written Post 4 | } 5 | } 6 | 7 | model Post { 8 | fields { 9 | //expect-error:9:15:RelationshipError:A one to one relationship requires a single side to be @unique 10 | author Author @relation(written) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_one_to_one_with_relation_on_wrong_side.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | written Post @unique 4 | } 5 | } 6 | 7 | model Post { 8 | fields { 9 | //expect-error:33:40:RelationshipError:Cannot create a relationship to the unique field 'written' on Author 10 | author Author @relation(written) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/relationships_optional_on_many.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | //expect-error:9:14:RelationshipError:Cannot define a repeated model field as optional 4 | posts Post[]? 5 | } 6 | } 7 | 8 | model Post { 9 | fields { 10 | author Author 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/request_headers_invalid_dot_notation.keel: -------------------------------------------------------------------------------- 1 | model Something { 2 | fields { 3 | foo Text 4 | } 5 | 6 | actions { 7 | create createSomething() with (foo) { 8 | //expect-error:75:76:AttributeExpressionError:type Text does not have any fields to select 9 | @permission(expression: something.createdAt == ctx.headers.KEY.KEY2) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_action_name_request_password_reset.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | email Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:36:NamingError:There already exists a reserved action with the name 'requestPasswordReset' 8 | create requestPasswordReset() with (email) 9 | } 10 | } 11 | 12 | api Web { 13 | models { 14 | User 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_action_name_request_password_reset_function.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | email Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:36:NamingError:There already exists a reserved action with the name 'requestPasswordReset' 8 | create requestPasswordReset() with (email) 9 | } 10 | } 11 | 12 | api Web { 13 | models { 14 | User 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_action_name_reset_password.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | email Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:29:NamingError:There already exists a reserved action with the name 'resetPassword' 8 | create resetPassword() with (email) 9 | } 10 | } 11 | 12 | api Web { 13 | models { 14 | User 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_action_name_reset_password_function.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | email Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:29:NamingError:There already exists a reserved action with the name 'resetPassword' 8 | create resetPassword() with (email) 9 | } 10 | } 11 | 12 | api Web { 13 | models { 14 | User 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_field_name_created_at.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | //expect-error:9:18:E006:Cannot use 'createdAt' as it already exists as a built-in field 4 | createdAt Text 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_field_name_id.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | //expect-error:9:11:E006:Cannot use 'id' as it already exists as a built-in field 4 | id Text 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_field_name_updated_at.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | //expect-error:9:18:E006:Cannot use 'updatedAt' as it already exists as a built-in field 4 | updatedAt Text 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_model_name_identity.keel: -------------------------------------------------------------------------------- 1 | //expect-error:7:15:NamingError:There already exists a reserved model with the name 'Identity' 2 | model Identity { 3 | fields { 4 | email Text 5 | } 6 | } -------------------------------------------------------------------------------- /schema/testdata/errors/reserved_model_names.keel: -------------------------------------------------------------------------------- 1 | //expect-error:7:12:NamingError:Reserved name 'Query' 2 | model Query { 3 | } 4 | -------------------------------------------------------------------------------- /schema/testdata/errors/role_duplicate_definitions.keel: -------------------------------------------------------------------------------- 1 | role Admin { 2 | emails { 3 | "tom@keel.xyz" 4 | } 5 | } 6 | 7 | //expect-error:6:11:E018:You have a duplicate definition for 'role Admin' 8 | role Admin { 9 | emails { 10 | "adam@keel.xyz" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/unique_input_bare_model.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | } 3 | 4 | model Book { 5 | fields { 6 | author Author @unique 7 | } 8 | 9 | actions { 10 | //expect-error:25:31:ActionInputError:'author' refers to a model which cannot be used as an input 11 | get getByAuthor(author) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/unresolvable_lhs_with_incorrect_operator.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | published Boolean 5 | } 6 | 7 | actions { 8 | get posts(id) { 9 | //expect-error:20:37:AttributeExpressionError:assignment operator '=' not valid - did you mean to use the comparison operator '=='? 10 | @where(post.titles = 123) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/update_operation_no_unique_lookup_in_inputs.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | text Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:29:ActionInputError:The action 'updateMyModel' can only update a single record and therefore must be filtered by unique fields 8 | update updateMyModel(text) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/errors/update_operation_no_unique_lookup_in_wheres.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | nonUnique Text 4 | } 5 | 6 | actions { 7 | //expect-error:16:29:ActionInputError:The action 'updateMyModel' can only update a single record and therefore must be filtered by unique fields 8 | update updateMyModel() { 9 | @where(myModel.nonUnique == "something") 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/errors/where_expression_single_string_literal.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text 4 | published Boolean 5 | } 6 | 7 | actions { 8 | get posts(id) { 9 | //expect-error:20:31:AttributeExpressionError:expression expected to resolve to type Boolean but it is Text 10 | @where("something") 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/errors/where_mismatched_operator_for_matching_lhs_rhs.keel: -------------------------------------------------------------------------------- 1 | model Profile { 2 | fields { 3 | username Text 4 | } 5 | 6 | @permission( 7 | //expect-error:38:40:AttributeExpressionError:cannot use operator '>=' with types Text and Text 8 | expression: profile.username >= "adaam2", 9 | actions: [get] 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/action_inputs_with_empty/schema.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | name Text? 4 | email Text 5 | } 6 | 7 | actions { 8 | create createAccount() with () { 9 | @set(account.email = ctx.identity.email) 10 | } 11 | 12 | update updateAccount(id) with () { 13 | @set(account.email = ctx.identity.email) 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /schema/testdata/proto/action_inputs_with_omitted/schema.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | name Text? 4 | email Text 5 | } 6 | 7 | actions { 8 | create createAccount() { 9 | @set(account.email = ctx.identity.email) 10 | } 11 | 12 | update updateAccount(id) { 13 | @set(account.email = ctx.identity.email) 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /schema/testdata/proto/apis_default_api_false/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: false -------------------------------------------------------------------------------- /schema/testdata/proto/apis_default_api_false/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { } 2 | 3 | api Admin { 4 | models { 5 | Author 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/proto/apis_default_api_model_with_no_actions/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: true -------------------------------------------------------------------------------- /schema/testdata/proto/apis_default_api_model_with_no_actions/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { } -------------------------------------------------------------------------------- /schema/testdata/proto/apis_none_defined_and_default_api_false/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: false -------------------------------------------------------------------------------- /schema/testdata/proto/apis_none_defined_and_default_api_false/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | actions { 3 | get getAuthor(id) 4 | delete deleteAuthor(id) 5 | } 6 | } -------------------------------------------------------------------------------- /schema/testdata/proto/apis_none_defined_and_default_api_true/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: true -------------------------------------------------------------------------------- /schema/testdata/proto/apis_none_defined_and_default_api_true/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | actions { 3 | get getAuthor(id) 4 | delete deleteAuthor(id) 5 | } 6 | } -------------------------------------------------------------------------------- /schema/testdata/proto/apis_override_default_api_actions/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: true -------------------------------------------------------------------------------- /schema/testdata/proto/apis_override_default_api_models/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: true -------------------------------------------------------------------------------- /schema/testdata/proto/apis_override_default_api_models/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | actions { 3 | get getAuthor(id) 4 | delete deleteAuthor(id) 5 | } 6 | } 7 | 8 | model Book { 9 | actions { 10 | get getBook(id) 11 | } 12 | } 13 | 14 | api Api { 15 | models { 16 | Author 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema/testdata/proto/apis_without_actions_specified/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | actions { 3 | get getFoo(id) 4 | } 5 | } 6 | 7 | model Bar { 8 | actions { 9 | get getBar(id) 10 | } 11 | } 12 | 13 | api ApiOne { 14 | models { 15 | Foo 16 | Bar 17 | } 18 | } 19 | 20 | api ApiTwo { 21 | models { 22 | Foo 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /schema/testdata/proto/arbitrary_function/schema.keel: -------------------------------------------------------------------------------- 1 | message In { 2 | foo Text 3 | } 4 | 5 | message Out { 6 | bar Text 7 | } 8 | 9 | model Post { 10 | actions { 11 | read getPost(In) returns(Out) 12 | } 13 | } -------------------------------------------------------------------------------- /schema/testdata/proto/arbitrary_function_any_type/schema.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Any 3 | } 4 | 5 | model Person { 6 | actions { 7 | read getPerson(id) returns (Foo) { 8 | @function 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /schema/testdata/proto/arbitrary_function_inline_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | message Out { 2 | bar Text 3 | } 4 | 5 | model Post { 6 | fields { 7 | name Text 8 | } 9 | 10 | actions { 11 | read getPost(name, someBool: Boolean) returns(Out) 12 | } 13 | } -------------------------------------------------------------------------------- /schema/testdata/proto/arbitrary_function_no_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | message In {} 2 | 3 | message Out { 4 | bar Text 5 | } 6 | 7 | model Post { 8 | actions { 9 | read noInput() returns(Out) 10 | read emptyInput(In) returns(Out) 11 | write noInputWrite() returns(Out) 12 | write emptyInputWrite(In) returns(Out) 13 | } 14 | } -------------------------------------------------------------------------------- /schema/testdata/proto/arbitrary_functions_any/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | read getPerson(Any) returns(Any) 4 | write updatePerson(Any) returns(Any) 5 | } 6 | } -------------------------------------------------------------------------------- /schema/testdata/proto/array_fields/schema.keel: -------------------------------------------------------------------------------- 1 | model Thing { 2 | fields { 3 | texts Text[] 4 | numbers Number[] 5 | booleans Boolean[] 6 | dates Date[] 7 | timestamps Timestamp[] 8 | } 9 | 10 | actions { 11 | create createThing() with (texts, numbers, booleans, dates, timestamps) 12 | list listThings(texts, numbers, booleans, dates, timestamps) 13 | } 14 | } -------------------------------------------------------------------------------- /schema/testdata/proto/array_fields_optional/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | texts Text[]? 4 | numbers Number[]? 5 | booleans Boolean[]? 6 | dates Date[]? 7 | timestamps Timestamp[]? 8 | } 9 | 10 | actions { 11 | create createThing() with (texts?, numbers?, booleans?, dates?, timestamps?) 12 | } 13 | } -------------------------------------------------------------------------------- /schema/testdata/proto/attribute_computed/schema.keel: -------------------------------------------------------------------------------- 1 | model Item { 2 | fields { 3 | price Decimal 4 | units Decimal 5 | total Decimal @computed(item.price * item.units) 6 | } 7 | actions { 8 | create createItem() with (price, units) 9 | } 10 | } -------------------------------------------------------------------------------- /schema/testdata/proto/attribute_facet/schema.keel: -------------------------------------------------------------------------------- 1 | model Product { 2 | fields { 3 | name Text 4 | category Category 5 | colour Text 6 | inStock Boolean 7 | } 8 | 9 | actions { 10 | list listProducts(category?, colour?) { 11 | @facet(category, colour) 12 | } 13 | } 14 | } 15 | 16 | enum Category { 17 | Food 18 | Drink 19 | } -------------------------------------------------------------------------------- /schema/testdata/proto/attribute_on/schema.keel: -------------------------------------------------------------------------------- 1 | model Member { 2 | fields { 3 | name Text 4 | email Text 5 | } 6 | 7 | @on([create], sendWelcomeMail) 8 | @on([create, update], verifyEmail) 9 | @on([delete], sendGoodbyeMail) 10 | } 11 | 12 | model Employee { 13 | fields { 14 | name Text 15 | email Text 16 | } 17 | 18 | @on([create, update], verifyEmail) 19 | } -------------------------------------------------------------------------------- /schema/testdata/proto/attribute_orderby/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | firstName Text 4 | surname Text 5 | } 6 | 7 | actions { 8 | list listAuthors() { 9 | @orderBy(firstName: asc, surname: desc) 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /schema/testdata/proto/attribute_sortable/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | firstName Text 4 | surname Text 5 | } 6 | 7 | actions { 8 | list listAuthors() { 9 | @sortable(firstName, surname) 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /schema/testdata/proto/basics/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | firstName Text 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /schema/testdata/proto/computed_fields/schema.keel: -------------------------------------------------------------------------------- 1 | model Invoice { 2 | fields { 3 | items Item[] 4 | } 5 | } 6 | 7 | model Item { 8 | fields { 9 | invoice Invoice 10 | description Text 11 | price Decimal 12 | quantity Number 13 | total Decimal @computed(item.price * item.quantity) 14 | } 15 | 16 | actions { 17 | list listItems(price, quantity, total) 18 | } 19 | } -------------------------------------------------------------------------------- /schema/testdata/proto/default_zero/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | published Boolean @default 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /schema/testdata/proto/delete_action/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | actions { 3 | delete deletePerson(id) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /schema/testdata/proto/delete_operation_unique_lookup_in_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | isLocked Boolean 4 | } 5 | 6 | actions { 7 | delete deleteMyModel(id) { 8 | @where(myModel.isLocked == false) 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /schema/testdata/proto/delete_operation_unique_lookup_in_wheres/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | isLocked Boolean 4 | code Text @unique 5 | } 6 | 7 | actions { 8 | delete deleteMyModel(explicitCode: Text) { 9 | @where(myModel.isLocked == false) 10 | @where(myModel.code == explicitCode) 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /schema/testdata/proto/empty_fields/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields {} 3 | } 4 | -------------------------------------------------------------------------------- /schema/testdata/proto/enum_default/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | type PostType @default(PostType.Draft) 4 | } 5 | } 6 | 7 | enum PostType { 8 | Draft 9 | Published 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/proto/enums/schema.keel: -------------------------------------------------------------------------------- 1 | enum Planets { 2 | Mercury 3 | Venus 4 | Earth 5 | Mars 6 | Jupiter 7 | Saturn 8 | Uranus 9 | Neptune 10 | } 11 | 12 | enum BlackHoles { 13 | SaggittariusA 14 | Ton_618 15 | NGC_4468B 16 | S5_0014plus81 17 | ic_1459 18 | } -------------------------------------------------------------------------------- /schema/testdata/proto/environment_variables/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: FOO 3 | value: 'bar' 4 | -------------------------------------------------------------------------------- /schema/testdata/proto/environment_variables/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | // FOO is a default environment variable 3 | @permission(expression: ctx.env.FOO == "d", actions: [get]) 4 | } 5 | -------------------------------------------------------------------------------- /schema/testdata/proto/expression_enum_field_comparison/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | type PostType 4 | differentType PostType 5 | } 6 | 7 | actions { 8 | get getPost(id) { 9 | @permission(expression: post.type == post.differentType) 10 | } 11 | } 12 | } 13 | 14 | enum PostType { 15 | Bar 16 | Foo 17 | } 18 | -------------------------------------------------------------------------------- /schema/testdata/proto/expression_enum_set/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | status PostStatus 4 | } 5 | 6 | actions { 7 | update published(id) { 8 | @set(post.status = PostStatus.Published) 9 | } 10 | } 11 | } 12 | 13 | enum PostStatus { 14 | Published 15 | Draft 16 | } -------------------------------------------------------------------------------- /schema/testdata/proto/expression_nullish_optional_comparison/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | identity Identity? 4 | } 5 | 6 | actions { 7 | create createPost() { 8 | @set(post.identity = ctx.identity) 9 | @permission(expression: post.identity == null) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/proto/expressions_compare_model_operand/schema.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | identity Identity @unique 4 | } 5 | } 6 | model BankAccount { 7 | fields { 8 | identity Identity @unique 9 | } 10 | actions { 11 | get getBankAccount(id) { 12 | @permission(expression: ctx.identity.user == bankAccount.identity.user) 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /schema/testdata/proto/expressions_null_values/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | title Text? 4 | } 5 | 6 | actions { 7 | update removeTitle(id) { 8 | @set(post.title = null) 9 | } 10 | list listPostsWithNoTitle() { 11 | @where(post.title == null) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/proto/field_array_of_same_message_type/schema.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Foo[] 3 | } 4 | 5 | model Person { 6 | actions { 7 | read getPerson(id) returns (Foo) 8 | } 9 | } 10 | 11 | message UnusedMessage { 12 | bar UnusedMessage[] 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/field_optional_of_same_message_type/schema.keel: -------------------------------------------------------------------------------- 1 | message Foo { 2 | bar Foo? 3 | } 4 | 5 | model Person { 6 | actions { 7 | read getPerson(id) returns (Foo) 8 | } 9 | } 10 | 11 | message UnusedMessage { 12 | bar UnusedMessage? 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/field_optional_of_same_model_type/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | mother Person? @relation(children) 4 | children Person[] 5 | } 6 | 7 | actions { 8 | create createPerson() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/proto/fields_attributes/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | bar Text @unique 4 | baz Text? @default("foo") 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/proto/fields_names/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | name1 Text 4 | name2 Text 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/proto/fields_optional/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text? 4 | } 5 | } 6 | 7 | -------------------------------------------------------------------------------- /schema/testdata/proto/fields_types/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | a Identity 4 | b Text 5 | c Date 6 | d Timestamp 7 | e Boolean 8 | f Markdown 9 | g Beatles 10 | h Decimal 11 | i File 12 | j Duration 13 | } 14 | } 15 | 16 | enum Beatles { 17 | John 18 | Paul 19 | George 20 | Ringo 21 | } 22 | -------------------------------------------------------------------------------- /schema/testdata/proto/flow_fields/schema.keel: -------------------------------------------------------------------------------- 1 | flow MyWorkflow { 2 | inputs { 3 | id ID 4 | text Text 5 | number Number 6 | bool Boolean 7 | array Text[] 8 | enum MyEnum 9 | file File 10 | duration Duration 11 | thing Thing 12 | } 13 | } 14 | 15 | enum MyEnum { 16 | One 17 | Two 18 | } 19 | 20 | model Thing { } -------------------------------------------------------------------------------- /schema/testdata/proto/flow_permissions/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: FOO 3 | value: 'bar' 4 | -------------------------------------------------------------------------------- /schema/testdata/proto/flow_permissions/schema.keel: -------------------------------------------------------------------------------- 1 | flow MyFlow1 { 2 | } 3 | 4 | flow MyFlow2 { 5 | inputs { 6 | name Text 7 | } 8 | 9 | @permission(roles: [Admin]) 10 | } 11 | 12 | flow MyFlow3 { 13 | @permission(roles: [Admin, Developer]) 14 | } 15 | 16 | role Admin { 17 | domains { 18 | "keel.so" 19 | } 20 | } 21 | 22 | role Developer {} 23 | -------------------------------------------------------------------------------- /schema/testdata/proto/functions/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | actions { 3 | list operationA() 4 | } 5 | } -------------------------------------------------------------------------------- /schema/testdata/proto/functions_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | bar Text 4 | } 5 | 6 | actions { 7 | list operationA() @function 8 | create operationB() with (bar) @function 9 | create operationC() with (bar, baz: Text) @function 10 | create operationD() with (baz: Text) @function 11 | } 12 | } -------------------------------------------------------------------------------- /schema/testdata/proto/get_operation_unique_lookup_in_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | isActive Boolean 4 | } 5 | 6 | actions { 7 | get getMyModel(id) { 8 | @where(myModel.isActive == true) 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /schema/testdata/proto/get_operation_unique_lookup_in_wheres/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | isActive Boolean 4 | code Text @unique 5 | } 6 | 7 | actions { 8 | get getMyModel(explicitCode: Text) { 9 | @where(myModel.isActive == true) 10 | @where(myModel.code == explicitCode) 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /schema/testdata/proto/identity_backlinks/schema.keel: -------------------------------------------------------------------------------- 1 | model CompanyEmployee { 2 | fields { 3 | pIdentity Identity @unique 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /schema/testdata/proto/identity_backlinks_relation_attribute/schema.keel: -------------------------------------------------------------------------------- 1 | model CompanyEmployee { 2 | fields { 3 | abcIdentity Identity { 4 | @unique 5 | @relation(abcEmployee) 6 | } 7 | xyzIdentity Identity { 8 | @unique 9 | @relation(xyzEmployee) 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/proto/identity_claims/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | auth: 2 | claims: 3 | - key: myClaim 4 | field: myCustomClaim 5 | - key: https://slack.com/claims/#teamId 6 | field: teamId1 -------------------------------------------------------------------------------- /schema/testdata/proto/identity_claims/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { } 2 | 3 | api Admin { 4 | models { 5 | Author 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /schema/testdata/proto/job_adhoc/schema.keel: -------------------------------------------------------------------------------- 1 | job MyJob { 2 | inputs { 3 | veryImportantValue Text 4 | } 5 | 6 | @permission(roles: [Admin]) 7 | } 8 | 9 | role Admin {} -------------------------------------------------------------------------------- /schema/testdata/proto/job_fields/schema.keel: -------------------------------------------------------------------------------- 1 | job MyJob { 2 | inputs { 3 | id ID 4 | text Text 5 | number Number 6 | bool Boolean 7 | array Text[] 8 | enum MyEnum 9 | } 10 | 11 | @permission(roles: [Admin]) 12 | } 13 | 14 | role Admin {} 15 | 16 | enum MyEnum { 17 | One 18 | Two 19 | } -------------------------------------------------------------------------------- /schema/testdata/proto/job_permissions_expressions/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | environment: 2 | - name: FOO 3 | value: 'bar' 4 | -------------------------------------------------------------------------------- /schema/testdata/proto/job_permissions_expressions/schema.keel: -------------------------------------------------------------------------------- 1 | job MyJob1 { 2 | @permission(expression: ctx.isAuthenticated) 3 | } 4 | 5 | job MyJob2 { 6 | @permission(expression: true) 7 | } 8 | 9 | job MyJob3 { 10 | @permission(expression: ctx.env.FOO == "bar") 11 | @permission(roles: [Admin]) 12 | } 13 | 14 | job MyJob4 { 15 | @permission(expression: ctx.env.FOO == "bar", roles: [Admin]) 16 | } 17 | 18 | role Admin {} -------------------------------------------------------------------------------- /schema/testdata/proto/job_permissions_roles/schema.keel: -------------------------------------------------------------------------------- 1 | job MyManualJob { 2 | @permission(roles: [Admin, Developer]) 3 | } 4 | 5 | job MyManualJobWithInputs { 6 | inputs { 7 | name Text 8 | } 9 | @permission(roles: [Admin]) 10 | } 11 | 12 | role Admin { 13 | domains { 14 | "keel.so" 15 | } 16 | } 17 | 18 | role Developer {} -------------------------------------------------------------------------------- /schema/testdata/proto/job_scheduled/schema.keel: -------------------------------------------------------------------------------- 1 | job UnixCron { 2 | @schedule("*/10 * * * 1-3") 3 | } 4 | 5 | job IntervalCron { 6 | @schedule("every 10 minutes") 7 | } 8 | 9 | job DayCron { 10 | @schedule("every monday at 9am") 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/message_enum_field/schema.keel: -------------------------------------------------------------------------------- 1 | message Input { 2 | sports Sport[] 3 | favouriteSport Sport? 4 | } 5 | 6 | message Response { 7 | sports Sport[] 8 | favouriteSport Sport? 9 | } 10 | 11 | model Person { 12 | fields { 13 | name Text 14 | } 15 | 16 | actions { 17 | write writeSportInterests(Input) returns (Response) 18 | } 19 | } 20 | 21 | enum Sport { 22 | Cricket 23 | Rugby 24 | Soccer 25 | } -------------------------------------------------------------------------------- /schema/testdata/proto/message_file_field/schema.keel: -------------------------------------------------------------------------------- 1 | message Input { 2 | data File 3 | } 4 | 5 | message Response { 6 | data File 7 | } 8 | 9 | model Person { 10 | actions { 11 | write writeFile(Input) returns (Response) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/message_type/schema.keel: -------------------------------------------------------------------------------- 1 | message MyCustomFunctionInput { 2 | titleSpecial Text 3 | } 4 | 5 | message MyCustomFunctionOutput { 6 | id ID 7 | title Text 8 | } 9 | 10 | model Post { 11 | fields { 12 | title Text 13 | } 14 | 15 | actions { 16 | write createPostSpecial(MyCustomFunctionInput) returns(MyCustomFunctionOutput) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /schema/testdata/proto/message_type_nested_model/schema.keel: -------------------------------------------------------------------------------- 1 | message MyCustomFunctionInput { 2 | post Post 3 | } 4 | 5 | message MyCustomFunctionOutput { 6 | post Post 7 | } 8 | 9 | model Post { 10 | fields { 11 | title Text 12 | } 13 | 14 | actions { 15 | write createPostSpecial(MyCustomFunctionInput) returns(MyCustomFunctionOutput) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /schema/testdata/proto/models_multiple/schema.keel: -------------------------------------------------------------------------------- 1 | model Model1 { 2 | } 3 | 4 | model Model2 { 5 | } 6 | -------------------------------------------------------------------------------- /schema/testdata/proto/models_unique/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | firstName Text 4 | lastName Text 5 | } 6 | 7 | @unique([firstName, lastName]) 8 | } 9 | -------------------------------------------------------------------------------- /schema/testdata/proto/operations_attr_set/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | f1 Boolean 4 | f2 Text 5 | f3 Text 6 | someId ID 7 | } 8 | actions { 9 | create createPost() with (f2, f3, someId) { 10 | @set(foo.f1 = true) 11 | } 12 | update updatePost1(id) with (f2) { 13 | @set(foo.f3 = f2) 14 | } 15 | update updatePost2(id) { 16 | @set(foo.someId = id) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /schema/testdata/proto/operations_attr_set_null/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | text Text? 4 | number Number? 5 | } 6 | actions { 7 | create createPost() { 8 | @set(foo.text = null) 9 | @set(foo.number = null) 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /schema/testdata/proto/operations_attr_validate/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | create createPerson() with (name) { 8 | @validate(name != "") 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/operations_attr_where/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | f1 Text @unique 4 | } 5 | 6 | actions { 7 | get operationA() { 8 | @where(foo.f1 == "foo") 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/operations_create_all_required_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | age Number? 5 | } 6 | 7 | actions { 8 | create createPerson() with (name) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /schema/testdata/proto/operations_create_mutate_id/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | create createPerson() with (id, name) 8 | 9 | create createPersonSet() with (id: ID, name) { 10 | @set(person.id = id) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/operations_get_unique_input/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | somethingUnique Text @unique 4 | notUnique Text 5 | } 6 | actions { 7 | get getMyModel(id) 8 | get getMyModelByUnique(somethingUnique) 9 | get getMyModelByUnique2(somethingUnique, notUnique) 10 | } 11 | } -------------------------------------------------------------------------------- /schema/testdata/proto/operations_get_unique_where/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | name Text { 4 | @unique 5 | } 6 | } 7 | 8 | actions { 9 | get getAuthor() { 10 | @where(author.name == "Bob") 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/operations_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | f1 Text 4 | f2 Number 5 | f3 File 6 | } 7 | 8 | actions { 9 | get opA(id) 10 | list opB(f1, f2) 11 | update opC(id) with (f1) 12 | create opD() with (f1, f2, f3) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/proto/operations_multiple/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | actions { 3 | list operationA() 4 | list operationB() 5 | } 6 | } -------------------------------------------------------------------------------- /schema/testdata/proto/operations_types/schema.keel: -------------------------------------------------------------------------------- 1 | model Foo { 2 | fields { 3 | name Text? 4 | } 5 | actions { 6 | create opA() 7 | update opB(id) with (name) 8 | get opC(id) 9 | list opD() 10 | } 11 | } -------------------------------------------------------------------------------- /schema/testdata/proto/optional_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | name Text 4 | preferredName Text? 5 | } 6 | 7 | actions { 8 | create createPerson() with (name, preferredName) 9 | update updatePerson(id) with (name, preferredName) 10 | list listPerson(name, preferredName) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/proto/permission_expression_with_identity/schema.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | identity Identity? @unique 4 | } 5 | 6 | actions { 7 | create createAccount() { 8 | @set(account.identity = ctx.identity) 9 | } 10 | } 11 | 12 | @permission( 13 | expression: account.identity == ctx.identity, 14 | actions: [create] 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/proto/relations_both_sides/schema.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | posts Post[] 4 | } 5 | } 6 | 7 | model Post { 8 | fields { 9 | author Account 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/relations_multi_uses_reln_attribute/schema.keel: -------------------------------------------------------------------------------- 1 | model Account { 2 | fields { 3 | favourite Post @relation(favouritedBy) 4 | authoredPosts Post[] 5 | reviewedPosts Post[] 6 | } 7 | } 8 | 9 | model Post { 10 | fields { 11 | reviewer Account @relation(reviewedPosts) 12 | favouritedBy Account[] 13 | author Account @relation(authoredPosts) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /schema/testdata/proto/relations_single/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | } 3 | 4 | model Book { 5 | fields { 6 | author Person 7 | } 8 | } -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_mixed/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | written Post[] 4 | reviewed Post 5 | starPost Post @unique 6 | } 7 | } 8 | 9 | model Post { 10 | fields { 11 | author Author @relation(written) 12 | reviewedBy Author[] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_mixed_one_to_many_single_sided/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | written Post @relation(author) 4 | coWritten Post @relation(coAuthor) 5 | reviewed Post 6 | } 7 | } 8 | 9 | model Post { 10 | fields { 11 | author Author[] 12 | coAuthor Author[] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_mixed_one_to_one_single_sided/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | written Post @relation(author) 4 | coWritten Post @relation(coAuthor) 5 | reviewed Post @unique 6 | } 7 | } 8 | 9 | model Post { 10 | fields { 11 | author Author[] 12 | coAuthor Author[] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_mixed_one_to_one_single_sided_2/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | written Post @relation(author) 4 | coWritten Post @relation(coAuthor) 5 | } 6 | } 7 | 8 | model Post { 9 | fields { 10 | reviewed Author @unique 11 | author Author[] 12 | coAuthor Author[] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_many_multi_one_sided/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | author Author 4 | } 5 | } 6 | 7 | model Author { 8 | fields { 9 | post Post 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_many_multi_one_sided_same_side/schema.keel: -------------------------------------------------------------------------------- 1 | model Post { 2 | fields { 3 | author Author 4 | coAuthor Author 5 | } 6 | } 7 | 8 | model Author { 9 | } 10 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_many_single_sided_multiple/schema.keel: -------------------------------------------------------------------------------- 1 | model Author { 2 | fields { 3 | topPost Post 4 | worstPost Post 5 | } 6 | } 7 | 8 | model Post { 9 | fields { 10 | authoredBy Author 11 | coAuthoredBy Author 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one/schema.keel: -------------------------------------------------------------------------------- 1 | model Company { 2 | fields { 3 | profile CompanyProfile @unique 4 | } 5 | 6 | actions { 7 | create createCompany() with (profile.employeeCount) 8 | } 9 | } 10 | 11 | model CompanyProfile { 12 | fields { 13 | employeeCount Number 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one_both_directions/schema.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | student Student { 4 | @unique 5 | @relation(user) 6 | } 7 | student2 Student 8 | } 9 | } 10 | 11 | model Student { 12 | fields { 13 | user User 14 | user2 User { 15 | @unique 16 | @relation(student2) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one_both_directions_missing_relation_attribute/schema.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | student Student { 4 | @unique 5 | @relation(user) 6 | } 7 | student2 Student 8 | } 9 | } 10 | 11 | model Student { 12 | fields { 13 | user User 14 | user2 User @unique 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one_both_directions_no_relation_attribute/schema.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | student Student @unique 4 | student2 Student 5 | } 6 | } 7 | 8 | model Student { 9 | fields { 10 | user User 11 | user2 User @unique 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one_both_sides/schema.keel: -------------------------------------------------------------------------------- 1 | model Company { 2 | fields { 3 | profile CompanyProfile @unique 4 | } 5 | 6 | actions { 7 | create createCompany() with (profile.employeeCount) 8 | } 9 | } 10 | 11 | model CompanyProfile { 12 | fields { 13 | employeeCount Number 14 | company Company 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one_missing_reln/schema.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | student Student { 4 | @unique 5 | @relation(user) 6 | } 7 | student2 Student @unique 8 | } 9 | } 10 | 11 | model Student { 12 | fields { 13 | user User 14 | user2 User 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one_unique_both_sides/schema.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | student Student @unique 4 | } 5 | } 6 | 7 | model Student { 8 | fields { 9 | user User @unique 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_one_to_one_with_unique/schema.keel: -------------------------------------------------------------------------------- 1 | model User { 2 | fields { 3 | identity Identity @unique 4 | student Student? 5 | } 6 | } 7 | 8 | model Student { 9 | fields { 10 | user User @unique 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_self_referencing_multiple/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | bestFriend Person? @relation(bestFriendsOf) 4 | bestFriendsOf Person[] 5 | mother Person? @relation(children) 6 | children Person[] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_self_referencing_no_reln_attribute/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | children Person[] 4 | mother Person? 5 | } 6 | } 7 | 8 | model Person2 { 9 | fields { 10 | children Person2[] 11 | mother Person2? 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/relationships_self_referencing_uses_reln_attribute/schema.keel: -------------------------------------------------------------------------------- 1 | model Person { 2 | fields { 3 | children Person[] 4 | mother Person? @relation(children) 5 | } 6 | } 7 | 8 | model Person2 { 9 | fields { 10 | children Person2[] 11 | mother Person2? @relation(children) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /schema/testdata/proto/request_headers_expression/schema.keel: -------------------------------------------------------------------------------- 1 | model ApiKey { 2 | fields { 3 | key Text 4 | } 5 | 6 | actions { 7 | create createApiKey() { 8 | @set(apiKey.key = ctx.headers.X_API_TOKEN) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /schema/testdata/proto/roles/schema.keel: -------------------------------------------------------------------------------- 1 | role Admin { 2 | domains { 3 | "domain1.com" 4 | "domain2.zyz" 5 | } 6 | 7 | emails { 8 | "person1@bbc.com" 9 | "person2@keel.xyz" 10 | } 11 | } 12 | 13 | role Other { 14 | } -------------------------------------------------------------------------------- /schema/testdata/proto/route_functions/schema.keel: -------------------------------------------------------------------------------- 1 | routes { 2 | get("/my/route", myHandler) 3 | post("/some/webhook/:param", otherHandler) 4 | } -------------------------------------------------------------------------------- /schema/testdata/proto/secrets/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | secrets: 2 | - name: TEST_SECRET 3 | -------------------------------------------------------------------------------- /schema/testdata/proto/secrets/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | @permission( 3 | expression: ctx.secrets.TEST_SECRET == "d", 4 | actions: [get] 5 | ) 6 | } 7 | -------------------------------------------------------------------------------- /schema/testdata/proto/unique_attribute/schema.keel: -------------------------------------------------------------------------------- 1 | model Product { 2 | fields { 3 | name Text 4 | publisher Publisher 5 | } 6 | 7 | @unique([name, publisher]) 8 | } 9 | 10 | model Publisher { } 11 | -------------------------------------------------------------------------------- /schema/testdata/proto/unique_lookup_model/schema.keel: -------------------------------------------------------------------------------- 1 | model BankAccount { 2 | fields { 3 | identity Identity { 4 | @unique 5 | @relation(account) 6 | } 7 | } 8 | 9 | actions { 10 | get getBankAccount() { 11 | @where(bankAccount == ctx.identity.account) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /schema/testdata/proto/update_operation_unique_lookup_in_inputs/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | isActive Boolean 4 | } 5 | 6 | actions { 7 | get updateMyModel(id) { 8 | @where(myModel.isActive == true) 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /schema/testdata/proto/update_operation_unique_lookup_in_wheres/schema.keel: -------------------------------------------------------------------------------- 1 | model MyModel { 2 | fields { 3 | isActive Boolean 4 | code Text @unique 5 | } 6 | 7 | actions { 8 | update updateMyModel(explicitCode: Text) { 9 | @where(myModel.isActive == true) 10 | @where(myModel.code == explicitCode) 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /schema/validation/rules/rules.go: -------------------------------------------------------------------------------- 1 | package rules 2 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import (fetchTarball 2 | "https://github.com/NixOS/nixpkgs/archive/4ae2e647537bcdbb82265469442713d066675275.tar.gz") 3 | { } }: 4 | 5 | pkgs.mkShell { 6 | buildInputs = [ 7 | pkgs.go_1_23 8 | pkgs.golangci-lint 9 | pkgs.nodejs-18_x 10 | pkgs.nodePackages_latest.pnpm 11 | pkgs.protobuf 12 | pkgs.protoc-gen-go 13 | pkgs.nixfmt 14 | pkgs.goreleaser 15 | ]; 16 | } 17 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import ( 7 | _ "github.com/jonbretman/gotestpretty" 8 | _ "github.com/twitchtv/twirp/protoc-gen-twirp" 9 | _ "google.golang.org/protobuf/cmd/protoc-gen-go" 10 | ) 11 | -------------------------------------------------------------------------------- /tools/testdata/config_use_api/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | console: 2 | api: Console -------------------------------------------------------------------------------- /tools/testdata/config_use_api_case_insensitive/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | console: 2 | api: console -------------------------------------------------------------------------------- /tools/testdata/config_use_api_case_insensitive/schema.keel: -------------------------------------------------------------------------------- 1 | model Category { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | get getCategory(id) 8 | delete deleteCategory(id) 9 | list listCategories() 10 | } 11 | } 12 | 13 | api Console { 14 | models { 15 | Category { 16 | actions { 17 | getCategory 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /tools/testdata/config_use_api_case_insensitive/tools.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tools/testdata/config_use_api_does_not_exist/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | console: 2 | api: none -------------------------------------------------------------------------------- /tools/testdata/config_use_api_does_not_exist/schema.keel: -------------------------------------------------------------------------------- 1 | model Category { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | get getCategory(id) 8 | delete deleteCategory(id) 9 | list listCategories() 10 | } 11 | } -------------------------------------------------------------------------------- /tools/testdata/config_use_api_does_not_exist/tools.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tools/testdata/entry_activity_actions/schema.keel: -------------------------------------------------------------------------------- 1 | model BlogPost { 2 | actions { 3 | get getBlogPost(id) 4 | list listBlogPosts() 5 | create createBlogPost() 6 | update updateBlogPost(id) 7 | update deactivateBlogPost(id) 8 | delete deleteBlogPost(id) 9 | write cleanComments(id) returns (Any) 10 | read readComments(id) returns (Any) 11 | } 12 | } -------------------------------------------------------------------------------- /tools/testdata/facets/schema.keel: -------------------------------------------------------------------------------- 1 | model Order { 2 | fields { 3 | price Decimal 4 | quantity Number 5 | category Category 6 | durationToOrder Duration 7 | } 8 | actions { 9 | list listOrders() { 10 | @facet(price, quantity, category, durationToOrder, createdAt) 11 | } 12 | } 13 | } 14 | 15 | enum Category { 16 | Electronics 17 | Books 18 | Clothing 19 | } 20 | 21 | -------------------------------------------------------------------------------- /tools/testdata/flows/schema.keel: -------------------------------------------------------------------------------- 1 | flow MyFlow { 2 | inputs { 3 | decimal Decimal 4 | number Number 5 | text Text 6 | boolean Boolean 7 | duration Duration 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tools/testdata/input_get_entry_actions_with_relationships/schema.keel: -------------------------------------------------------------------------------- 1 | model Product { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | get getProduct(id) 8 | } 9 | } 10 | 11 | model Sale { 12 | fields { 13 | product Product 14 | } 15 | 16 | actions { 17 | create createSale() with (product.id) 18 | list listSales(product.id) 19 | } 20 | } -------------------------------------------------------------------------------- /tools/testdata/input_lookup_actions/schema.keel: -------------------------------------------------------------------------------- 1 | 2 | 3 | model Product { 4 | fields { 5 | name Text 6 | sku Text @unique 7 | } 8 | 9 | actions { 10 | list listProducts() 11 | 12 | update updateProduct(id) 13 | write writeProductFunc(id) returns (Any) 14 | read readProductFunc(id) returns (Any) 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /tools/testdata/input_lookup_actions_with_relationships/schema.keel: -------------------------------------------------------------------------------- 1 | model Product { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | get getProduct(id) 8 | } 9 | } 10 | 11 | model Sale { 12 | fields { 13 | product Product 14 | } 15 | 16 | actions { 17 | create createSale() with (product.id) 18 | list listSales(product.id) 19 | } 20 | } -------------------------------------------------------------------------------- /tools/testdata/one_to_many_links/schema.keel: -------------------------------------------------------------------------------- 1 | model Order { 2 | fields { 3 | items LineItem[] 4 | } 5 | 6 | actions { 7 | get getOrder(id) 8 | list listOrders(items.id) 9 | } 10 | } 11 | 12 | model LineItem { 13 | fields { 14 | order Order 15 | product Text 16 | } 17 | 18 | actions { 19 | list listLineItems(order.id) 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /tools/testdata/with_api_definition_no_config/schema.keel: -------------------------------------------------------------------------------- 1 | model Category { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | get getCategory(id) 8 | delete deleteCategory(id) 9 | list listCategories() 10 | } 11 | } 12 | 13 | api Console { 14 | models { 15 | Category { 16 | actions { 17 | getCategory 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /tools/testdata/without_default_api_no_config/keelconfig.yaml: -------------------------------------------------------------------------------- 1 | useDefaultApi: false -------------------------------------------------------------------------------- /tools/testdata/without_default_api_no_config/schema.keel: -------------------------------------------------------------------------------- 1 | model Category { 2 | fields { 3 | name Text 4 | } 5 | 6 | actions { 7 | get getCategory(id) 8 | delete deleteCategory(id) 9 | list listCategories() 10 | } 11 | } -------------------------------------------------------------------------------- /tools/testdata/without_default_api_no_config/tools.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ES2016"], 4 | "target": "ES2016", 5 | "esModuleInterop": true, 6 | "moduleResolution": "node", 7 | "skipLibCheck": true, 8 | "strictNullChecks": true, 9 | "types": ["node"], 10 | "allowJs": true 11 | }, 12 | "include": ["**/*.ts"], 13 | "exclude": ["node_modules"] 14 | } -------------------------------------------------------------------------------- /util/str.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | func PadRight(str string, padAmount int) string { 4 | for len(str) < padAmount { 5 | str += " " 6 | } 7 | 8 | return str 9 | } 10 | --------------------------------------------------------------------------------