jinaga-server 3.7.3 → 3.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
24
24
|
steps:
|
|
25
25
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
|
26
|
-
- uses: actions/checkout@
|
|
26
|
+
- uses: actions/checkout@v7
|
|
27
27
|
|
|
28
28
|
- name: Build and Test
|
|
29
29
|
run: |
|
|
@@ -11,13 +11,15 @@ jobs:
|
|
|
11
11
|
contents: read
|
|
12
12
|
id-token: write
|
|
13
13
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
15
|
-
- uses: actions/setup-node@
|
|
14
|
+
- uses: actions/checkout@v7
|
|
15
|
+
- uses: actions/setup-node@v7
|
|
16
16
|
with:
|
|
17
|
-
node-version: '
|
|
17
|
+
node-version: '24.x'
|
|
18
18
|
registry-url: 'https://registry.npmjs.org'
|
|
19
|
-
- run: npm
|
|
20
|
-
|
|
21
|
-
-
|
|
19
|
+
- run: npm ci
|
|
20
|
+
# Force NODE_AUTH_TOKEN empty so .npmrc has no static token and npm
|
|
21
|
+
# falls through to OIDC trusted publishing. setup-node defaults this
|
|
22
|
+
# to GITHUB_TOKEN, which the npm registry rejects (masked as 404).
|
|
23
|
+
- run: npm publish --provenance
|
|
22
24
|
env:
|
|
23
25
|
NODE_AUTH_TOKEN: ''
|
|
@@ -14,11 +14,11 @@ jobs:
|
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
18
|
|
|
19
|
-
- uses: actions/setup-node@
|
|
19
|
+
- uses: actions/setup-node@v7
|
|
20
20
|
with:
|
|
21
|
-
node-version: '
|
|
21
|
+
node-version: '24.x'
|
|
22
22
|
|
|
23
23
|
- name: Get versions
|
|
24
24
|
id: versions
|
package/package.json
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { buildFeeds, dehydrateReference, getAllFactTypes, getAllRoles, Specification, SpecificationParser } from "jinaga";
|
|
2
|
+
|
|
3
|
+
import { addFactType, addRole, emptyFactTypeMap, emptyRoleMap, FactTypeMap, getFactTypeId, RoleMap } from "../../src/postgres/maps";
|
|
4
|
+
import { sqlFromFeed } from "../../src/postgres/specification-sql";
|
|
5
|
+
|
|
6
|
+
// Regression coverage for jinaga-server#179.
|
|
7
|
+
//
|
|
8
|
+
// The issue named two candidate loci for a `Jinaga.User`-given feed returning
|
|
9
|
+
// empty despite matching data: the distribution decision (jinaga.js) and the
|
|
10
|
+
// Postgres feed SQL (this repo, `sqlFromFeed`). These tests pin down the
|
|
11
|
+
// second: the feed SQL generated for a specification whose given is typed
|
|
12
|
+
// `Jinaga.User` is structurally identical to the SQL for the same join shape
|
|
13
|
+
// against any other given type. There is no `Jinaga.User` special-casing in
|
|
14
|
+
// jinaga-server's query generation, so the empty-result trigger does not live
|
|
15
|
+
// here.
|
|
16
|
+
|
|
17
|
+
function parseSpecification(input: string): Specification {
|
|
18
|
+
const parser = new SpecificationParser(input);
|
|
19
|
+
parser.skipWhitespace();
|
|
20
|
+
return parser.parseSpecification();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function buildMaps(specification: Specification): { factTypes: FactTypeMap; roleMap: RoleMap } {
|
|
24
|
+
// Assign deterministic type ids in first-seen order so two specifications
|
|
25
|
+
// with the same shape but different type names produce identical parameter
|
|
26
|
+
// sequences. Filter the synthetic "Unknown" fact type and "unknown" role
|
|
27
|
+
// name (matching the other Postgres SQL generator specs) so they cannot
|
|
28
|
+
// shift the id assignment. Roles get ids offset well past the type ids.
|
|
29
|
+
const factTypes = getAllFactTypes(specification)
|
|
30
|
+
.filter(factType => factType !== 'Unknown')
|
|
31
|
+
.reduce((f, factType, i) => addFactType(f, factType, i + 1), emptyFactTypeMap());
|
|
32
|
+
const roleMap = getAllRoles(specification)
|
|
33
|
+
.filter(role => role.name !== 'unknown')
|
|
34
|
+
.reduce((r, role, i) => {
|
|
35
|
+
const factTypeId = getFactTypeId(factTypes, role.successorType);
|
|
36
|
+
if (!factTypeId) {
|
|
37
|
+
return r;
|
|
38
|
+
}
|
|
39
|
+
return addRole(r, factTypeId, role.name, i + 100);
|
|
40
|
+
}, emptyRoleMap());
|
|
41
|
+
return { factTypes, roleMap };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Pure: generates the feed SQL for a single-feed specification. All assertions
|
|
45
|
+
// live in the tests (or beforeAll) so a parse/build failure surfaces as a test
|
|
46
|
+
// failure rather than a suite-collection error.
|
|
47
|
+
function feedSqlFor(descriptiveString: string, startType: string) {
|
|
48
|
+
const specification = parseSpecification(descriptiveString);
|
|
49
|
+
const { factTypes, roleMap } = buildMaps(specification);
|
|
50
|
+
const feeds = buildFeeds(specification);
|
|
51
|
+
const start = [dehydrateReference({ type: startType, key: "value" })];
|
|
52
|
+
const query = sqlFromFeed(feeds[0], start, "public", "", 100, factTypes, roleMap);
|
|
53
|
+
return { query, feedCount: feeds.length, factTypes };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The canonical "owned by me" shape: a successor edged to the given user
|
|
57
|
+
// through a role named `owner`.
|
|
58
|
+
const USER_GIVEN_SPEC = `
|
|
59
|
+
(user: Jinaga.User) {
|
|
60
|
+
activity: Networking.Activity [
|
|
61
|
+
activity->owner: Jinaga.User = user
|
|
62
|
+
]
|
|
63
|
+
}`;
|
|
64
|
+
|
|
65
|
+
// The identical join shape rooted at a non-user given.
|
|
66
|
+
const ACTIVITY_GIVEN_SPEC = `
|
|
67
|
+
(activity: Networking.Activity) {
|
|
68
|
+
log: Networking.ActivityCrmLog [
|
|
69
|
+
log->activity: Networking.Activity = activity
|
|
70
|
+
]
|
|
71
|
+
}`;
|
|
72
|
+
|
|
73
|
+
describe("Jinaga.User-given feed SQL (issue #179)", () => {
|
|
74
|
+
let userGiven: ReturnType<typeof feedSqlFor>;
|
|
75
|
+
let activityGiven: ReturnType<typeof feedSqlFor>;
|
|
76
|
+
|
|
77
|
+
beforeAll(() => {
|
|
78
|
+
userGiven = feedSqlFor(USER_GIVEN_SPEC, "Jinaga.User");
|
|
79
|
+
activityGiven = feedSqlFor(ACTIVITY_GIVEN_SPEC, "Networking.Activity");
|
|
80
|
+
// Both shapes must reduce to a single feed for the comparison to be
|
|
81
|
+
// apples-to-apples.
|
|
82
|
+
expect(userGiven.feedCount).toBe(1);
|
|
83
|
+
expect(activityGiven.feedCount).toBe(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("produces a satisfiable, single feed query for a Jinaga.User given", () => {
|
|
87
|
+
// A satisfiable query is the whole point: a null query here would be
|
|
88
|
+
// the silent-empty the issue describes. The given's type is loaded and
|
|
89
|
+
// the predecessor edge on `owner` is generated.
|
|
90
|
+
expect(userGiven.query).not.toBeNull();
|
|
91
|
+
expect(userGiven.query!.sql).toContain("f1.fact_type_id = $1 AND f1.hash = $2");
|
|
92
|
+
expect(userGiven.query!.sql).toContain("public.edge");
|
|
93
|
+
expect(userGiven.query!.labels.length).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("generates SQL structurally identical to a non-user given of the same shape", () => {
|
|
97
|
+
// No Jinaga.User special-casing: the two SQL strings match exactly.
|
|
98
|
+
expect(userGiven.query).not.toBeNull();
|
|
99
|
+
expect(activityGiven.query).not.toBeNull();
|
|
100
|
+
expect(userGiven.query!.sql).toEqual(activityGiven.query!.sql);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("binds the given type id (from the fact-type map) and hash as the leading parameters", () => {
|
|
104
|
+
// The given fact type participates in the WHERE clause like any other —
|
|
105
|
+
// it is not dropped or defaulted to a constant for Jinaga.User. Derive
|
|
106
|
+
// the expected id from the map built for this spec so the assertion is
|
|
107
|
+
// not coupled to getAllFactTypes ordering.
|
|
108
|
+
const userReference = dehydrateReference({ type: "Jinaga.User", key: "value" });
|
|
109
|
+
const expectedTypeId = getFactTypeId(userGiven.factTypes, "Jinaga.User");
|
|
110
|
+
expect(expectedTypeId).toBeDefined();
|
|
111
|
+
expect(userGiven.query).not.toBeNull();
|
|
112
|
+
expect(userGiven.query!.parameters[0]).toBe(expectedTypeId);
|
|
113
|
+
expect(userGiven.query!.parameters[1]).toBe(userReference.hash);
|
|
114
|
+
});
|
|
115
|
+
});
|