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(cli): allow overriding imagery names #2169

Merged
merged 10 commits into from
May 16, 2022
2 changes: 2 additions & 0 deletions packages/cli/src/cli/cogify/action.job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
CommandLineIntegerParameter,
CommandLineStringParameter,
} from '@rushstack/ts-command-line';
import { basename } from 'path';
import { JobCreationContext } from '../../cog/cog.stac.job.js';
import { CogJobFactory, MaxConcurrencyDefault } from '../../cog/job.factory.js';
import { GdalCogBuilderDefaults, GdalCogBuilderResampling, GdalResamplingOptions } from '../../gdal/gdal.config.js';
Expand Down Expand Up @@ -120,6 +121,7 @@ export class ActionJobCreate extends CommandLineAction {
}

const ctx: JobCreationContext = {
imageryName: basename(sourceLocation.path),
sourceLocation,
outputLocation,
cutline,
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/cog/cog.stac.job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
export const MaxConcurrencyDefault = 50;

export interface JobCreationContext {
imageryName: string;

/** Source config */
sourceLocation: FileConfig | FileConfigPath;

Expand Down
6 changes: 2 additions & 4 deletions packages/cli/src/cog/job.factory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Bounds } from '@basemaps/geo';
import { fsa, isConfigS3Role, isFileConfigPath, LogConfig } from '@basemaps/shared';
import { basename } from 'path';
import * as ulid from 'ulid';
import { CogBuilder } from '../index.js';
import { BatchJob } from '../cli/cogify/batch.job.js';
Expand All @@ -22,8 +21,7 @@ export const CogJobFactory = {
*/
async create(ctx: JobCreationContext): Promise<CogJob> {
const id = ctx.override?.id ?? ulid.ulid();
const imageryName = basename(ctx.sourceLocation.path).replace(/\./g, '-'); // batch does not allow '.' in names
const logger = LogConfig.get().child({ id, imageryName });
const logger = LogConfig.get().child({ id, imageryName: ctx.imageryName });

const gdalVersion = await Gdal.version(logger);
logger.info({ version: gdalVersion }, 'GdalVersion');
Expand Down Expand Up @@ -101,7 +99,7 @@ export const CogJobFactory = {

const job = await CogStacJob.create({
id,
imageryName,
imageryName: ctx.imageryName,
metadata,
ctx,
addAlpha,
Expand Down
5 changes: 3 additions & 2 deletions packages/lambda-tiler/src/__test__/tile.import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ o.spec('Import', () => {

const tileMatrix = Nztm2000Tms;
const bucket = 'testSourceBucket';
const path = `s3://${bucket}/imagery/`;
const path = `s3://${bucket}/imagery_2022/`;
const role: RoleConfig = {
bucket,
accountId: '123456',
Expand All @@ -43,6 +43,7 @@ o.spec('Import', () => {
}

const ctx: JobCreationContext = {
imageryName: 'imagery_2022',
override: {
projection: tileMatrix.projection,
resampling: {
Expand Down Expand Up @@ -96,7 +97,7 @@ o.spec('Import', () => {
o('should return Unable to access bucket', async () => {
// Given... different bucket have no access role
sandbox.stub(fsa, 'readJson').resolves({ buckets: [role] });
const req = getRequest(`s3://wrong-bucket/imagery/`, '2193');
const req = getRequest(`s3://wrong-bucket/imagery_2022/`, '2193');

// When ...Then ...
const res = await Import(req);
Expand Down
2 changes: 2 additions & 0 deletions packages/lambda-tiler/src/import/make.cog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { RoleConfig } from './imagery.find.js';
export async function getJobCreationContext(
path: string,
tileMatrix: TileMatrixSet,
name: string,
role: RoleConfig,
files: string[],
): Promise<JobCreationContext> {
const bucket = Env.get(Env.ImportImageryBucket);
if (bucket == null) throw new Error('Output AWS s3 bucket Not Found.');
const ctx: JobCreationContext = {
imageryName: name,
override: {
projection: tileMatrix.projection,
resampling: {
Expand Down
17 changes: 14 additions & 3 deletions packages/lambda-tiler/src/routes/import.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
import { Config, fsa } from '@basemaps/shared';
import { Config, extractYearRangeFromName, fsa } from '@basemaps/shared';
import { createHash } from 'crypto';
import { findImagery, RoleRegister } from '../import/imagery.find.js';
import { Nztm2000Tms, TileMatrixSets } from '@basemaps/geo';
import { getJobCreationContext } from '../import/make.cog.js';
import { ConfigProcessingJob, JobStatus } from '@basemaps/config';
import { CogJobFactory } from '@basemaps/cli';
import * as ulid from 'ulid';
import { basename } from 'path';

/**
* Trigger import imagery job by this endpoint
Expand All @@ -17,6 +18,7 @@ import * as ulid from 'ulid';
export async function Import(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
const path = req.query.get('path');
const projection = req.query.get('p');
const name = req.query.get('name');

// Parse projection as target, default to process both NZTM2000Quad
let targetTms = Nztm2000Tms;
Expand All @@ -26,15 +28,24 @@ export async function Import(req: LambdaHttpRequest): Promise<LambdaHttpResponse
targetTms = tileMatrix;
}

// Find the imagery from s3
// Validate the s3 path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blacha I added some more here, to validate the imagery name before pass it to the CogJobContext.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is going to give very weird imagery names, maybe we should just use :ulid_:year to show its a completly made up name?

Some of these import paths will be completely meaningless, imagine a folder s3://a/2022/04/eca/RGB/ what sort of "name" would we get from this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, we cannot just add uild_year into CogJobContext. This will make a different hash each time from CogJobContext.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to make this imageryName as optional attributions for CogJobContext. And add this after creating hash.

if (path == null || !path.startsWith('s3://')) return new LambdaHttpResponse(400, `Invalid s3 path: ${path}`);

// Check if the imagery name contains a year
const imageryName = name != null ? name : basename(path);
const years = extractYearRangeFromName(imageryName);
if (years[0] === -1) {
return new LambdaHttpResponse(400, `Invalid Imagery Name: ${imageryName}`);
}

// Find the imagery from s3
const role = await RoleRegister.findRole(path);
if (role == null) return new LambdaHttpResponse(403, 'Unable to Access the s3 bucket');
const files = await findImagery(path);
if (files.length === 0) return new LambdaHttpResponse(404, 'Imagery Not Found');

// Prepare Cog jobs
const ctx = await getJobCreationContext(path, targetTms, role, files);
const ctx = await getJobCreationContext(path, targetTms, imageryName, role, files);

const hash = createHash('sha256').update(JSON.stringify(ctx)).digest('base64');
const jobId = Config.ProcessingJob.id(hash);
Expand Down