Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
69bb557
Add test definition
kafkas May 26, 2024
ff44e26
Generate json schema for local use
kafkas May 26, 2024
bad867e
Use local schema everywhere
kafkas May 26, 2024
1d0e965
Rename Validator to TypeValidator
kafkas May 26, 2024
7fa2100
Update definition and rules
kafkas May 27, 2024
10d3aa6
Update type validator
kafkas May 27, 2024
bbd11b6
Add missing validator
kafkas May 27, 2024
8455097
Fixes
kafkas May 27, 2024
0a5cae6
Init read-only field validator declarations
kafkas May 27, 2024
ec90d26
Fix
kafkas May 27, 2024
4d1425d
Generate rules predicate in generator
kafkas May 27, 2024
d159854
Make sure renderer has no other responsibilities
kafkas May 27, 2024
6010c5e
Fix tests
kafkas May 27, 2024
f524265
Rename `validatorNamePattern` to `typeValidatorNamePattern`
kafkas May 27, 2024
ff79b59
Add changeset
kafkas May 27, 2024
10aaab7
Rename `validatorParamName` to `typeValidatorParamName`
kafkas May 27, 2024
f2eb975
Add changeset
kafkas May 27, 2024
1fcfaa5
Change generation structure
kafkas May 27, 2024
814c9cf
Rename methods
kafkas May 27, 2024
a7a8792
Implement ReadonlyFieldValidatorPredicate
kafkas May 27, 2024
629868a
Generate readonly field validator
kafkas May 27, 2024
0f69518
Fixes
kafkas May 27, 2024
8dfa44e
Fix
kafkas May 27, 2024
58cc7e8
Implement `MapDiffHasAffectedKeysPredicate`
kafkas May 27, 2024
c50b5c3
Commit desired output
kafkas May 27, 2024
8246adc
Fix
kafkas May 27, 2024
fe052a6
Vertical alignment for 'or' predicate
kafkas May 28, 2024
a655284
Implement config options
kafkas May 31, 2024
3a8dd7a
Update tests
kafkas May 31, 2024
d828f07
Better organized predicates
kafkas Jun 1, 2024
04ee435
Implement `typeHasReadonlyField` func
kafkas Jun 1, 2024
dec0a6b
Readonly field predicate creators
kafkas Jun 2, 2024
b29cbee
Init tests for readonly field predicates
kafkas Jun 2, 2024
066519d
Fix `readonlyFieldPredicateForObjectType`
kafkas Jun 2, 2024
0c10276
Add ReferencePredicate
kafkas Jun 4, 2024
05fb989
Init discriminated union predicates
kafkas Jun 4, 2024
53aa1be
Add user pet to test definition
kafkas Jun 10, 2024
7affe82
Fix incorrect filtering
kafkas Jun 18, 2024
4182c79
Update firestore.rules
kafkas Jun 18, 2024
b3fe8dc
Change expected generation
kafkas Jun 21, 2024
1c0a82f
Disable tests
kafkas Jun 24, 2024
ab7db6c
Add todo
kafkas Jun 24, 2024
1e8b79b
Add another todo
kafkas Jun 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/selfish-cows-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"typesync-cli": minor
---

[BREAKING] Renamed the `validatorNamePattern` option for the `generate-rules` command to `typeValidatorNamePattern`.
5 changes: 5 additions & 0 deletions .changeset/wicked-goats-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"typesync-cli": minor
---

[BREAKING] Renamed the `validatorParamName` option for the `generate-rules` command to `typeValidatorParamName`.
7 changes: 4 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ jobs:
- run:
name: Run rests for the source code
command: yarn test:src
- run:
name: Run Security Rules tests
command: yarn test:rules
# TODO: Implement
# - run:
# name: Run Security Rules tests
# command: yarn test:rules

publish-npm-package:
executor: node/default
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,12 @@
"format-check:src": "prettier \"src/**/*.(js|ts|json)\" --check",
"format:package-json": "sort-package-json \"package.json\"",
"format:src": "prettier --write \"src/**/*.(js|ts|json)\"",
"generate-json-schema": "tsx scripts/generate-json-schema",
"generate-json-schema": "tsx scripts/generate-json-schema current-version",
"generate-json-schema-local": "tsx scripts/generate-json-schema local",
"lint": "eslint src --ext .ts",
"test": "yarn run test:compile && yarn run test:src --verbose && yarn run test:rules",
"test:compile": "tsc -p tsconfig.test.json",
"test:rules": "firebase emulators:exec --project demo-rules --only firestore 'jest --testMatch \"<rootDir>/tests/security/**/*.test.ts\"'",
"test:rules": "tsx src/cli/index.tsx generate-rules --definition tests/security/definition.yml --outFile tests/security/firestore.rules && firebase emulators:exec --project demo-rules --only firestore 'jest --testMatch \"<rootDir>/tests/security/**/*.test.ts\"'",
"test:src": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
"version": "changeset version && yarn run generate-json-schema"
},
Expand Down
459 changes: 459 additions & 0 deletions schema.local.json

Large diffs are not rendered by default.

30 changes: 24 additions & 6 deletions scripts/generate-json-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { format } from 'prettier';
import { zodToJsonSchema } from 'zod-to-json-schema';

import { definition } from '../src/definition/index.js';
import { assert } from '../src/util/assert.js';
import { assert, assertNever } from '../src/util/assert.js';
import { extractPackageJsonVersion } from '../src/util/extract-package-json-version.js';
import { getDirName, writeFile } from '../src/util/fs.js';

Expand All @@ -14,8 +14,8 @@ function inferCurrentSchemaVersion() {
return `v${major}.${minor}`;
}

function generateJsonSchema(minorVersionName: string) {
return zodToJsonSchema(definition.zodSchema, minorVersionName);
function generateJsonSchema(name: string) {
return zodToJsonSchema(definition.zodSchema, name);
}

type JsonSchema = ReturnType<typeof generateJsonSchema>;
Expand All @@ -30,9 +30,27 @@ async function writeJsonSchemaToFile(jsonSchema: JsonSchema, outPath: string) {
}

async function main() {
const minorVersionName = inferCurrentSchemaVersion();
const jsonSchema = generateJsonSchema(minorVersionName);
const pathToOutputFile = resolve(getDirName(import.meta.url), `../public/${minorVersionName}.json`);
const [, , type] = process.argv;
assert(
type === 'current-version' || type === 'local',
`The 'type' argument for generate-json-schema must be one of 'current-version' or 'local'.`
);

let schemaName: string | undefined;
let pathToOutputFile: string | undefined;

if (type === 'current-version') {
const minorVersionName = inferCurrentSchemaVersion();
schemaName = minorVersionName;
pathToOutputFile = resolve(getDirName(import.meta.url), `../public/${minorVersionName}.json`);
} else if (type === 'local') {
schemaName = 'local';
pathToOutputFile = resolve(getDirName(import.meta.url), `../schema.local.json`);
} else {
assertNever(type);
}

const jsonSchema = generateJsonSchema(schemaName);
await writeJsonSchemaToFile(jsonSchema, pathToOutputFile);
}

Expand Down
7 changes: 5 additions & 2 deletions src/api/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import { GenerateRepresentationResult } from './_common.js';

export interface GenerateRulesRepresentationOptions {
definition: string;
typeValidatorNamePattern?: string;
typeValidatorParamName?: string;
readonlyFieldValidatorNamePattern?: string;
readonlyFieldValidatorPrevDataParamName?: string;
readonlyFieldValidatorNextDataParamName?: string;
debug?: boolean;
}

export interface GenerateRulesOptions extends GenerateRulesRepresentationOptions {
outFile: string;
startMarker?: string;
endMarker?: string;
validatorNamePattern?: string;
validatorParamName?: string;
indentation?: number;
}

Expand Down
55 changes: 43 additions & 12 deletions src/cli/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,20 @@ import {
DEFAULT_RULES_DEBUG,
DEFAULT_RULES_END_MARKER,
DEFAULT_RULES_INDENTATION,
DEFAULT_RULES_READONLY_FIELD_VALIDATOR_NAME_PATTERN,
DEFAULT_RULES_READONLY_FIELD_VALIDATOR_NEXT_DATA_PARAM_NAME,
DEFAULT_RULES_READONLY_FIELD_VALIDATOR_PREV_DATA_PARAM_NAME,
DEFAULT_RULES_START_MARKER,
DEFAULT_RULES_VALIDATOR_NAME_PATTERN,
DEFAULT_RULES_VALIDATOR_PARAM_NAME,
DEFAULT_RULES_TYPE_VALIDATOR_NAME_PATTERN,
DEFAULT_RULES_TYPE_VALIDATOR_PARAM_NAME,
DEFAULT_SWIFT_DEBUG,
DEFAULT_SWIFT_INDENTATION,
DEFAULT_TS_DEBUG,
DEFAULT_TS_INDENTATION,
DEFAULT_TS_OBJECT_TYPE_FORMAT,
DEFAULT_VALIDATE_DEBUG,
RULES_VALIDATOR_NAME_PATTERN_PARAM,
RULES_READONLY_FIELD_VALIDATOR_NAME_PATTERN_PARAM,
RULES_TYPE_VALIDATOR_NAME_PATTERN_PARAM,
} from '../constants.js';
import { extractErrorMessage } from '../util/extract-error-message.js';
import { extractPackageJsonVersion } from '../util/extract-package-json-version.js';
Expand Down Expand Up @@ -263,17 +267,38 @@ await yargs(hideBin(process.argv))
demandOption: false,
default: DEFAULT_RULES_END_MARKER,
})
.option('validatorNamePattern', {
describe: `The pattern that specifies how the validators are named. The string must contain the '${RULES_VALIDATOR_NAME_PATTERN_PARAM}' substring (this is a literal value). For example, providing 'isValid${RULES_VALIDATOR_NAME_PATTERN_PARAM}' ensures that the generated validators are given names like 'isValidUser', 'isValidProject' etc.`,
.option('typeValidatorNamePattern', {
describe: `The pattern that specifies how the generated type validators are named. The pattern must be a string that contains the '${RULES_TYPE_VALIDATOR_NAME_PATTERN_PARAM}' substring (this is a literal value). For example, providing 'isValid${RULES_TYPE_VALIDATOR_NAME_PATTERN_PARAM}' ensures that the generated validators are given names like 'isValidUser', 'isValidProject' etc.`,
type: 'string',
demandOption: false,
default: DEFAULT_RULES_VALIDATOR_NAME_PATTERN,
default: DEFAULT_RULES_TYPE_VALIDATOR_NAME_PATTERN,
})
.option('validatorParamName', {
.option('typeValidatorParamName', {
describe: 'The name of the parameter taken by each type validator.',
type: 'string',
demandOption: false,
default: DEFAULT_RULES_VALIDATOR_PARAM_NAME,
default: DEFAULT_RULES_TYPE_VALIDATOR_PARAM_NAME,
})
// TODO: Implement
// .option('readonlyFieldValidatorNamePattern', {
// describe: `The pattern that specifies how the generated readonly field validators are named. The pattern must be a string that contains the '${RULES_READONLY_FIELD_VALIDATOR_NAME_PATTERN_PARAM}' substring (this is a literal value). For example, providing 'isReadonlyFieldAffectedFor${RULES_READONLY_FIELD_VALIDATOR_NAME_PATTERN_PARAM}' ensures that the generated validators are given names like 'isReadonlyFieldAffectedForUser', 'isReadonlyFieldAffectedForProject' etc.`,
// type: 'string',
// demandOption: false,
// default: DEFAULT_RULES_READONLY_FIELD_VALIDATOR_NAME_PATTERN,
// })
// .option('readonlyFieldValidatorPrevDataParamName', {
// describe:
// 'The name of the first parameter taken by each readonly field validator representing previous data. This parameter used when computing the diff between next data and previous data to determine whether a readonly field has been affected by a write.',
// type: 'string',
// demandOption: false,
// default: DEFAULT_RULES_READONLY_FIELD_VALIDATOR_PREV_DATA_PARAM_NAME,
// })
.option('readonlyFieldValidatorNextDataParamName', {
describe:
'The name of the second parameter taken by each readonly field validator representing next data. This parameter used when computing the diff between next data and previous data to determine whether a readonly field has been affected by a write.',
type: 'string',
demandOption: false,
default: DEFAULT_RULES_READONLY_FIELD_VALIDATOR_NEXT_DATA_PARAM_NAME,
})
.option('indentation', {
describe: 'Indentation or tab width for the generated code.',
Expand All @@ -293,8 +318,11 @@ await yargs(hideBin(process.argv))
outFile,
startMarker,
endMarker,
validatorNamePattern,
validatorParamName,
typeValidatorNamePattern,
typeValidatorParamName,
readonlyFieldValidatorNamePattern,
readonlyFieldValidatorPrevDataParamName,
readonlyFieldValidatorNextDataParamName,
indentation,
debug,
} = args;
Expand All @@ -306,8 +334,11 @@ await yargs(hideBin(process.argv))
outFile: pathToOutputFile,
startMarker,
endMarker,
validatorNamePattern,
validatorParamName,
typeValidatorNamePattern,
typeValidatorParamName,
// readonlyFieldValidatorNamePattern,
// readonlyFieldValidatorPrevDataParamName,
// readonlyFieldValidatorNextDataParamName,
indentation,
debug,
});
Expand Down
10 changes: 7 additions & 3 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import type { SchemaGraphOrientation } from './api/graph.js';
import type { TSObjectTypeFormat } from './api/ts.js';

export const PYTHON_UNDEFINED_SENTINEL_CLASS = 'TypesyncUndefined';
export const RULES_VALIDATOR_NAME_PATTERN_PARAM = '{modelName}';
export const RULES_TYPE_VALIDATOR_NAME_PATTERN_PARAM = '{modelName}';
export const RULES_READONLY_FIELD_VALIDATOR_NAME_PATTERN_PARAM = '{modelName}';

/*
* Default values
Expand All @@ -21,8 +22,11 @@ export const DEFAULT_PY_DEBUG = false;

export const DEFAULT_RULES_START_MARKER = 'typesync-start';
export const DEFAULT_RULES_END_MARKER = 'typesync-end';
export const DEFAULT_RULES_VALIDATOR_NAME_PATTERN = `isValid${RULES_VALIDATOR_NAME_PATTERN_PARAM}`;
export const DEFAULT_RULES_VALIDATOR_PARAM_NAME = 'data';
export const DEFAULT_RULES_TYPE_VALIDATOR_NAME_PATTERN = `isValid${RULES_TYPE_VALIDATOR_NAME_PATTERN_PARAM}`;
export const DEFAULT_RULES_TYPE_VALIDATOR_PARAM_NAME = 'data';
export const DEFAULT_RULES_READONLY_FIELD_VALIDATOR_NAME_PATTERN = `isReadonlyFieldAffectedFor${RULES_TYPE_VALIDATOR_NAME_PATTERN_PARAM}`;
export const DEFAULT_RULES_READONLY_FIELD_VALIDATOR_PREV_DATA_PARAM_NAME = 'prevData';
export const DEFAULT_RULES_READONLY_FIELD_VALIDATOR_NEXT_DATA_PARAM_NAME = 'nextData';
export const DEFAULT_RULES_INDENTATION = 2;
export const DEFAULT_RULES_DEBUG = false;

Expand Down
2 changes: 1 addition & 1 deletion src/core/__tests__/definitions/bad-field.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"bad": "../../../../public/v0.9.json",
"bad": "../../../../schema.local.json",
"Username": {
"model": "alias",
"type": "string"
Expand Down
2 changes: 1 addition & 1 deletion src/core/__tests__/definitions/with-$schema-key.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "../../../../public/v0.9.json",
"$schema": "../../../../schema.local.json",
"Username": {
"model": "alias",
"type": "string"
Expand Down
2 changes: 1 addition & 1 deletion src/core/__tests__/definitions/yaml/alias.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# yaml-language-server: $schema=../../../../../public/v0.9.json
# yaml-language-server: $schema=../../../../../schema.local.json

Username:
model: alias
Expand Down
2 changes: 1 addition & 1 deletion src/core/__tests__/definitions/yaml/document.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# yaml-language-server: $schema=../../../../../public/v0.9.json
# yaml-language-server: $schema=../../../../../schema.local.json

UserProfile:
model: document
Expand Down
Loading