-
-
Notifications
You must be signed in to change notification settings - Fork 31
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
fi: support uuid -> username mapping #290
Changes from all commits
31343cb
db09e97
09c1786
865bb1e
fbfd402
25ed1d2
cc4fad6
e7df087
85dc609
a7e3966
7ebf2dd
e5da2b0
c07b001
2917f38
aa92761
26fdf95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,35 @@ | ||
import { rule } from 'graphql-shield' | ||
import { rule, inputRule } from 'graphql-shield' | ||
import muuid from 'uuid-mongodb' | ||
|
||
export const isEditor = rule()(async (parent, args, ctx, info) => { | ||
return (ctx.user.uuid != null) && ctx.user.roles.includes('editor') | ||
return _hasUserUuid(ctx) && ctx.user.roles.includes('editor') | ||
}) | ||
|
||
export const isUserAdmin = rule()(async (parent, args, ctx, info) => { | ||
return (ctx.user.uuid != null) && ctx.user.roles.includes('user_admin') | ||
return _hasUserUuid(ctx) && ctx.user.roles.includes('user_admin') | ||
}) | ||
|
||
export const isOwner = rule()(async (parent, args, ctx, info) => { | ||
return _hasUserUuid(ctx) && ctx.user.uuid === muuid.from(args.userUuid) | ||
}) | ||
|
||
export const isBuilderServiceAccount = rule()(async (parent, args, ctx: Context, info) => { | ||
return _hasUserUuid(ctx) && ctx.user.isBuilder | ||
}) | ||
|
||
export const isValidEmail = inputRule()( | ||
(yup) => | ||
yup.object({ | ||
email: yup.string().email('Please provide a valid email') | ||
}), | ||
{ abortEarly: false } | ||
) | ||
|
||
interface Context { | ||
user: { | ||
uuid?: string | ||
isBuilder: boolean | ||
} | ||
} | ||
|
||
const _hasUserUuid = (ctx: Context): boolean => ctx.user.uuid != null |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
import mongoose from 'mongoose' | ||
import muuid from 'uuid-mongodb' | ||
|
||
import { ExperimentalUserType } from './UserTypes.js' | ||
import { ExperimentalUserType, User, UsernameInfo } from './UserTypes.js' | ||
|
||
const { Schema } = mongoose | ||
|
||
|
@@ -18,6 +18,53 @@ export const ExperimentalUserSchema = new Schema<ExperimentalUserType>({ | |
timestamps: true | ||
}) | ||
|
||
/** | ||
* Temporary model used to capture user profile during bulk import of Canada data. | ||
* Use the standard User model instead. | ||
*/ | ||
export const getExperimentalUserModel = (): mongoose.Model<ExperimentalUserType> => { | ||
vnugent marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return mongoose.model('exp_users', ExperimentalUserSchema) | ||
} | ||
|
||
const UsernameSchema = new Schema<UsernameInfo>({ | ||
username: { type: Schema.Types.String } | ||
}, { | ||
_id: false, | ||
timestamps: { | ||
updatedAt: true, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slightly confused too, since below we type this as a "NotUpdatableField", so how come we allow an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I understand now why we allow There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
createdAt: false | ||
} | ||
}) | ||
|
||
export const UserSchema = new Schema<User>({ | ||
_id: { | ||
type: 'object', | ||
value: { type: 'Buffer' } | ||
}, | ||
email: { type: Schema.Types.String }, | ||
emailVerified: { type: Schema.Types.Boolean }, | ||
displayName: { type: Schema.Types.String }, | ||
vnugent marked this conversation as resolved.
Show resolved
Hide resolved
|
||
bio: { type: Schema.Types.String }, | ||
website: { type: Schema.Types.String }, | ||
usernameInfo: { type: UsernameSchema, required: false }, | ||
createdBy: { | ||
type: 'object', | ||
value: { type: 'Buffer' } | ||
}, | ||
updatedBy: { | ||
type: 'object', | ||
value: { type: 'Buffer' } | ||
} | ||
}, { | ||
_id: false, | ||
timestamps: true | ||
}) | ||
|
||
/** | ||
* For sorting by most recent | ||
*/ | ||
UserSchema.index({ createdAt: -1 }) | ||
|
||
export const getUserModel = (): mongoose.Model<User> => { | ||
return mongoose.model('users', UserSchema) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { glob } from 'glob' | ||
import { validate as uuidValidate } from 'uuid' | ||
import muuid from 'uuid-mongodb' | ||
import fs from 'fs' | ||
import { connectDB, gracefulExit, getUserModel } from '../../../index.js' | ||
import { logger } from '../../../../logger.js' | ||
import { User } from '../../../UserTypes.js' | ||
|
||
const LOCAL_MEDIA_DIR_UID = process.env.LOCAL_MEDIA_DIR_UID | ||
|
||
if (LOCAL_MEDIA_DIR_UID == null) { | ||
throw new Error('LOCAL_MEDIA_DIR_UID env not defined') | ||
} | ||
|
||
/** | ||
* Create a new Users collection from uid.json files in local media dir. | ||
*/ | ||
const onConnected = async (): Promise<void> => { | ||
logger.info('Creating users collection') | ||
const model = getUserModel() | ||
await model.ensureIndexes() | ||
const uidFIle = await glob(LOCAL_MEDIA_DIR_UID, { | ||
nodir: true, | ||
stat: false, | ||
withFileTypes: true | ||
}) | ||
|
||
let list: Array<Omit<User, 'createdAt' | 'updatedAt'>> = [] | ||
let count = 0 | ||
for (const file of uidFIle) { | ||
const folderUuidStr = file.parent?.name ?? '' | ||
if (!uuidValidate(folderUuidStr)) { | ||
logger.error({ file: file.name, parent: folderUuidStr }, 'Error: expect folder name to have uuid format. Found ') | ||
continue | ||
} | ||
const userUuid = muuid.from(folderUuidStr) | ||
const f = fs.readFileSync(file.fullpath(), 'utf-8') | ||
|
||
const { uid, ts } = JSON.parse(f) | ||
const newUser: Omit<User, 'createdAt' | 'updatedAt'> = { | ||
_id: userUuid, | ||
usernameInfo: { | ||
username: uid as string, | ||
updatedAt: new Date(ts) | ||
}, | ||
createdBy: userUuid | ||
} | ||
list.push(newUser) | ||
|
||
if (list.length === 40) { | ||
const rs = await model.insertMany(list) | ||
count = count + rs.length | ||
list = [] | ||
} | ||
} | ||
|
||
if (list.length > 0) { | ||
await model.insertMany(list) | ||
count = count + list.length | ||
} | ||
|
||
logger.info({ count }, 'Finish') | ||
|
||
await gracefulExit() | ||
} | ||
|
||
void connectDB(onConnected) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for fixing this!