Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add endpoints for graphql api #108

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23,210 changes: 23,210 additions & 0 deletions backend/package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,19 @@
"dependencies": {
"@roarr/cli": "^3.2.2",
"apollo-server-fastify": "3.6.3",
"dotenv": "^16.0.3",
"fast-safe-stringify": "^2.1.1",
"fastify": "^3.14.0",
"fastify-cookie": "^5.4.0",
"got": "^11.8.5",
"graphql": "^15.4.0",
"graphql-import": "^1.0.2",
"graphql-tools": "^7.0.2",
"pg-native": "^3.0.1",
"roarr": "^3.2.0",
"slonik": "^23.5.1",
"slonik-interceptor-field-name-transformation": "^1.5.3",
"slonik-interceptor-preset": "^1.2.10",
"ts-node": "^9.1.1"
"ts-node": "^10.9.1"
}
}
50 changes: 50 additions & 0 deletions backend/populate_db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { createPool, sql } from 'slonik';
import 'dotenv/config';

if (!process.env.POSTGRES_CONNECTION_STRING) {
throw new Error(
'Must provide a PG connection string (export POSTGRES_CONNECTION_STRING=value)',
);
}

const pool = createPool(process.env.POSTGRES_CONNECTION_STRING);
async function populateDatabase() {
// Insert sample users
const users = await pool.many(
sql`
INSERT INTO user_account (given_name, family_name, email_address)
VALUES ('John', 'Doe', 'john.doe@example.com'),
('Jane', 'Doe', 'jane.doe@example.com')
RETURNING id;
`,
);

// Insert sample feature flags
const featureFlags = await pool.many(
sql`
INSERT INTO feature_flags (id, name, values)
VALUES (${users[0].id}, 'new_feature_A', ARRAY['A', 'B', 'C']),
(${users[1].id}, 'new_feature_B', ARRAY['X', 'Y', 'Z'])
RETURNING id;
`,
);

// Insert sample feature flag assignments
await pool.query(
sql`
INSERT INTO feature_flag_assignments (user_id, feature_flag_id, value)
VALUES (${users[0].id}, ${featureFlags[0].id}, 'A'),
(${users[0].id}, ${featureFlags[1].id}, 'X'),
(${users[1].id}, ${featureFlags[0].id}, 'C');
`,
);

console.log('Database populated successfully');
}

populateDatabase()
.then(() => process.exit(0))
.catch((error) => {
console.error('Error populating the database:', error);
process.exit(1);
});
2 changes: 2 additions & 0 deletions backend/src/bin/migrate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SlonikMigrator } from '@slonik/migrator';
import { createPool } from 'slonik';
require('dotenv').config()


if (!process.env.POSTGRES_CONNECTION_STRING) {
throw new Error(
Expand Down
10 changes: 5 additions & 5 deletions backend/src/bin/migrations/2022.06.29T05.05.57.user_account.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
CREATE TABLE user_account (
id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
given_name text NOT NULL,
family_name text NOT NULL,
email_address text NOT NULL,
created_at timestamp with time zone DEFAULT NOW(),
updated_at timestamp with time zone DEFAULT NOW()
givenNme text NOT NULL,
familyName text NOT NULL,
emailAddress text NOT NULL,
createdAt timestamp with time zone DEFAULT NOW(),
updatedAt timestamp with time zone DEFAULT NOW()
);

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE feature_flags (
id serial PRIMARY KEY,
name text NOT NULL UNIQUE,
values text[] NOT NULL
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE feature_flag_assignments (
user_id integer NOT NULL,
feature_flag_id integer NOT NULL,
value text NOT NULL,
PRIMARY KEY (user_id, feature_flag_id),
FOREIGN KEY (user_id) REFERENCES user_account (id) ON DELETE CASCADE,
FOREIGN KEY (feature_flag_id) REFERENCES feature_flags (id) ON DELETE CASCADE
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
raise 'down migration not implemented'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
raise 'down migration not implemented'
3 changes: 1 addition & 2 deletions backend/src/bin/server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import Logger from 'roarr';
import { createPool } from 'slonik';
// @ts-expect-error
import { createInterceptors } from 'slonik-interceptor-preset';
import { createFastifyServer } from '../factories/createFastifyServer';

require('dotenv').config()
const log = Logger.child({ context: 'bin/server' });

if (!process.env.POSTGRES_CONNECTION_STRING)
Expand Down
31 changes: 28 additions & 3 deletions backend/src/schema/resolvers/Mutations/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
import { resolve as sampleMutation } from './sampleMutation';
import { MutationResolvers } from '../../../generated/types';
import { sql } from 'slonik';

export const Mutation = {
sampleMutation,
const Mutation: MutationResolvers = {
addTargetedUsers: async (_parent, { userId, featureFlagId, value }, { pool }) => {
const newAssignment = await pool.any(
sql`
INSERT INTO feature_flag_assignments (user_id, feature_flag_id, value)
VALUES (${userId}, ${featureFlagId}, ${value})
RETURNING *;
`, [userId, featureFlagId, value]);

return newAssignment[0];
},

updateFeatureFlagValue: async (_parent, { userId, featureFlagId, value }, { pool }) => {
const updatedAssignment = await pool.any(sql`
UPDATE feature_flag_assignments
SET value = ${value}
WHERE user_id = ${userId} AND feature_flag_id = ${featureFlagId}
RETURNING *
`, [userId, featureFlagId, value]);

console.log(updatedAssignment)

return updatedAssignment[0]
},
};

export default Mutation;
46 changes: 42 additions & 4 deletions backend/src/schema/resolvers/Queries/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
import { resolve as hello } from './hello';
import { QueryResolvers } from '../../../generated/types';
import { sql } from 'slonik';

export const Query = {
hello,
};
const Query: QueryResolvers = {
allUsersWithFeatureFlags: async ( _parent,
_args,
context,
) => {
const { pool } = context;
// Your SQL query using the `sql` tagged template literal
const users = await pool.any(
sql`
SELECT
u.id AS id,
u.given_name,
u.family_name,
u.email_address,
a.user_id AS userId,
a.feature_flag_id AS featureFlagId,
a.value AS featureFlagAssignmentValue
FROM
user_account u
LEFT JOIN
feature_flag_assignments a ON u.id = a.user_id
ORDER BY
u.id;
`,
);


for(let user of users) {
user.featureflag = {
userId: user.id,
featureFlagId: user.featureflagid,
value: user.featureflagassignmentvalue
}
}

return users || [];
}
}

export default Query
5 changes: 3 additions & 2 deletions backend/src/schema/resolvers/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { mergeResolvers } from 'graphql-tools';
import { Mutation } from './Mutations';
import { Query } from './Queries';
import Mutation from './Mutations';
import Query from './Queries';


export const resolvers = mergeResolvers([
{
Expand Down
27 changes: 24 additions & 3 deletions backend/src/schema/schema.graphql
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
type User {
id: ID!
givenName: String!
familyName: String!
emailAddress: String!
featureflag: FeatureFlagAssignment!
}

type FeatureFlag {
id: ID!
name: String!
values: [String!]!
}

type FeatureFlagAssignment {
userId: ID!
featureFlagId: ID!
value: String!
}

type Mutation {
sampleMutation: String!
addTargetedUsers(userId: ID!, featureFlagId: ID!, value: String!): FeatureFlagAssignment!
updateFeatureFlagValue(userId: ID!, featureFlagId: ID!, value: String!): FeatureFlagAssignment!
}

type Query {
hello: String!
}
allUsersWithFeatureFlags: [User!]!
}
3 changes: 1 addition & 2 deletions backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"outDir": "./build",
"rootDir": "./",
"strict": true,
"noImplicitAny": true,
"noImplicitAny": false,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
Expand All @@ -25,7 +25,6 @@
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"useUnknownInCatchVariables": false
},
"include": ["src", "test"]
}